diff mbox series

[pushed] c++: improve lookup of member-qualified names

Message ID 20210918024245.1586788-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: improve lookup of member-qualified names | expand

Commit Message

Jason Merrill Sept. 18, 2021, 2:42 a.m. UTC
I've been working on the resolution of CWG1835 by P1787, which among many
other things clarified that a name after -> or . is looked up first in the
class of the object expression even if it's dependent.  This patch does not
make that change; this is a smaller change extracted from that work in
progress to make the lookup in the object type work better in cases where
unqualified lookup doesn't find anything.

Basically, if we see "t.foo::" we know that looking up foo in t needs to
find a type, so we build an implicit TYPENAME_TYPE for it.

This also implements the change from P1787 to assume that a name followed by
 < in a type-only context names a template, since the less-than operator
can't appear in a type context.  This makes some of the lines in dtor11.C
work.

I introduce the predicate 'dependentish_scope_p' for the case where the
current instantiation has dependent bases, so even though we can perform
name lookup, we can't conclude that a lookup failure is conclusive.

gcc/cp/ChangeLog:

	* cp-tree.h (dependentish_scope_p): Declare.
	* pt.c (dependentish_scope_p): New.
	* parser.c (cp_parser_lookup_name): Return a TYPENAME_TYPE
	for lookup of a type in a dependent object.
	(cp_parser_template_id): Handle TYPENAME_TYPE.
	(cp_parser_template_name): If we're looking for a type,
	a name followed by < names a template.

gcc/testsuite/ChangeLog:

	* g++.dg/template/dtor5.C: Adjust expected error.
	* g++.dg/cpp23/lookup2.C: New test.
	* g++.dg/template/dtor11.C: New test.
---
 gcc/cp/cp-tree.h                       |  1 +
 gcc/cp/parser.c                        | 69 ++++++++++++++++++++------
 gcc/cp/pt.c                            |  9 ++++
 gcc/testsuite/g++.dg/cpp23/lookup2.C   |  6 +++
 gcc/testsuite/g++.dg/template/dtor11.C | 22 ++++++++
 gcc/testsuite/g++.dg/template/dtor5.C  |  2 +-
 6 files changed, 92 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/lookup2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dtor11.C


base-commit: 3a2bcffac602f5de56537a77db1062984bcefd45
prerequisite-patch-id: c5f057b2fc6fc258a5cccda2b4efadc187347f29
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8df18c38d43..1fcd50c64fd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7263,6 +7263,7 @@  extern tree maybe_get_template_decl_from_type_decl (tree);
 extern int processing_template_parmlist;
 extern bool dependent_type_p			(tree);
 extern bool dependent_scope_p			(tree);
+extern bool dependentish_scope_p		(tree);
 extern bool any_dependent_template_arguments_p  (const_tree);
 extern bool any_erroneous_template_args_p       (const_tree);
 extern bool dependent_template_p		(tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 20f949edfe0..31bae6d8983 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18187,6 +18187,16 @@  cp_parser_template_id (cp_parser *parser,
       if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR)
 	SET_EXPR_LOCATION (template_id, combined_loc);
     }
+  else if (TREE_CODE (templ) == TYPE_DECL
+	   && TREE_CODE (TREE_TYPE (templ)) == TYPENAME_TYPE)
+    {
+      /* Some type template in dependent scope.  */
+      tree &name = TYPENAME_TYPE_FULLNAME (TREE_TYPE (templ));
+      name = build_min_nt_loc (combined_loc,
+			       TEMPLATE_ID_EXPR,
+			       name, arguments);
+      template_id = templ;
+    }
   else
     {
       /* If it's not a class-template or a template-template, it should be
@@ -18413,8 +18423,8 @@  cp_parser_template_name (cp_parser* parser,
     }
 
   /* cp_parser_lookup_name clears OBJECT_TYPE.  */
-  const bool scoped_p = ((parser->scope ? parser->scope
-			  : parser->context->object_type) != NULL_TREE);
+  tree scope = (parser->scope ? parser->scope
+		: parser->context->object_type);
 
   /* Look up the name.  */
   decl = cp_parser_lookup_name (parser, identifier,
@@ -18427,6 +18437,19 @@  cp_parser_template_name (cp_parser* parser,
 
   decl = strip_using_decl (decl);
 
+  /* 13.3 [temp.names] A < is interpreted as the delimiter of a
+    template-argument-list if it follows a name that is not a
+    conversion-function-id and
+    - that follows the keyword template or a ~ after a nested-name-specifier or
+    in a class member access expression, or
+    - for which name lookup finds the injected-class-name of a class template
+    or finds any declaration of a template, or
+    - that is an unqualified name for which name lookup either finds one or
+    more functions or finds nothing, or
+    - that is a terminal name in a using-declarator (9.9), in a declarator-id
+    (9.3.4), or in a type-only context other than a nested-name-specifier
+    (13.8).  */
+
   /* If DECL is a template, then the name was a template-name.  */
   if (TREE_CODE (decl) == TEMPLATE_DECL)
     {
@@ -18454,11 +18477,7 @@  cp_parser_template_name (cp_parser* parser,
     }
   else
     {
-      /* The standard does not explicitly indicate whether a name that
-	 names a set of overloaded declarations, some of which are
-	 templates, is a template-name.  However, such a name should
-	 be a template-name; otherwise, there is no way to form a
-	 template-id for the overloaded templates.  */
+      /* Look through an overload set for any templates.  */
       bool found = false;
 
       for (lkp_iterator iter (MAYBE_BASELINK_FUNCTIONS (decl));
@@ -18466,34 +18485,41 @@  cp_parser_template_name (cp_parser* parser,
 	if (TREE_CODE (*iter) == TEMPLATE_DECL)
 	  found = true;
 
+      /* "an unqualified name for which name lookup either finds one or more
+	 functions or finds nothing".  */
       if (!found
 	  && (cxx_dialect > cxx17)
-	  && !scoped_p
+	  && !scope
 	  && cp_lexer_next_token_is (parser->lexer, CPP_LESS)
 	  && tag_type == none_type)
 	{
-	  /* [temp.names] says "A name is also considered to refer to a template
-	     if it is an unqualified-id followed by a < and name lookup finds
-	     either one or more functions or finds nothing."  */
-
 	  /* The "more functions" case.  Just use the OVERLOAD as normally.
 	     We don't use is_overloaded_fn here to avoid considering
 	     BASELINKs.  */
 	  if (TREE_CODE (decl) == OVERLOAD
 	      /* Name lookup found one function.  */
-	      || TREE_CODE (decl) == FUNCTION_DECL)
+	      || TREE_CODE (decl) == FUNCTION_DECL
+	      /* Name lookup found nothing.  */
+	      || decl == error_mark_node)
 	    found = true;
-	  /* Name lookup found nothing.  */
-	  else if (decl == error_mark_node)
-	    return identifier;
 	}
 
+      /* "in a type-only context" */
+      if (!found && scope
+	  && tag_type != none_type
+	  && dependentish_scope_p (scope)
+	  && cp_parser_nth_token_starts_template_argument_list_p (parser, 1))
+	found = true;
+
       if (!found)
 	{
 	  /* The name does not name a template.  */
 	  cp_parser_error (parser, "expected template-name");
 	  return error_mark_node;
 	}
+      else if (decl == error_mark_node)
+	/* Repeat the lookup at instantiation time.  */
+	decl = identifier;
     }
 
   return decl;
@@ -30373,6 +30399,17 @@  cp_parser_lookup_name (cp_parser *parser, tree name,
 			       consider class templates.  */
 			    : is_template ? LOOK_want::TYPE
 			    : prefer_type_arg (tag_type));
+
+      /* If we know we're looking for a type (e.g. A in p->A::x),
+	 mock up a typename.  */
+      if (!decl && object_type && tag_type != none_type
+	  && dependentish_scope_p (object_type))
+	{
+	  tree type = build_typename_type (object_type, name, name,
+					   typename_type);
+	  decl = TYPE_NAME (type);
+	}
+
       parser->object_scope = object_type;
       parser->qualifying_scope = NULL_TREE;
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 12c8812d8b2..4d42899f28d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -26970,6 +26970,15 @@  dependent_scope_p (tree scope)
 	  && !currently_open_class (scope));
 }
 
+/* True if we might find more declarations in SCOPE during instantiation than
+   we can when parsing the template.  */
+
+bool
+dependentish_scope_p (tree scope)
+{
+  return dependent_scope_p (scope) || any_dependent_bases_p (scope);
+}
+
 /* T is a SCOPE_REF.  Return whether it represents a non-static member of
    an unknown base of 'this' (and is therefore instantiation-dependent).  */
 
diff --git a/gcc/testsuite/g++.dg/cpp23/lookup2.C b/gcc/testsuite/g++.dg/cpp23/lookup2.C
new file mode 100644
index 00000000000..a16afbe2196
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/lookup2.C
@@ -0,0 +1,6 @@ 
+// DR 1835
+
+template <class T> void f(T t) { t.foo::bar(); }
+struct foo { void bar(); };
+struct baz : foo { };
+int main() { f(baz()); }
diff --git a/gcc/testsuite/g++.dg/template/dtor11.C b/gcc/testsuite/g++.dg/template/dtor11.C
new file mode 100644
index 00000000000..9bb58b41d47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dtor11.C
@@ -0,0 +1,22 @@ 
+template <class T>
+struct B
+{
+  void f(T *p)
+  {
+    p->template A<int>::~A<int>();
+    p->A::~A();
+    p->~A<int>();
+    p->~A();
+    p->~T();
+    p->T::~T();
+  }
+};
+
+template <class T>
+struct A
+{ };
+
+int main()
+{
+  B<A<int> >().f(0);
+}
diff --git a/gcc/testsuite/g++.dg/template/dtor5.C b/gcc/testsuite/g++.dg/template/dtor5.C
index 8fa4eeb6f06..d9a1c692a34 100644
--- a/gcc/testsuite/g++.dg/template/dtor5.C
+++ b/gcc/testsuite/g++.dg/template/dtor5.C
@@ -11,7 +11,7 @@  template <class T> void f(A<T> *ap) {
 } 
 
 template <class T> void g(A<T> *ap) {
-  ap->~B(); 			// { dg-error "destructor name" }
+  ap->~B(); 			// { dg-error "" }
 } 
 
 int main()