diff mbox series

c++: Member template function lookup failure [PR94799]

Message ID 20201020005202.64863-1-polacek@redhat.com
State New
Headers show
Series c++: Member template function lookup failure [PR94799] | expand

Commit Message

Marek Polacek Oct. 20, 2020, 12:52 a.m. UTC
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


base-commit: 970d683f67777319990b30302a21a860990e2ec8

Comments

Jason Merrill Oct. 28, 2020, 7:19 p.m. UTC | #1
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
>
diff mbox series

Patch

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>());
+}