public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Member template function lookup failure [PR94799]
@ 2020-10-20  0:52 Marek Polacek
  2020-10-28 19:19 ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2020-10-20  0:52 UTC (permalink / raw)
  To: Jason Merrill, GCC Patches

My earlier patch for this PR, r11-86, broke pybind11.  That patch
changed cp_parser_class_name to also consider the object expression
scope (parser->context->object_type) to fix parsing of

  p->template A<T>::foo(); // consider p's scope too

Here we reject

  b.operator typename B<T>::type();

because 'typename_p' in cp_parser_class_name uses 'scope', which means
that 'typename_p' will be true for the example above.  Then we create
a TYPENAME_TYPE via make_typename_type, which fails when tsubsting it;
the code basically created 'typename B::B' and then we complain that there
is no member named 'B' in 'A<int>'.  So, when deciding if we should
create a TYPENAME_TYPE, don't consider the object_type scope, like we
did pre-r11-86.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

gcc/cp/ChangeLog:

	PR c++/94799
	* parser.c (cp_parser_class_name): Use parser->scope when
	setting typename_p.

gcc/testsuite/ChangeLog:

	PR c++/94799
	* g++.dg/template/lookup16.C: New test.
---
 gcc/cp/parser.c                          | 13 ++++++-------
 gcc/testsuite/g++.dg/template/lookup16.C | 23 +++++++++++++++++++++++
 2 files changed, 29 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/lookup16.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7ec7d42773c..ecbd4b7d15a 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -23784,13 +23784,10 @@ cp_parser_class_name (cp_parser *parser,
 		      bool enum_ok)
 {
   tree decl;
-  tree scope;
-  bool typename_p;
-  cp_token *token;
   tree identifier = NULL_TREE;
 
   /* All class-names start with an identifier.  */
-  token = cp_lexer_peek_token (parser->lexer);
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
   if (token->type != CPP_NAME && token->type != CPP_TEMPLATE_ID)
     {
       cp_parser_error (parser, "expected class-name");
@@ -23806,14 +23803,16 @@ cp_parser_class_name (cp_parser *parser,
 
       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;
+  tree scope = parser->scope ? parser->scope : parser->context->object_type;
   if (scope == error_mark_node)
     return error_mark_node;
 
   /* Any name names a type if we're following the `typename' keyword
      in a qualified name where the enclosing scope is type-dependent.  */
-  typename_p = (typename_keyword_p && scope && TYPE_P (scope)
-		&& dependent_type_p (scope));
+  const bool typename_p = (typename_keyword_p
+			   && parser->scope
+			   && TYPE_P (parser->scope)
+			   && dependent_type_p (parser->scope));
   /* Handle the common case (an identifier, but not a template-id)
      efficiently.  */
   if (token->type == CPP_NAME
diff --git a/gcc/testsuite/g++.dg/template/lookup16.C b/gcc/testsuite/g++.dg/template/lookup16.C
new file mode 100644
index 00000000000..5b34c08282c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/lookup16.C
@@ -0,0 +1,23 @@
+// PR c++/94799
+// { dg-do compile { target c++11 } }
+
+template <typename> struct A {
+  typedef int type;
+  operator int();
+};
+
+template <typename T> using B = A<T>;
+
+template <typename T> typename B<T>::type foo(B<T> b)
+{
+  auto r1 = b.operator typename A<T>::type();
+  auto r2 = b.operator typename A<T>::template A<T>::type();
+  auto r3 = b.operator typename B<T>::type();
+  auto r4 = b.operator typename B<T>::template A<T>::type();
+  return r1 + r2 + r3 + r4;
+}
+
+void bar()
+{
+  foo(A<int>());
+}

base-commit: 970d683f67777319990b30302a21a860990e2ec8
-- 
2.26.2


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: Member template function lookup failure [PR94799]
  2020-10-20  0:52 [PATCH] c++: Member template function lookup failure [PR94799] Marek Polacek
@ 2020-10-28 19:19 ` Jason Merrill
  0 siblings, 0 replies; 4+ messages in thread
From: Jason Merrill @ 2020-10-28 19:19 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches

On 10/19/20 8:52 PM, Marek Polacek wrote:
> My earlier patch for this PR, r11-86, broke pybind11.  That patch
> changed cp_parser_class_name to also consider the object expression
> scope (parser->context->object_type) to fix parsing of
> 
>    p->template A<T>::foo(); // consider p's scope too
> 
> Here we reject
> 
>    b.operator typename B<T>::type();
> 
> because 'typename_p' in cp_parser_class_name uses 'scope', which means
> that 'typename_p' will be true for the example above.  Then we create
> a TYPENAME_TYPE via make_typename_type, which fails when tsubsting it;
> the code basically created 'typename B::B' and then we complain that there
> is no member named 'B' in 'A<int>'.  So, when deciding if we should
> create a TYPENAME_TYPE, don't consider the object_type scope, like we
> did pre-r11-86.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

> gcc/cp/ChangeLog:
> 
> 	PR c++/94799
> 	* parser.c (cp_parser_class_name): Use parser->scope when
> 	setting typename_p.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/94799
> 	* g++.dg/template/lookup16.C: New test.
> ---
>   gcc/cp/parser.c                          | 13 ++++++-------
>   gcc/testsuite/g++.dg/template/lookup16.C | 23 +++++++++++++++++++++++
>   2 files changed, 29 insertions(+), 7 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/template/lookup16.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 7ec7d42773c..ecbd4b7d15a 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -23784,13 +23784,10 @@ cp_parser_class_name (cp_parser *parser,
>   		      bool enum_ok)
>   {
>     tree decl;
> -  tree scope;
> -  bool typename_p;
> -  cp_token *token;
>     tree identifier = NULL_TREE;
>   
>     /* All class-names start with an identifier.  */
> -  token = cp_lexer_peek_token (parser->lexer);
> +  cp_token *token = cp_lexer_peek_token (parser->lexer);
>     if (token->type != CPP_NAME && token->type != CPP_TEMPLATE_ID)
>       {
>         cp_parser_error (parser, "expected class-name");
> @@ -23806,14 +23803,16 @@ cp_parser_class_name (cp_parser *parser,
>   
>         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;
> +  tree scope = parser->scope ? parser->scope : parser->context->object_type;
>     if (scope == error_mark_node)
>       return error_mark_node;
>   
>     /* Any name names a type if we're following the `typename' keyword
>        in a qualified name where the enclosing scope is type-dependent.  */
> -  typename_p = (typename_keyword_p && scope && TYPE_P (scope)
> -		&& dependent_type_p (scope));
> +  const bool typename_p = (typename_keyword_p
> +			   && parser->scope
> +			   && TYPE_P (parser->scope)
> +			   && dependent_type_p (parser->scope));
>     /* Handle the common case (an identifier, but not a template-id)
>        efficiently.  */
>     if (token->type == CPP_NAME
> diff --git a/gcc/testsuite/g++.dg/template/lookup16.C b/gcc/testsuite/g++.dg/template/lookup16.C
> new file mode 100644
> index 00000000000..5b34c08282c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/lookup16.C
> @@ -0,0 +1,23 @@
> +// PR c++/94799
> +// { dg-do compile { target c++11 } }
> +
> +template <typename> struct A {
> +  typedef int type;
> +  operator int();
> +};
> +
> +template <typename T> using B = A<T>;
> +
> +template <typename T> typename B<T>::type foo(B<T> b)
> +{
> +  auto r1 = b.operator typename A<T>::type();
> +  auto r2 = b.operator typename A<T>::template A<T>::type();
> +  auto r3 = b.operator typename B<T>::type();
> +  auto r4 = b.operator typename B<T>::template A<T>::type();
> +  return r1 + r2 + r3 + r4;
> +}
> +
> +void bar()
> +{
> +  foo(A<int>());
> +}
> 
> base-commit: 970d683f67777319990b30302a21a860990e2ec8
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: Member template function lookup failure [PR94799]
  2020-04-29  3:55 Marek Polacek
@ 2020-04-29 21:10 ` Jason Merrill
  0 siblings, 0 replies; 4+ messages in thread
From: Jason Merrill @ 2020-04-29 21:10 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches

On 4/28/20 11:55 PM, Marek Polacek wrote:
> 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.

> The "&& scope != unknown_type_node" line in cp_parser_class_name is there
> for diagnostic purposes only (to avoid issuing a confusing error).
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> (Happy to defer to GCC 11 if this doesn't seem very safe.)
> 
> 	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.
> 	(cp_parser_class_name): Consider object scope too.  Don't call
> 	make_typename_type when the scope 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.
> ---
>   gcc/cp/parser.c                          | 28 ++++++++++++++++++------
>   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 ++++++++++
>   5 files changed, 87 insertions(+), 8 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/template/lookup12.C
>   create mode 100644 gcc/testsuite/g++.dg/template/lookup13.C
>   create mode 100644 gcc/testsuite/g++.dg/template/lookup14.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index e1f9786893a..b344721fb60 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -7694,11 +7694,16 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
>     bool pseudo_destructor_p;
>     tree scope = NULL_TREE;
>     location_t start_loc = postfix_expression.get_start ();
> +  tree type = TREE_TYPE (postfix_expression);
>   
>     /* If this is a `->' operator, dereference the pointer.  */
>     if (token_type == CPP_DEREF)
> -    postfix_expression = build_x_arrow (location, postfix_expression,
> -					tf_warning_or_error);
> +    {
> +      if (type && POINTER_TYPE_P (type))
> +	type = TREE_TYPE (type);
> +      postfix_expression = build_x_arrow (location, postfix_expression,
> +					  tf_warning_or_error);
> +    }
>     /* Check to see whether or not the expression is type-dependent and
>        not the current instantiation.  */
>     dependent_p = type_dependent_object_expression_p (postfix_expression);
> @@ -7754,9 +7759,10 @@ 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;
> +    /* If we don't have a (type-dependent) object of class type, use
> +       unknown_type_node to signal that there was an object.  */
> +    parser->context->object_type = (type && CLASS_TYPE_P (type)
> +				    ? type : unknown_type_node);

Anything that depends on CLASS_TYPE_P won't work if 'p' isn't clearly a 
class, i.e. if it has type T*, T, or NULL_TREE.  Why not use any 
non-null type here?

For null type, I wonder if using decltype would make sense.

>     /* Assume this expression is not a pseudo-destructor access.  */
>     pseudo_destructor_p = false;
> @@ -23625,8 +23631,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;
>   
> @@ -23720,6 +23733,7 @@ cp_parser_class_name (cp_parser *parser,
>     /* Check to see that it is really the name of a class.  */
>     if (TREE_CODE (decl) == TEMPLATE_ID_EXPR
>         && identifier_p (TREE_OPERAND (decl, 0))
> +      && scope != unknown_type_node

This will make the more general dependent case give an error.

>         && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
>       /* Situations like this:
>   
> 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 ();
> +}
> 
> base-commit: 19667c82e479dc2bf8351588ed57aff90220b748
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] c++: Member template function lookup failure [PR94799]
@ 2020-04-29  3:55 Marek Polacek
  2020-04-29 21:10 ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2020-04-29  3:55 UTC (permalink / raw)
  To: Jason Merrill, GCC Patches

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.

The "&& scope != unknown_type_node" line in cp_parser_class_name is there
for diagnostic purposes only (to avoid issuing a confusing error).

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
(Happy to defer to GCC 11 if this doesn't seem very safe.)

	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.
	(cp_parser_class_name): Consider object scope too.  Don't call
	make_typename_type when the scope 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.
---
 gcc/cp/parser.c                          | 28 ++++++++++++++++++------
 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 ++++++++++
 5 files changed, 87 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/lookup12.C
 create mode 100644 gcc/testsuite/g++.dg/template/lookup13.C
 create mode 100644 gcc/testsuite/g++.dg/template/lookup14.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index e1f9786893a..b344721fb60 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7694,11 +7694,16 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
   bool pseudo_destructor_p;
   tree scope = NULL_TREE;
   location_t start_loc = postfix_expression.get_start ();
+  tree type = TREE_TYPE (postfix_expression);
 
   /* If this is a `->' operator, dereference the pointer.  */
   if (token_type == CPP_DEREF)
-    postfix_expression = build_x_arrow (location, postfix_expression,
-					tf_warning_or_error);
+    {
+      if (type && POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      postfix_expression = build_x_arrow (location, postfix_expression,
+					  tf_warning_or_error);
+    }
   /* Check to see whether or not the expression is type-dependent and
      not the current instantiation.  */
   dependent_p = type_dependent_object_expression_p (postfix_expression);
@@ -7754,9 +7759,10 @@ 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;
+    /* If we don't have a (type-dependent) object of class type, use
+       unknown_type_node to signal that there was an object.  */
+    parser->context->object_type = (type && CLASS_TYPE_P (type)
+				    ? type : unknown_type_node);
 
   /* Assume this expression is not a pseudo-destructor access.  */
   pseudo_destructor_p = false;
@@ -23625,8 +23631,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;
 
@@ -23720,6 +23733,7 @@ cp_parser_class_name (cp_parser *parser,
   /* Check to see that it is really the name of a class.  */
   if (TREE_CODE (decl) == TEMPLATE_ID_EXPR
       && identifier_p (TREE_OPERAND (decl, 0))
+      && scope != unknown_type_node
       && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
     /* Situations like this:
 
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 ();
+}

base-commit: 19667c82e479dc2bf8351588ed57aff90220b748
-- 
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-10-28 19:19 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-20  0:52 [PATCH] c++: Member template function lookup failure [PR94799] Marek Polacek
2020-10-28 19:19 ` Jason Merrill
  -- strict thread matches above, loose matches on Subject: below --
2020-04-29  3:55 Marek Polacek
2020-04-29 21:10 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).