]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: diagnose failed qualified lookup into current inst
authorPatrick Palka <ppalka@redhat.com>
Thu, 18 Jul 2024 00:54:14 +0000 (20:54 -0400)
committerPatrick Palka <ppalka@redhat.com>
Thu, 18 Jul 2024 00:54:14 +0000 (20:54 -0400)
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 (where
the current instantiation is A<T>):

  this->non_existent
  a.non_existent
  A::non_existent
  typename A::non_existent

It turns out we already optimistically attempt qualified lookup of
seemingly every qualified name, even when it's dependently scoped, and
then suppress issuing a lookup failure diagnostic after the fact.
So implementing this is mostly a matter of restricting the diagnostic
suppression to "dependentish" scopes (i.e. dependent scopes or the
current instantiation with dependent bases), rather than suppressing
for any dependently-typed scope as we currently do.

The cp_parser_conversion_function_id change is needed to avoid regressing
lookup/using8.C:

  using A<T>::operator typename A<T>::Nested*;

When looking up 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 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.

Reviewed-by: Jason Merrill <jason@redhat.com>
19 files changed:
gcc/cp/decl.cc
gcc/cp/error.cc
gcc/cp/parser.cc
gcc/cp/semantics.cc
gcc/cp/typeck.cc
gcc/testsuite/g++.dg/cpp0x/alignas18.C
gcc/testsuite/g++.dg/cpp0x/forw_enum13.C
gcc/testsuite/g++.dg/parse/access13.C
gcc/testsuite/g++.dg/parse/enum11.C
gcc/testsuite/g++.dg/template/crash123.C
gcc/testsuite/g++.dg/template/crash124.C
gcc/testsuite/g++.dg/template/crash7.C
gcc/testsuite/g++.dg/template/dtor6.C
gcc/testsuite/g++.dg/template/error22.C
gcc/testsuite/g++.dg/template/non-dependent34.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/static30.C
gcc/testsuite/g++.old-deja/g++.other/decl5.C
libstdc++-v3/include/experimental/socket
libstdc++-v3/include/tr2/dynamic_bitset

index 66e8a973cce56316248a00d96c24a87dd2e30fe1..6b686d75a49f9d368ec70a735b3ed5143faa3bfa 100644 (file)
@@ -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;
index e35448f54346fc6080505af69211514fe4f5f8d8..6d99cb277038e10b485acf54b02a8152d195f2dc 100644 (file)
@@ -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)
index 1dd0efaf96378f847ac00da1832462cb9c733bff..efd5d6f29a718a4bf1c2eecfbdfbbd2a4ce61c16 100644 (file)
@@ -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)
            {
index cd3df13772db1c64620c15070ce2268476c5cbfa..c21572e5d7f7e7fdf8a627bc2e39132bb3119322 100644 (file)
@@ -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))))))
index 15e5577f67035b54041c6ac5a2293441da0472d0..8df8b871676b45c864d338e851b79658a47e9834 100644 (file)
@@ -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)
index 820bdd2d7cab33693c6f0561aa9c53f264d0502b..9c25cd0942bec5a3db027adda673bb80a3556ef6 100644 (file)
@@ -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" }
 };
index b8027f0c28d48dd46aa4081196500c9c822b8273..37ffad9b95635d19e4eba788fc8e3dc166d6be6c 100644 (file)
@@ -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" }
   };
 };
 
index 41463c5dde5e2d2130e1fff798cfd8c7d055947d..ea3cf1111a83e2ef08caec701f87747fb0530b7b 100644 (file)
@@ -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" }
 };
index 68ddedbeeeca2a45a5de1e9d8903f333c8360fdf..8bab16a67999341affb7622390bd81fad4c5fd69 100644 (file)
@@ -2,5 +2,5 @@
 
 template<typename> struct A
 { 
-  enum A::B::C {};   // { dg-error "has not been declared" }
+  enum A::B::C {};   // { dg-error "" }
 };
index 20a49619c6fcf1d5fb9fc23e304c17960b2657c1..20a71cf192b1cccd1194a27581f21d90f6a6597d 100644 (file)
@@ -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
 {
index 4931aa8e9c6df55ce8c09e0e8fcb0be89fdca55e..5788ead46308956e624f7545cbd0acf78e25fc50 100644 (file)
@@ -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;
index 691628e787817eab00bc2c9191cd60a5a5d6e080..977b4e454ba7861b58a1f936ab1d3cdd2a53184c 100644 (file)
@@ -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" }
index a3d778a1ea1114a5b3cabb318cd3075b295dc124..46ebf9ff95f8d269b185bf0cd3e43e2159136c9d 100644 (file)
@@ -3,6 +3,7 @@
 template<int> 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" }
index a7e6172111351381c79b9838830db052f8e088e3..af87992219b2f880719fe50902af799d11312929 100644 (file)
@@ -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 (file)
index 0000000..4f369ff
--- /dev/null
@@ -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;
+      }
+    };
+  }
+};
+}
index 07dafe23ffaab29f8d84df3caebef01bcb6393f4..248f9e9025e45bdba58a0ba6ae481fee9fd096e8 100644 (file)
@@ -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" }
index 26556aaa7ef51dde2371c8f8633d13be3d8315e1..c24957f8bbe20d3d0d5e657076bbbeef22c18830 100644 (file)
@@ -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;
index 02c27d66c6a97e82a7e967adb22be327f7a2068b..3fe83a001e61fcef6460d26a990e0c5875ae6181 100644 (file)
@@ -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>
index 274c4f6a1386ec796d64114be8b71343e19dac20..f0878d7429e08352041bba6fe3f69c5b150a923c 100644 (file)
@@ -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;