On Wed, Jul 17, 2024 at 10:55 AM Patrick Palka <ppa...@redhat.com> wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look > OK for trunk?
Just an FYI. This broke xalancbmk_r in SPEC 2017. clang has a flag to delay the checking until instantiation time to work around this buggy code, -fdelayed-template-parsing . Does it make sense to add a similar one for GCC? See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116064 and https://github.com/llvm/llvm-project/issues/96859 for reference. Thanks, Andrew Pinski > > -- >8 -- > > When the scope of a qualified name is the current instantiation, and > qualified lookup finds nothing at template definition time, then we > know it'll find nothing at instantiation time (unless the current > instantiation has dependent bases). So such qualified name lookup > failure can be diagnosed ahead of time as per [temp.res.general]/6. > > This patch implements that, for qualified names of the form: > > this->non_existent > a.non_existent > A::non_existent > typename A::non_existent > > It turns out we already optimistically attempt qualified lookup of > basically every qualified name, even when it's dependently scoped, and > just suppress issuing a lookup failure diagnostic after the fact when > the scope is a dependent type. So implementing this is mostly a > matter of restricting the diagnostic suppression to "dependentish" > scopes, rather than all dependently typed scopes. > > The cp_parser_conversion_function_id change is needed to avoid regressing > lookup/using8.C: > > using A<T>::operator typename A<T>::Nested*; > > When resolving A<T>::Nested we consider it not dependently scoped since > we entered A<T> from cp_parser_conversion_function_id earlier. But this > A<T> is the implicit instantiation A<T> not the primary template type A<T>, > and so the lookup of Nested fails which we now diagnose. This patch works > around this by not entering the template scope of a qualified conversion > function-id in this case, i.e. if we're in an expression vs declaration > context, by seeing if the type already went through finish_template_type > with entering_scope=true. > > gcc/cp/ChangeLog: > > * decl.cc (make_typename_type): Restrict name lookup failure > punting to dependentish_scope_p instead of dependent_type_p. > * error.cc (qualified_name_lookup_error): Improve diagnostic > when the scope is the current instantiation. > * parser.cc (cp_parser_diagnose_invalid_type_name): Likewise. > (cp_parser_conversion_function_id): Don't call push_scope on > a template scope unless we're in a declaration context. > (cp_parser_lookup_name): Restrict name lookup failure > punting to dependentish_scope_p instead of depedent_type_p. > * semantics.cc (finish_id_expression_1): Likewise. > * typeck.cc (finish_class_member_access_expr): Likewise. > > libstdc++-v3/ChangeLog: > > * include/experimental/socket > (basic_socket_iostream::basic_socket_iostream): Fix typo. > * include/tr2/dynamic_bitset > (__dynamic_bitset_base::_M_is_proper_subset_of): Likewise. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/alignas18.C: Expect name lookup error for U::X. > * g++.dg/cpp0x/forw_enum13.C: Expect name lookup error for > D3::A and D4<T>::A. > * g++.dg/parse/access13.C: Declare A::E::V to avoid name lookup > failure and preserve intent of the test. > * g++.dg/parse/enum11.C: Expect extra errors, matching the > non-template case. > * g++.dg/template/crash123.C: Avoid name lookup failure to > preserve intent of the test. > * g++.dg/template/crash124.C: Likewise. > * g++.dg/template/crash7.C: Adjust expected diagnostics. > * g++.dg/template/dtor6.C: Declare A::~A() to avoid name lookup > failure and preserve intent of the test. > * g++.dg/template/error22.C: Adjust expected diagnostics. > * g++.dg/template/static30.C: Avoid name lookup failure to > preserve intent of the test. > * g++.old-deja/g++.other/decl5.C: Adjust expected diagnostics. > * g++.dg/template/non-dependent34.C: New test. > --- > gcc/cp/decl.cc | 2 +- > gcc/cp/error.cc | 3 +- > gcc/cp/parser.cc | 10 +++-- > gcc/cp/semantics.cc | 2 +- > gcc/cp/typeck.cc | 2 +- > gcc/testsuite/g++.dg/cpp0x/alignas18.C | 3 +- > gcc/testsuite/g++.dg/cpp0x/forw_enum13.C | 6 +-- > gcc/testsuite/g++.dg/parse/access13.C | 1 + > gcc/testsuite/g++.dg/parse/enum11.C | 2 +- > gcc/testsuite/g++.dg/template/crash123.C | 2 +- > gcc/testsuite/g++.dg/template/crash124.C | 4 +- > gcc/testsuite/g++.dg/template/crash7.C | 6 +-- > gcc/testsuite/g++.dg/template/dtor6.C | 3 +- > gcc/testsuite/g++.dg/template/error22.C | 2 +- > .../g++.dg/template/non-dependent34.C | 44 +++++++++++++++++++ > gcc/testsuite/g++.dg/template/static30.C | 4 +- > gcc/testsuite/g++.old-deja/g++.other/decl5.C | 2 +- > libstdc++-v3/include/experimental/socket | 2 +- > libstdc++-v3/include/tr2/dynamic_bitset | 2 +- > 19 files changed, 75 insertions(+), 27 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent34.C > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index e7bb4fa3089..3c5ad554ff2 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -4536,7 +4536,7 @@ make_typename_type (tree context, tree name, enum > tag_types tag_type, > else > t = NULL_TREE; > > - if ((!t || TREE_CODE (t) == TREE_LIST) && dependent_type_p (context)) > + if ((!t || TREE_CODE (t) == TREE_LIST) && dependentish_scope_p (context)) > return build_typename_type (context, name, fullname, tag_type); > > want_template = TREE_CODE (fullname) == TEMPLATE_ID_EXPR; > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc > index e35448f5434..6d99cb27703 100644 > --- a/gcc/cp/error.cc > +++ b/gcc/cp/error.cc > @@ -4714,7 +4714,8 @@ qualified_name_lookup_error (tree scope, tree name, > ; /* We already complained. */ > else if (TYPE_P (scope)) > { > - if (!COMPLETE_TYPE_P (scope)) > + if (!COMPLETE_TYPE_P (scope) > + && !currently_open_class (scope)) > error_at (location, "incomplete type %qT used in nested name > specifier", > scope); > else if (TREE_CODE (decl) == TREE_LIST) > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 1dd0efaf963..efd5d6f29a7 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -3888,7 +3888,8 @@ cp_parser_diagnose_invalid_type_name (cp_parser > *parser, tree id, > else if (TYPE_P (parser->scope)) > { > auto_diagnostic_group d; > - if (!COMPLETE_TYPE_P (parser->scope)) > + if (!COMPLETE_TYPE_P (parser->scope) > + && !currently_open_class (parser->scope)) > cxx_incomplete_type_error (location_of (id), NULL_TREE, > parser->scope); > else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) > @@ -17514,7 +17515,10 @@ cp_parser_conversion_function_id (cp_parser* parser) > > In order to see that `I' is a type-name in the definition, we > must be in the scope of `S'. */ > - if (saved_scope) > + if (saved_scope > + /* In A<T>::operator I(), we don't want to enter A<T> if we're > + in an expression rather than declaration context. */ > + && adjust_type_for_entering_scope (saved_scope) == saved_scope) > pushed_scope = push_scope (saved_scope); > /* Parse the conversion-type-id. */ > type = cp_parser_conversion_type_id (parser); > @@ -32219,7 +32223,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name, > /* If the scope is a dependent type and either we deferred lookup or > we did lookup but didn't find the name, rememeber the name. */ > if (decl == error_mark_node && TYPE_P (parser->scope) > - && dependent_type_p (parser->scope)) > + && dependentish_scope_p (parser->scope)) > { > if (tag_type) > { > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index cd3df13772d..c21572e5d7f 100644 > --- a/gcc/cp/semantics.cc > +++ b/gcc/cp/semantics.cc > @@ -4353,7 +4353,7 @@ finish_id_expression_1 (tree id_expression, > /* Name lookup failed. */ > if (scope > && (!TYPE_P (scope) > - || (!dependent_type_p (scope) > + || (!dependentish_scope_p (scope) > && !(identifier_p (id_expression) > && IDENTIFIER_CONV_OP_P (id_expression) > && dependent_type_p (TREE_TYPE (id_expression)))))) > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > index 5041a70d089..e5361a5e693 100644 > --- a/gcc/cp/typeck.cc > +++ b/gcc/cp/typeck.cc > @@ -3542,7 +3542,7 @@ finish_class_member_access_expr (cp_expr object, tree > name, bool template_p, > afi.maybe_suggest_accessor (TYPE_READONLY (object_type)); > if (member == NULL_TREE) > { > - if (dependent_type_p (object_type)) > + if (dependentish_scope_p (object_type)) > /* Try again at instantiation time. */ > goto dependent; > if (complain & tf_error) > diff --git a/gcc/testsuite/g++.dg/cpp0x/alignas18.C > b/gcc/testsuite/g++.dg/cpp0x/alignas18.C > index 820bdd2d7ca..9c25cd0942b 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/alignas18.C > +++ b/gcc/testsuite/g++.dg/cpp0x/alignas18.C > @@ -3,6 +3,5 @@ > > template <typename T> struct S { > using U = S; > - // FIXME: This is ill-formed; see PR90847. > - void fn() alignas(U::X); > + void fn() alignas(U::X); // { dg-error "not a member" } > }; > diff --git a/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C > b/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C > index b8027f0c28d..37ffad9b956 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C > +++ b/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C > @@ -18,13 +18,13 @@ class D2 > template <typename T> > class D3 > { > - enum D3::A { foo } c; // { dg-error "extra qualification not allowed" } > + enum D3::A { foo } c; // { dg-error "does not name an enumeration" } > }; > > template <typename T> > class D4 > { > - enum D4<T>::A { foo } c; // { dg-error "extra qualification not allowed" } > + enum D4<T>::A { foo } c; // { dg-error "does not name an enumeration" } > }; > > template <typename T> > @@ -32,7 +32,7 @@ class D5 > { > class D6 > { > - enum D6::A { foo } c; // { dg-error "extra qualification not allowed" } > + enum D6::A { foo } c; // { dg-error "does not name an enumeration" } > }; > }; > > diff --git a/gcc/testsuite/g++.dg/parse/access13.C > b/gcc/testsuite/g++.dg/parse/access13.C > index 41463c5dde5..ea3cf1111a8 100644 > --- a/gcc/testsuite/g++.dg/parse/access13.C > +++ b/gcc/testsuite/g++.dg/parse/access13.C > @@ -2,6 +2,7 @@ > > template <typename> struct A > { > + struct E { static int V; }; > A::E::V; // { dg-warning "access decl" } > enum { V }; // { dg-error "conflicts with a previous decl" > } > }; > diff --git a/gcc/testsuite/g++.dg/parse/enum11.C > b/gcc/testsuite/g++.dg/parse/enum11.C > index 68ddedbeeec..8bab16a6799 100644 > --- a/gcc/testsuite/g++.dg/parse/enum11.C > +++ b/gcc/testsuite/g++.dg/parse/enum11.C > @@ -2,5 +2,5 @@ > > template<typename> struct A > { > - enum A::B::C {}; // { dg-error "has not been declared" } > + enum A::B::C {}; // { dg-error "" } > }; > diff --git a/gcc/testsuite/g++.dg/template/crash123.C > b/gcc/testsuite/g++.dg/template/crash123.C > index 20a49619c6f..20a71cf192b 100644 > --- a/gcc/testsuite/g++.dg/template/crash123.C > +++ b/gcc/testsuite/g++.dg/template/crash123.C > @@ -4,7 +4,7 @@ template <bool> struct VI {}; > template <typename T> > struct IP > { > - static const bool r = IP<T>::r; // { dg-error "depth" } > + static const bool r = IP<T*>::r; // { dg-error "depth" } > }; > template <typename T> struct V > { > diff --git a/gcc/testsuite/g++.dg/template/crash124.C > b/gcc/testsuite/g++.dg/template/crash124.C > index 4931aa8e9c6..5788ead4630 100644 > --- a/gcc/testsuite/g++.dg/template/crash124.C > +++ b/gcc/testsuite/g++.dg/template/crash124.C > @@ -4,12 +4,12 @@ template <bool> struct VI {}; > template <typename T> > struct IP > { > - static const bool r = IP<T>::r; // { dg-error "depth" } > + static const bool r = IP<T*>::r; // { dg-error "depth" } > }; > template <typename T> > struct V > { > - static const bool r = IP<T>::r; > + static const bool r = IP<T*>::r; > VI<r> vi; > }; > struct X; > diff --git a/gcc/testsuite/g++.dg/template/crash7.C > b/gcc/testsuite/g++.dg/template/crash7.C > index 691628e7878..977b4e454ba 100644 > --- a/gcc/testsuite/g++.dg/template/crash7.C > +++ b/gcc/testsuite/g++.dg/template/crash7.C > @@ -7,9 +7,7 @@ > > template <typename> struct A > { > - template <typename> A(typename A::X) {} // { dg-error "incomplete" } > + template <typename> A(typename A::X) {} // { dg-error "does not name a > type" } > }; > > -// We currently don't give the "no match" error because we don't add the > -// invalid constructor template to TYPE_METHODS. > -A<void> a; // { dg-message "required" } > +A<void> a; // { dg-error "no match" } > diff --git a/gcc/testsuite/g++.dg/template/dtor6.C > b/gcc/testsuite/g++.dg/template/dtor6.C > index a3d778a1ea1..5757028814e 100644 > --- a/gcc/testsuite/g++.dg/template/dtor6.C > +++ b/gcc/testsuite/g++.dg/template/dtor6.C > @@ -1,8 +1,9 @@ > // PR c++/40139 > > -template<int> struct A > +template<int N> struct A > { > static int i; > + ~A(); > }; > > template<int N> int A<N>::i = { A::~A }; // { dg-error "36:invalid use of > non-static member function" } > diff --git a/gcc/testsuite/g++.dg/template/error22.C > b/gcc/testsuite/g++.dg/template/error22.C > index a7e61721113..af87992219b 100644 > --- a/gcc/testsuite/g++.dg/template/error22.C > +++ b/gcc/testsuite/g++.dg/template/error22.C > @@ -4,6 +4,6 @@ struct A > { > template<void (A::*)()> struct B {}; > void ::foo(); // { dg-error "10:invalid use" } > - B<&A::foo> b; // { dg-error "incomplete type|template argument" } > + B<&A::foo> b; // { dg-error "'foo' is not a member of 'A'|template > argument" } > }; > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent34.C > b/gcc/testsuite/g++.dg/template/non-dependent34.C > new file mode 100644 > index 00000000000..ed4c146ea3d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent34.C > @@ -0,0 +1,44 @@ > +// Verify we diagnose failed qualified lookup into the current > +// instantiation ahead of time. > + > +namespace without_dependent_base { > +template<class T> > +struct A { > + void f(A& other) { > + A::x; // { dg-error "'x' is not a member" } > + this->x; // { dg-error "no member named 'x' } > + other.y; // { dg-error "no member named 'y' } > + typename A::type z; // { dg-error "does not name a type" } > + > + struct B { > + void g(A& other) { > + A::x; // { dg-error "'x' is not a member" } > + this->x; // { dg-error "no member named 'x' } > + other.y; // { dg-error "no member named 'y' } > + typename A::type z; // { dg-error "does not name a type" } > + } > + }; > + } > +}; > +} > + > +namespace with_dependent_base { > +template<class T> > +struct A : T { > + void f(A& other) { > + A::x; > + this->x; > + other.y; > + typename A::type z; > + > + struct B : T { > + void g(A& other) { > + A::x; > + this->x; > + other.y; > + typename A::type z; > + } > + }; > + } > +}; > +} > diff --git a/gcc/testsuite/g++.dg/template/static30.C > b/gcc/testsuite/g++.dg/template/static30.C > index 07dafe23ffa..248f9e9025e 100644 > --- a/gcc/testsuite/g++.dg/template/static30.C > +++ b/gcc/testsuite/g++.dg/template/static30.C > @@ -6,5 +6,5 @@ template <int> struct A > static const int i2; > }; > > -template <int N> const int A<N>::i1(A<N>::i); > -template <int N> const int A<N>::i2(3, A<N>::i); // { dg-error "expression > list" } > +template <int N> const int A<N>::i1(A<N>::i1); > +template <int N> const int A<N>::i2(3, A<N>::i2); // { dg-error "expression > list" } > diff --git a/gcc/testsuite/g++.old-deja/g++.other/decl5.C > b/gcc/testsuite/g++.old-deja/g++.other/decl5.C > index 26556aaa7ef..c24957f8bbe 100644 > --- a/gcc/testsuite/g++.old-deja/g++.other/decl5.C > +++ b/gcc/testsuite/g++.old-deja/g++.other/decl5.C > @@ -18,7 +18,7 @@ struct A { > struct Z; > expand me; // { dg-error "'expand' does not name a type" } > void foo(struct A::e); > - void foo(struct A::z); // { dg-error "incomplete" } > + void foo(struct A::z); // { dg-error "does not name a type" } > }; > > struct Q; > diff --git a/libstdc++-v3/include/experimental/socket > b/libstdc++-v3/include/experimental/socket > index 02c27d66c6a..3fe83a001e6 100644 > --- a/libstdc++-v3/include/experimental/socket > +++ b/libstdc++-v3/include/experimental/socket > @@ -2450,7 +2450,7 @@ inline namespace v1 > // XXX ??? ^^^^^^^ > { > // XXX ??? this->init(std::addressof(_M_sb)); > - this->set_rbduf(std::addressof(_M_sb)); > + this->set_rdbuf(std::addressof(_M_sb)); > } > > template<typename... _Args> > diff --git a/libstdc++-v3/include/tr2/dynamic_bitset > b/libstdc++-v3/include/tr2/dynamic_bitset > index 274c4f6a138..f0878d7429e 100644 > --- a/libstdc++-v3/include/tr2/dynamic_bitset > +++ b/libstdc++-v3/include/tr2/dynamic_bitset > @@ -304,7 +304,7 @@ namespace tr2 > bool > _M_is_proper_subset_of(const __dynamic_bitset_base& __b) const noexcept > { > - if (this->is_subset_of(__b)) > + if (this->_M_is_subset_of(__b)) > { > if (*this == __b) > return false; > -- > 2.46.0.rc0.75.g04f5a52757 >