[gcc/devel/ranger] c++: Member template function lookup failure [PR94799]

Aldy Hernandez aldyh@gcc.gnu.org
Wed Jun 17 21:03:03 GMT 2020


https://gcc.gnu.org/g:ef3479afc5ab415f00a53fc6f6a990df7f6a0747

commit ef3479afc5ab415f00a53fc6f6a990df7f6a0747
Author: Marek Polacek <polacek@redhat.com>
Date:   Tue Apr 28 22:30:44 2020 -0400

    c++: Member template function lookup failure [PR94799]
    
    Whew, this took a while.  We fail to parse "p->template A<T>::a()"
    (where p is of type A<T> *) because since r249752 we treat the RHS of the ->
    as dependent and avoid a lookup in the enclosing context: since that rev
    cp_parser_template_name checks parser->context->object_type too, which
    here is unknown_type_node, signalling a type-dependent object:
    
     7756   if (dependent_p)
     7757     /* Tell cp_parser_lookup_name that there was an object, even though it's
     7758        type-dependent.  */
     7759     parser->context->object_type = unknown_type_node;
    
    with which cp_parser_template_name returns identifier 'A', cp_parser_class_name
    then creates a TEMPLATE_ID_EXPR A<T>, but then
    
    23735       decl = make_typename_type (scope, decl, tag_type, tf_error);
    
    in cp_parser_class_name fails because scope is NULL.  Then we return
    error_mark_node and parse errors ensue.
    
    I've tried various approaches, e.g. keeping TEMPLATE_ID_EXPR around
    instead of calling make_typename_type, which didn't work, whereupon I
    realized that since we don't want to perform name lookup if we've seen
    the template keyword and the scope is dependent, we can adjust
    parser->context->object_type and use the type of the object expression
    as the scope, even if it's type-dependent.  This should be in line with
    [basic.lookup.classref]p4.  If the postfix expression doesn't have a type,
    use typeof to carry its type.  This typeof will be processed in
    tsubst/TYPENAME_TYPE.
    
            PR c++/94799
            * parser.c (cp_parser_postfix_dot_deref_expression): If we have
            a type-dependent object of class type, stash it to
            parser->context->object_type.  If the postfix expression doesn't have
            a type, use typeof.
            (cp_parser_class_name): Consider object scope too.
            (cp_parser_lookup_name): Remove code dealing with the case when
            object_type is unknown_type_node.
    
            * g++.dg/lookup/this1.C: Adjust dg-error.
            * g++.dg/template/lookup12.C: New test.
            * g++.dg/template/lookup13.C: New test.
            * g++.dg/template/lookup14.C: New test.
            * g++.dg/template/lookup15.C: New test.

Diff:
---
 gcc/cp/ChangeLog                         | 12 ++++++++++++
 gcc/cp/parser.c                          | 26 +++++++++++++++++---------
 gcc/testsuite/ChangeLog                  |  9 +++++++++
 gcc/testsuite/g++.dg/lookup/this1.C      |  2 +-
 gcc/testsuite/g++.dg/template/lookup12.C | 26 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/lookup13.C | 28 ++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/lookup14.C | 11 +++++++++++
 gcc/testsuite/g++.dg/template/lookup15.C | 24 ++++++++++++++++++++++++
 8 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index a3ae5efba01..ef82d93bc07 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,15 @@
+2020-05-05  Marek Polacek  <polacek@redhat.com>
+	    Jason Merrill  <jason@redhat.com>
+
+	PR c++/94799
+	* parser.c (cp_parser_postfix_dot_deref_expression): If we have
+	a type-dependent object of class type, stash it to
+	parser->context->object_type.  If the postfix expression doesn't have
+	a type, use typeof.
+	(cp_parser_class_name): Consider object scope too.
+	(cp_parser_lookup_name): Remove code dealing with the case when
+	object_type is unknown_type_node.
+
 2020-05-04  Patrick Palka  <ppalka@redhat.com>
 
 	PR c++/94038
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 337f22d2784..5832025443d 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7754,9 +7754,14 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
     }
 
   if (dependent_p)
-    /* Tell cp_parser_lookup_name that there was an object, even though it's
-       type-dependent.  */
-    parser->context->object_type = unknown_type_node;
+    {
+      tree type = TREE_TYPE (postfix_expression);
+      /* If we don't have a (type-dependent) object of class type, use
+	 typeof to figure out the type of the object.  */
+      if (type == NULL_TREE)
+	type = finish_typeof (postfix_expression);
+      parser->context->object_type = type;
+    }
 
   /* Assume this expression is not a pseudo-destructor access.  */
   pseudo_destructor_p = false;
@@ -23625,8 +23630,15 @@ cp_parser_class_name (cp_parser *parser,
     }
 
   /* PARSER->SCOPE can be cleared when parsing the template-arguments
-     to a template-id, so we save it here.  */
-  scope = parser->scope;
+     to a template-id, so we save it here.  Consider object scope too,
+     so that make_typename_type below can use it (cp_parser_template_name
+     considers object scope also).  This may happen with code like
+
+       p->template A<T>::a()
+
+      where we first want to look up A<T>::a in the class of the object
+      expression, as per [basic.lookup.classref].  */
+  scope = parser->scope ? parser->scope : parser->context->object_type;
   if (scope == error_mark_node)
     return error_mark_node;
 
@@ -28340,10 +28352,6 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 	decl = lookup_name_real (name, prefer_type_arg (tag_type, is_template),
 				 /*nonclass=*/0,
 				 /*block_p=*/true, is_namespace, 0);
-      if (object_type == unknown_type_node)
-	/* The object is type-dependent, so we can't look anything up; we used
-	   this to get the DR 141 behavior.  */
-	object_type = NULL_TREE;
       parser->object_scope = object_type;
       parser->qualifying_scope = NULL_TREE;
     }
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 88f8d06f1fb..c23213a847a 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,12 @@
+2020-05-05  Marek Polacek  <polacek@redhat.com>
+
+	PR c++/94799
+	* g++.dg/lookup/this1.C: Adjust dg-error.
+	* g++.dg/template/lookup12.C: New test.
+	* g++.dg/template/lookup13.C: New test.
+	* g++.dg/template/lookup14.C: New test.
+	* g++.dg/template/lookup15.C: New test.
+
 2020-05-05  Martin Liska  <mliska@suse.cz>
 
 	* gcc.dg/spellcheck-options-22.c: New test.
diff --git a/gcc/testsuite/g++.dg/lookup/this1.C b/gcc/testsuite/g++.dg/lookup/this1.C
index 20051bf7515..6b85cefcd37 100644
--- a/gcc/testsuite/g++.dg/lookup/this1.C
+++ b/gcc/testsuite/g++.dg/lookup/this1.C
@@ -4,5 +4,5 @@
 struct A
 {
     template<int> static void foo();
-    static void bar() { this->A::foo<0>(); } // { dg-error "unavailable" }
+    static void bar() { this->A::foo<0>(); } // { dg-error "unavailable|not a class|expected" }
 };
diff --git a/gcc/testsuite/g++.dg/template/lookup12.C b/gcc/testsuite/g++.dg/template/lookup12.C
new file mode 100644
index 00000000000..fc5939ab0f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/lookup12.C
@@ -0,0 +1,26 @@
+// PR c++/94799 - member template function lookup fails.
+
+template<typename T> struct B {
+  void foo ();
+  int i;
+};
+
+template<typename T>
+struct D : public B<T> { };
+
+template<typename T>
+void fn (D<T> d)
+{
+  d.template B<T>::foo ();
+  d.template B<T>::i = 42;
+  D<T>().template B<T>::foo ();
+  d.template D<T>::template B<T>::foo ();
+  d.template D<T>::template B<T>::i = 10;
+}
+
+int
+main ()
+{
+  D<int> d;
+  fn(d);
+}
diff --git a/gcc/testsuite/g++.dg/template/lookup13.C b/gcc/testsuite/g++.dg/template/lookup13.C
new file mode 100644
index 00000000000..a8c7e18a707
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/lookup13.C
@@ -0,0 +1,28 @@
+// PR c++/94799 - member template function lookup fails.
+
+template <typename T>
+struct A {
+    int a() {
+        return 42;
+    }
+
+    template<typename> struct X { typedef int type; };
+};
+
+template <typename T>
+struct B {
+    int b(A<T> *p) {
+	int i = 0;
+        i += p->a();
+        i += p->template A<T>::a();
+        i += p->template A<T>::template A<T>::a();
+	i += A<T>().template A<T>::a();
+	return i;
+    }
+};
+
+int main() {
+    A<int> a;
+    B<int> b;
+    return b.b(&a);
+}
diff --git a/gcc/testsuite/g++.dg/template/lookup14.C b/gcc/testsuite/g++.dg/template/lookup14.C
new file mode 100644
index 00000000000..e1c945a6dca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/lookup14.C
@@ -0,0 +1,11 @@
+// PR c++/94799 - member template function lookup fails.
+
+template<typename T>
+struct A { };
+
+template<typename T>
+void fn (A<T> a)
+{
+  // Don't perform name lookup of foo when parsing this template.
+  a.template A<T>::foo ();
+}
diff --git a/gcc/testsuite/g++.dg/template/lookup15.C b/gcc/testsuite/g++.dg/template/lookup15.C
new file mode 100644
index 00000000000..c7f3ba01576
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/lookup15.C
@@ -0,0 +1,24 @@
+// PR c++/94799 - member template function lookup fails.
+
+template<typename>
+struct M { void fn() { } };
+
+M<int>* bar (int);
+M<int> bar2 (int);
+
+template<typename T>
+struct X : M<T> {
+  void xfn ()
+  {
+    this->template M<T>::fn ();
+    bar((T)1)->template M<T>::fn ();
+    bar2((T)1).template M<T>::fn ();
+  }
+};
+
+int
+main ()
+{
+  X<int> x;
+  x.xfn();
+}


More information about the Gcc-cvs mailing list