public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: error message for dependent template members [PR70417]
@ 2021-08-20 16:56 Anthony Sharp
  2021-08-27 21:33 ` Jason Merrill
  0 siblings, 1 reply; 12+ messages in thread
From: Anthony Sharp @ 2021-08-20 16:56 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 6287 bytes --]

Hi, hope everyone is well. I have a patch here for issue 70417
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70417). I'm still a GCC
noob, and this is probably the hardest thing I have ever coded in my
life, so please forgive any initial mistakes!

TLDR

This patch introduces a helpful error message when the user attempts
to put a template-id after a member access token (., -> or ::) in a
dependent context without having put the "template" keyword after the
access token.

CONTEXT

In C++, when referencing a class member (using ., -> or ::) in a
dependent context, if that member is a template, the template keyword
is required after the access token. For example:

template<class T> void MyDependentTemplate(T t)
{
  t.DoSomething<T>(); /* Error - t is dependent. "<" is treated as a
less-than sign because DoSomething is assumed to be a non-template
identifier. */
  t.template DoSomething<T>(); // Good.
}

PATCH EXPLANATION

In order to throw an appropriate error message we need to identify
and/or create a point in the compiler where the following conditions
are all simultaneously satisfied:

A) a class member access token (., ->, ::)
B) a dependent context
C) the thing being accessed is a template-id
D) No template keyword

A, B and D are relatively straightforward and I won't go into detail
about how that was achieved. The real challenge is C - figuring out
whether a name is a template-id.

Usually, we would look up the name and use that to determine whether
the name is a template or not. But, we cannot do that. We are in a
dependent context, so lookup cannot (in theory) find anything at the
point the access expression is parsed.

We (maybe) could defer checking until the template is actually
instantiated. But this is not the approach I have gone for, since this
to me sounded unnecessarily awkward and clunky. In my mind this also
seems like a syntax error, and IMO it seems more natural that syntax
errors should get caught as soon as they are parsed.

So, the solution I went for was to figure out whether the name was a
template by looking at the surrounding tokens. The key to this lies in
being able to differentiate between the start and end of a template
parameter list (< and >) and greater-than and less-than tokens (and
perhaps also things like << or >>). If the final > (if we find one at
all) does indeed mark the end of a class or function template, then it
will be followed by something like (, ::, or even just ;. On the other
hand, a greater-than operator would be followed by a name.

PERFORMANCE IMPACT

My concern with this approach was that checking this would actually
slow the compiler down. In the end it seemed the opposite was true. By
parsing the tokens to determine whether the name looks like a
template-id, we can cut out a lot of the template-checking code that
gets run trying to find a template when there is none, making compile
time generally faster. That being said, I'm not sure if the
improvement will be that substantial, so I did not feel it necessary
to introduce the template checking method everywhere where we could
possibly have run into a template.

I ran an ABAB test with the following code repeated enough times to
fill up about 10,000 lines:

ai = 1;
bi = 2;
ci = 3;
c.x = 4;
(&c)->x = 5;
mytemplateclass<Y>::y = 6;
c.template class_func<Y>();
(&c)->template class_func<Y>();
mytemplateclass<Y>::template class_func_static<Y>();
c2.class_func<myclass>();
(&c2)->class_func<myclass>();
myclass::class_func_static<myclass>();
ai = 6;
bi = 5;
ci = 4;
c.x = 3;
(&c)->x = 2;
mytemplateclass<Y>::y = 1;
c.template class_func<Y>();
(&c)->template class_func<Y>();
mytemplateclass<Y>::template class_func_static<Y>();
c2.class_func<myclass>();
(&c2)->class_func<myclass>();
myclass::class_func_static<myclass>();

It basically just contains a mixture of class access expressions plus
some regular assignments for good measure. The compiler was compiled
without any optimisations (and so slower than usual). In hindsight,
perhaps this test case assumes the worst-ish-case scenario since it
contains a lot of templates; most of the benefits of this patch occur
when parsing non-templates.

The compiler (clean) and the compiler with the patch applied (patched)
compiled the above code 20 times each in ABAB fashion. On average, the
clean compiler took 2.26295 seconds and the patched compiler took
2.25145 seconds (about 0.508% faster). Whether this improvement
remains or disappears when the compiler is built with optimisations
turned on I haven't tested. My main concern was just making sure it
didn't get any slower.

AWKWARD TEST CASES

You will see from the patch that it required the updating of a few
testcases (as well as one place within the compiler). I believe this
is because up until now, the compiler allowed the omission of the
template keyword in some places it shouldn't have. Take decltype34.C
for example:

struct impl
{
  template <class T> static T create();
};

template<class T, class U, class =
decltype(impl::create<T>()->impl::create<U>())>
struct tester{};

GCC currently accepts this code. My patch causes it to be rejected.
For what it's worth, Clang also rejects this code:

1824786093/source.cpp:6:70: error: use 'template' keyword to treat
'create' as a dependent template name
template<class T, class U, class =
decltype(impl::create<T>()->impl::create<U>())>

                                      ^ template

I think Clang is correct since from what I understand it should be
written as impl::create<T>()->impl::template create<U>(). Here,
create<T>() is dependent, so it makes sense that we would need
"template" before the scope operator. Then again, I could well be
wrong. The rules around dependent template names are pretty crazy.

REGRESSION TESTING

No differences between clean/patched g++.sum, gcc.sum and
libstdc++.sum. Bootstraps fine. Built on x86_64-pc-linux-gnu for
x86_64-pc-linux-gnu.

CONCLUSION

Some of the changes are a bit debatable, e.g. passing and adding new
arguments to various functions just for the sake of a slight
performance improvement - I don't really have the high-level
experience to be able to judge whether that is worth the increased
code complexity or not, so I just went with my gut.

Please find patch attached :)

Kind regards,
Anthony Sharp

[-- Attachment #2: pr70417.patch --]
[-- Type: text/x-patch, Size: 34195 bytes --]

From 0316e821607f6875293a664ab181747188ef3e73 Mon Sep 17 00:00:00 2001
From: Anthony Sharp <anthonysharp15@gmail.com>
Date: Thu, 19 Aug 2021 17:51:38 +0100
Subject: [PATCH] c++: error message for dependent template members [PR70417]

Add a helpful error message for when the user forgets to
include the "template" keyword after ., -> or :: when
accessing a member in a dependent context, where the member is a
template.

Fix some cases where the compiler was allowing the exclusion
of the template keyword when it shouldn't have been.

gcc/cp/ChangeLog:

2021-08-15  Anthony Sharp  <anthonysharp15@gmail.com>

	* parser.c (next_token_begins_template_id): New method to
	check whether we are looking at what syntactically
	looks like a template-id.
	(cp_parser_id_expression): Added dependent_expression_p as
	function argument.  Check whether the id looks like a
	template; only attempt to parse it as one if so.  Throw
	error if missing "template" keyword.
	(cp_parser_unqualified_id): Pass looks_like_template through
	this function to cp_parser_template_id_expr to avoid repeating
	logic unnecessarily.
	(cp_parser_postfix_dot_deref_expression): Pass dependent_p
	to cp_parser_id_expression.
	(cp_parser_template_id): Pass looks_like_template to avoid
	repeating logic.
	(cp_parser_template_id_expr): As above.
	(cp_parser_parse_and_diagnose_invalid_type_name):
	Minor update to a function call.
	(cp_parser_decltype_expr): As above.
	(cp_parser_default_template_template_argument): As above.
	(cp_parser_template_argument): As above.
	(cp_parser_primary_expression): As above.
	(cp_parser_simple_type_specifier): As above.
	(cp_parser_pseudo_destructor_name): As above.
	(cp_parser_type_name): As above.
	(cp_parser_elaborated_type_specifier): As above.
	(cp_parser_using_declaration): As above.
	(cp_parser_declarator_id): As above.
	(cp_parser_class_name): As above.
	(cp_parser_class_head): AS above.
	(cp_parser_type_requirement): As above.
	(cp_parser_constructor_declarator_p): As above.
	(cp_parser_omp_var_list_no_open): As above.
	(cp_parser_omp_clause_reduction): As above.
	(cp_parser_omp_clause_linear): As above.
	(cp_parser_omp_clause_detach): As above.
	(cp_parser_omp_for_loop_init): As above.
	(cp_finish_omp_declare_variant): As above.
	(cp_parser_omp_declare_reduction_exprs): As above.
	(cp_parser_oacc_routine): As above.

2021-08-15  Anthony Sharp  <anthonysharp15@gmail.com>

gcc/ChangeLog:

        * symbol-summary.h: Added missing template keyword.

2021-08-15  Anthony Sharp  <anthonysharp15@gmail.com>

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/decltype34.C: Add missing template
	keyword(s).
	* g++.dg/parse/template26.C: As above.
	* g++.old-deja/g++.pt/explicit81.C: As above.
	* g++.dg/template/dependent-name15.C: New test for
	pr70417.
---
 gcc/cp/parser.c                               | 376 +++++++++++++-----
 gcc/symbol-summary.h                          |   4 +-
 gcc/testsuite/g++.dg/cpp0x/decltype34.C       |   4 +-
 gcc/testsuite/g++.dg/parse/template26.C       |   6 +-
 .../g++.dg/template/dependent-name15.C        |  46 +++
 .../g++.old-deja/g++.pt/explicit81.C          |   4 +-
 6 files changed, 341 insertions(+), 99 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name15.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 45216f0a222..12fa945c1e5 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2108,9 +2108,9 @@ static void cp_parser_translation_unit (cp_parser *);
 static cp_expr cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
 static cp_expr cp_parser_id_expression
-  (cp_parser *, bool, bool, bool *, bool, bool);
+  (cp_parser *, bool, bool, bool *, bool, bool, bool *);
 static cp_expr cp_parser_unqualified_id
-  (cp_parser *, bool, bool, bool, bool);
+  (cp_parser *, bool, bool, bool, bool, int);
 static tree cp_parser_nested_name_specifier_opt
   (cp_parser *, bool, bool, bool, bool, bool = false);
 static tree cp_parser_nested_name_specifier
@@ -2438,9 +2438,9 @@ static tree cp_parser_template_parameter
 static tree cp_parser_type_parameter
   (cp_parser *, bool *);
 static tree cp_parser_template_id
-  (cp_parser *, bool, bool, enum tag_types, bool);
+  (cp_parser *, bool, bool, enum tag_types, bool, int);
 static tree cp_parser_template_id_expr
-  (cp_parser *, bool, bool, bool);
+  (cp_parser *, bool, bool, bool, int);
 static tree cp_parser_template_name
   (cp_parser *, bool, bool, bool, enum tag_types, bool *);
 static tree cp_parser_template_argument_list
@@ -3665,7 +3665,8 @@ cp_parser_parse_and_diagnose_invalid_type_name (cp_parser *parser)
 				/*check_dependency_p=*/true,
 				/*template_p=*/NULL,
 				/*declarator_p=*/false,
-				/*optional_p=*/false);
+				/*optional_p=*/false,
+				/*dependent_expression_p=*/NULL);
   /* If the next token is a (, this is a function with no explicit return
      type, i.e. constructor, destructor or conversion op.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
@@ -5861,7 +5862,8 @@ cp_parser_primary_expression (cp_parser *parser,
 				     /*check_dependency_p=*/true,
 				     &template_p,
 				     /*declarator_p=*/false,
-				     /*optional_p=*/false);
+				     /*optional_p=*/false,
+				     /*dependent_expression_p=*/NULL);
 	if (id_expression == error_mark_node)
 	  return error_mark_node;
 	id_expr_token = token;
@@ -6028,6 +6030,106 @@ cp_parser_primary_expression (cp_parser *parser,
 				       /*decltype*/false, idk);
 }
 
+/* Check if we are looking at what "looks" like a template-id.  Of course,
+   we can't technically know for sure whether it is a template-id without
+   doing lookup (although, the reason we are here is because the context
+   might be dependent and so it might not be possible to do any lookup
+   yet).  Return 1 if it does look like a template-id.  Return 2
+   if not.  */
+static int
+next_token_begins_template_id (cp_parser* parser)
+{
+  cp_token* next_token = cp_lexer_peek_token (parser->lexer);
+  cp_token* following_token;
+  int depth = 1;
+  int i = 3;
+  bool saved = false;
+
+  /* For performance's sake, quickly return from the most common case.  */
+  if (next_token->type == CPP_NAME
+      && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_LESS)
+    return 2;
+
+  /* For these purposes we must believe all "template" keywords; identifiers
+     without <> at the end can still be template-ids, but they require the
+     template keyword.  The template keyword is the only way we can tell those
+     kinds of ids are template-ids.  */
+  if (next_token->keyword == RID_TEMPLATE)
+    {
+      /* But at least make sure it's properly formed (e.g. see PR19397).  */
+      if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_NAME)
+	return 1;
+
+      return 2;
+    }
+
+  /* E.g. "::operator- <>". */
+  if (next_token->keyword == RID_OPERATOR
+      && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_LESS)
+    return 1;
+
+  /* Could be a ~ referencing the destructor of a class template.
+     Temporarily eat it up so it's not in our way.  */
+  if (next_token->type == CPP_COMPL)
+    {
+      cp_lexer_save_tokens (parser->lexer);
+      cp_lexer_consume_token (parser->lexer);
+      next_token = cp_lexer_peek_token (parser->lexer);
+      saved = true;
+    }
+
+
+  if (next_token->type == CPP_TEMPLATE_ID)
+    goto yes;
+
+  if (next_token->type != CPP_NAME
+      || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_LESS)
+     goto no;
+
+  /* Find offset of final ">".  */
+  for (; depth > 0; i++)
+    {
+      switch (cp_lexer_peek_nth_token (parser->lexer, i)->type)
+	 {
+	   case CPP_LESS:
+	     depth++;
+	     break;
+	   case CPP_GREATER:
+	     depth--;
+	     break;
+	   case CPP_RSHIFT:
+	     depth-=2;
+	     break;
+	   case CPP_EOF:
+	   case CPP_SEMICOLON:
+	     goto no;
+	   default:
+	     break;
+	 }
+    }
+
+  following_token = cp_lexer_peek_nth_token (parser->lexer, i);
+
+  /* If this is a function template then we would see a "(" after the
+     final ">".  It could also be a class template constructor.  */
+  if (following_token->type == CPP_OPEN_PAREN
+      /* If this is a class template then we would expect to see ";" or a
+	  scope token after the final ">".  */
+      || following_token->type == CPP_SEMICOLON
+      || following_token->type == CPP_SCOPE)
+    goto yes;
+
+no:
+  if (saved)
+    cp_lexer_rollback_tokens (parser->lexer);
+  return 2;
+
+yes:
+  if (saved)
+    cp_lexer_rollback_tokens (parser->lexer);
+  return 1;
+}
+
 /* Parse an id-expression.
 
    id-expression:
@@ -6060,7 +6162,10 @@ cp_parser_primary_expression (cp_parser *parser,
    named is a template.
 
    If DECLARATOR_P is true, the id-expression is appearing as part of
-   a declarator, rather than as part of an expression.  */
+   a declarator, rather than as part of an expression.
+
+   If DEPENDENT_EXPRESSION_P is true, this is a dependent id-expression
+   that is not the current instantiation.  */
 
 static cp_expr
 cp_parser_id_expression (cp_parser *parser,
@@ -6068,7 +6173,8 @@ cp_parser_id_expression (cp_parser *parser,
 			 bool check_dependency_p,
 			 bool *template_p,
 			 bool declarator_p,
-			 bool optional_p)
+			 bool optional_p,
+			 bool *dependent_expression_p)
 {
   bool global_scope_p;
   bool nested_name_specifier_p;
@@ -6094,6 +6200,51 @@ cp_parser_id_expression (cp_parser *parser,
 					    template_keyword_p)
        != NULL_TREE);
 
+  /* If this doesn't look like a template-id, there is no point
+     trying to parse it as one.  By checking first, we usually speed
+     compilation up, and it allows us to spot missing "template"
+     keywords.  */
+  int looks_like_template
+    = next_token_begins_template_id (parser);
+  cp_token* next_token = cp_lexer_peek_token (parser->lexer);
+
+  /* If this is a dependent member-access expression accessing a template
+     member missing the "template" keyword, issue a helpful error message.  */
+  if (looks_like_template == 1
+      && !template_keyword_p
+      /* . and -> access tokens.  */
+      && ((dependent_expression_p != NULL
+	   && *dependent_expression_p)
+	   /* :: access expressions.  */
+	  || (parser->scope
+	      && TYPE_P (parser->scope)
+	      && dependent_scope_p (parser->scope)))
+      /* ~ before a class template-id disallows the "template" keyword.
+	 Probably because in the case of A::~A<>, it is certain that
+	 ~A is a template.  Note that a destructor cannot actually be a
+	 template, but you can call the destructor of a class template - e.g.
+	 see "__node->~_Rb_tree_node<_Val>();" in stl_tree.h.  */
+      && next_token->type != CPP_COMPL
+      /* A template cannot name a constructor.  But don't complain if missing
+	 the template keyword in that case; continue on and an error for the
+	 (ill-formed) named constructor will get thrown later.  */
+      && (!parser->scope
+	  || (parser->scope
+	      && next_token->type == CPP_NAME
+	      && MAYBE_CLASS_TYPE_P (parser->scope)
+	      && !constructor_name_p (cp_expr (next_token->u.value,
+					       next_token->location),
+				      parser->scope))))
+    {
+      error_at (next_token->location,
+		"expected \"template\" keyword before dependent template "
+		"name");
+
+      /* Skip to the end of the statement to avoid misleading errors.  */
+      cp_parser_skip_to_end_of_statement (parser);
+      return error_mark_node;
+    }
+
   /* If there is a nested-name-specifier, then we are looking at
      the first qualified-id production.  */
   if (nested_name_specifier_p)
@@ -6117,7 +6268,8 @@ cp_parser_id_expression (cp_parser *parser,
       unqualified_id = cp_parser_unqualified_id (parser, *template_p,
 						 check_dependency_p,
 						 declarator_p,
-						 /*optional_p=*/false);
+						 /*optional_p=*/false,
+						 looks_like_template);
       /* Restore the SAVED_SCOPE for our caller.  */
       parser->scope = saved_scope;
       parser->object_scope = saved_object_scope;
@@ -6129,33 +6281,23 @@ cp_parser_id_expression (cp_parser *parser,
      of the other qualified-id productions.  */
   else if (global_scope_p)
     {
-      cp_token *token;
-      tree id;
-
-      /* Peek at the next token.  */
-      token = cp_lexer_peek_token (parser->lexer);
-
-      /* If it's an identifier, and the next token is not a "<", then
-	 we can avoid the template-id case.  This is an optimization
-	 for this common case.  */
-      if (token->type == CPP_NAME
-	  && !cp_parser_nth_token_starts_template_argument_list_p
-	       (parser, 2))
-	return cp_parser_identifier (parser);
-
-      cp_parser_parse_tentatively (parser);
-      /* Try a template-id.  */
-      id = cp_parser_template_id_expr (parser,
-				       /*template_keyword_p=*/false,
-				       /*check_dependency_p=*/true,
-				       declarator_p);
-      /* If that worked, we're done.  */
-      if (cp_parser_parse_definitely (parser))
-	return id;
+      if (looks_like_template == 1)
+	{
+	  cp_parser_parse_tentatively (parser);
+	  /* Try a template-id.  */
+	  tree id = cp_parser_template_id_expr (parser,
+						/*template_keyword_p=*/false,
+						/*check_dependency_p=*/true,
+						declarator_p,
+						looks_like_template);
+	  /* If that worked, we're done.  */
+	  if (cp_parser_parse_definitely (parser))
+	    return id;
+	}
 
       /* Peek at the next token.  (Changes in the token buffer may
 	 have invalidated the pointer obtained above.)  */
-      token = cp_lexer_peek_token (parser->lexer);
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
 
       switch (token->type)
 	{
@@ -6176,7 +6318,8 @@ cp_parser_id_expression (cp_parser *parser,
     return cp_parser_unqualified_id (parser, template_keyword_p,
 				     /*check_dependency_p=*/true,
 				     declarator_p,
-				     optional_p);
+				     optional_p,
+				     looks_like_template);
 }
 
 /* Parse an unqualified-id.
@@ -6191,6 +6334,10 @@ cp_parser_id_expression (cp_parser *parser,
    If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template'
    keyword, in a construct like `A::template ...'.
 
+   If LOOKS_LIKE_TEMPLATE is 1, we checked and saw that this looks
+   like a template-id.  If it is 2, we checked and saw that it did
+   not look like a template-id.  If 0, we have not checked.
+
    Returns a representation of unqualified-id.  For the `identifier'
    production, an IDENTIFIER_NODE is returned.  For the `~ class-name'
    production a BIT_NOT_EXPR is returned; the operand of the
@@ -6206,7 +6353,8 @@ cp_parser_unqualified_id (cp_parser* parser,
 			  bool template_keyword_p,
 			  bool check_dependency_p,
 			  bool declarator_p,
-			  bool optional_p)
+			  bool optional_p,
+			  int looks_like_template)
 {
   cp_token *token;
 
@@ -6219,16 +6367,21 @@ cp_parser_unqualified_id (cp_parser* parser,
       {
 	tree id;
 
-	/* We don't know yet whether or not this will be a
-	   template-id.  */
-	cp_parser_parse_tentatively (parser);
-	/* Try a template-id.  */
-	id = cp_parser_template_id_expr (parser, template_keyword_p,
-					 check_dependency_p,
-					 declarator_p);
-	/* If it worked, we're done.  */
-	if (cp_parser_parse_definitely (parser))
-	  return id;
+  if (looks_like_template != 2)
+    {
+      /* We don't know yet whether or not this will be a
+	 template-id.  */
+      cp_parser_parse_tentatively (parser);
+      /* Try a template-id.  */
+      id = cp_parser_template_id_expr (parser,
+				       template_keyword_p,
+				       check_dependency_p,
+				       declarator_p,
+				       looks_like_template);
+      /* If it worked, we're done.  */
+      if (cp_parser_parse_definitely (parser))
+	 return id;
+    }
 	/* Otherwise, it's an ordinary identifier.  */
 	return cp_parser_identifier (parser);
       }
@@ -6236,7 +6389,8 @@ cp_parser_unqualified_id (cp_parser* parser,
     case CPP_TEMPLATE_ID:
       return cp_parser_template_id_expr (parser, template_keyword_p,
 					 check_dependency_p,
-					 declarator_p);
+					 declarator_p,
+					 looks_like_template);
 
     case CPP_COMPL:
       {
@@ -6497,7 +6651,8 @@ cp_parser_unqualified_id (cp_parser* parser,
 	  /* Try a template-id.  */
 	  id = cp_parser_template_id_expr (parser, template_keyword_p,
 					   /*check_dependency_p=*/true,
-					   declarator_p);
+					   declarator_p,
+					   /*looks_like_template=*/0);
 	  /* If that worked, we're done.  */
 	  if (cp_parser_parse_definitely (parser))
 	    return id;
@@ -8105,7 +8260,8 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 	       /*check_dependency_p=*/true,
 	       &template_p,
 	       /*declarator_p=*/false,
-	       /*optional_p=*/false));
+	       /*optional_p=*/false,
+	       &dependent_p));
       /* In general, build a SCOPE_REF if the member name is qualified.
 	 However, if the name was not dependent and has already been
 	 resolved; there is no need to build the SCOPE_REF.  For example;
@@ -8421,7 +8577,8 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
 			     /*template_keyword_p=*/true,
 			     /*check_dependency_p=*/false,
 			     class_type,
-			     /*is_declaration=*/true);
+			     /*is_declaration=*/true,
+			     /*looks_like_template=*/0);
       /* Look for the `::' token.  */
       cp_parser_require (parser, CPP_SCOPE, RT_SCOPE);
     }
@@ -15827,7 +15984,8 @@ cp_parser_decltype_expr (cp_parser *parser,
                                   /*check_dependency_p=*/true,
                                   /*template_p=*/NULL,
                                   /*declarator_p=*/false,
-                                  /*optional_p=*/false);
+				  /*optional_p=*/false,
+				  /*dependent_expression_p=*/NULL);
 
   if (!cp_parser_error_occurred (parser) && expr != error_mark_node)
     {
@@ -17321,7 +17479,8 @@ cp_parser_default_template_template_argument (cp_parser *parser)
                                /*check_dependency_p=*/true,
                                /*template_p=*/&is_template,
                                /*declarator_p=*/false,
-                               /*optional_p=*/false);
+			       /*optional_p=*/false,
+			       /*dependent_expression_p=*/NULL);
   if (TREE_CODE (default_argument) == TYPE_DECL)
     /* If the id-expression was a template-id that refers to
        a template-class, we already have the declaration here,
@@ -17651,6 +17810,11 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack)
    of functions, returns a TEMPLATE_ID_EXPR.  If the template-name
    names a class, returns a TYPE_DECL for the specialization.
 
+   If LOOKS_LIKE_TEMPLATE is 1, then we have already discovered
+   that this looks like a template-id.  If 2, we have checked and seen
+   that this does not look like a template-id.  If it is 0, then we
+   did not check.
+
    If CHECK_DEPENDENCY_P is FALSE, names are looked up in
    uninstantiated templates.  */
 
@@ -17659,7 +17823,8 @@ cp_parser_template_id (cp_parser *parser,
 		       bool template_keyword_p,
 		       bool check_dependency_p,
 		       enum tag_types tag_type,
-		       bool is_declaration)
+		       bool is_declaration,
+		       int looks_like_template)
 {
   tree templ;
   tree arguments;
@@ -17678,15 +17843,20 @@ cp_parser_template_id (cp_parser *parser,
       return saved_checks_value (token->u.tree_check_value);
     }
 
-  /* Avoid performing name lookup if there is no possibility of
-     finding a template-id.  */
-  if ((token->type != CPP_NAME && token->keyword != RID_OPERATOR)
-      || (token->type == CPP_NAME
-	  && !cp_parser_nth_token_starts_template_argument_list_p
-	       (parser, 2)))
+  /* Only if we have not already checked whether this looks like a
+     template.  */
+  if (looks_like_template == 0)
     {
-      cp_parser_error (parser, "expected template-id");
-      return error_mark_node;
+      /* Avoid performing name lookup if there is no possibility of
+	 finding a template-id.  */
+      if ((token->type != CPP_NAME && token->keyword != RID_OPERATOR)
+	  || (token->type == CPP_NAME
+	      && !cp_parser_nth_token_starts_template_argument_list_p
+		  (parser, 2)))
+	{
+	  cp_parser_error (parser, "expected template-id");
+	  return error_mark_node;
+	}
     }
 
   /* Remember where the template-id starts.  */
@@ -17910,10 +18080,12 @@ static tree
 cp_parser_template_id_expr (cp_parser *parser,
 			    bool template_keyword_p,
 			    bool check_dependency_p,
-			    bool is_declaration)
+			    bool is_declaration,
+			    int looks_like_template)
 {
   tree x = cp_parser_template_id (parser, template_keyword_p, check_dependency_p,
-				  none_type, is_declaration);
+				  none_type, is_declaration,
+				  looks_like_template);
   if (TREE_CODE (x) == TEMPLATE_ID_EXPR
       && concept_check_p (x))
     /* We didn't check the arguments in cp_parser_template_id; do that now.  */
@@ -18332,7 +18504,8 @@ cp_parser_template_argument (cp_parser* parser)
 				      /*check_dependency_p=*/true,
 				      &template_p,
 				      /*declarator_p=*/false,
-				      /*optional_p=*/false);
+				      /*optional_p=*/false,
+				      /*dependent_expression_p=*/NULL);
   /* If the next token isn't a `,' or a `>', then this argument wasn't
      really finished.  */
   if (!cp_parser_next_token_ends_template_argument_p (parser))
@@ -19255,7 +19428,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 					/*template_keyword_p=*/true,
 					/*check_dependency_p=*/true,
 					none_type,
-					/*is_declaration=*/false);
+					/*is_declaration=*/false,
+					/*looks_like_template=*/0);
 	  /* If the template-id did not name a type, we are out of
 	     luck.  */
 	  if (TREE_CODE (type) != TYPE_DECL)
@@ -19285,7 +19459,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 					/*template_keyword_p=*/true,
 					/*check_dependency_p=*/true,
 					none_type,
-					/*is_declaration=*/false);
+					/*is_declaration=*/false,
+					/*looks_like_template=*/0);
 	  /* This is handled below, so back off.  */
 	  if (type && concept_check_p (type))
 	    cp_parser_simulate_error (parser);
@@ -19322,7 +19497,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 					/*template_keyword_p=*/false,
 					/*check_dependency_p=*/true,
 					none_type,
-					/*is_declaration=*/false);
+					/*is_declaration=*/false,
+					/*looks_like_template=*/0);
 	  if (type && concept_check_p (type))
 	    {
 	      location_t loc = EXPR_LOCATION (type);
@@ -19623,7 +19799,8 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
 					 /*template_keyword_p=*/false,
 					 /*check_dependency_p=*/true,
 					 none_type,
-					 /*is_declaration=*/false);
+					 /*is_declaration=*/false,
+					 /*looks_like_template=*/0);
       /* Note that this must be an instantiation of an alias template
 	 because [temp.names]/6 says:
 
@@ -19865,7 +20042,8 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       decl = cp_parser_template_id (parser, template_p,
 				    /*check_dependency_p=*/true,
 				    tag_type,
-				    is_declaration);
+				    is_declaration,
+				    /*looks_like_template=*/0);
       /* If we didn't find a template-id, look for an ordinary
 	 identifier.  */
       if (!template_p && !cp_parser_parse_definitely (parser))
@@ -21034,7 +21212,8 @@ cp_parser_using_declaration (cp_parser* parser,
 					 /*template_keyword_p=*/false,
 					 /*check_dependency_p=*/true,
 					 /*declarator_p=*/true,
-					 /*optional_p=*/false);
+					 /*optional_p=*/false,
+					 /*looks_like_template=*/0);
 
   if (access_declaration_p)
     {
@@ -23533,7 +23712,8 @@ cp_parser_declarator_id (cp_parser* parser, bool optional_p)
 				/*check_dependency_p=*/false,
 				/*template_p=*/NULL,
 				/*declarator_p=*/true,
-				optional_p);
+				optional_p,
+				/*dependent_expression_p=*/NULL);
   if (id && BASELINK_P (id))
     id = BASELINK_FUNCTIONS (id);
   return id;
@@ -25090,7 +25270,8 @@ cp_parser_class_name (cp_parser *parser,
       decl = cp_parser_template_id (parser, template_keyword_p,
 				    check_dependency_p,
 				    tag_type,
-				    is_declaration);
+				    is_declaration,
+				    /*looks_like_template=*/0);
       if (decl == error_mark_node)
 	return error_mark_node;
     }
@@ -25746,7 +25927,8 @@ cp_parser_class_head (cp_parser* parser,
 				  /*template_keyword_p=*/false,
 				  /*check_dependency_p=*/true,
 				  class_key,
-				  /*is_declaration=*/true);
+				  /*is_declaration=*/true,
+				  /*looks_like_template=*/0);
       /* If that didn't work, it could still be an identifier.  */
       if (!cp_parser_parse_definitely (parser))
 	{
@@ -29532,7 +29714,8 @@ cp_parser_type_requirement (cp_parser *parser)
                                     /*template_keyword_p=*/true,
                                     /*check_dependency=*/false,
                                     /*tag_type=*/none_type,
-                                    /*is_declaration=*/false);
+				    /*is_declaration=*/false,
+				    /*looks_like_template=*/0);
       type = make_typename_type (parser->scope, type, typename_type,
                                  /*complain=*/tf_error);
     }
@@ -30253,7 +30436,8 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
 					  /*template_keyword_p=*/false,
 					  /*check_dependency_p=*/false,
 					  /*declarator_p=*/true,
-					  /*optional_p=*/false);
+					  /*optional_p=*/false,
+					  /*looks_like_template=*/0);
       if (is_overloaded_fn (id))
 	id = DECL_NAME (get_first_fn (id));
       if (!constructor_name_p (id, nested_name_specifier))
@@ -35845,7 +36029,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 					  /*check_dependency_p=*/true,
 					  /*template_p=*/NULL,
 					  /*declarator_p=*/false,
-					  /*optional_p=*/false);
+					  /*optional_p=*/false,
+					  /*dependent_expression_p=*/NULL);
 	  if (name == error_mark_node)
 	    {
 	      if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
@@ -37345,7 +37530,8 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
 				    /*check_dependency_p=*/true,
 				    /*template_p=*/NULL,
 				    /*declarator_p=*/false,
-				    /*optional_p=*/false);
+				    /*optional_p=*/false,
+				    /*dependent_expression_p=*/NULL);
       parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
       if (identifier_p (id))
 	{
@@ -37870,7 +38056,8 @@ cp_parser_omp_clause_linear (cp_parser *parser, tree list,
 					  /*check_dependency_p=*/true,
 					  /*template_p=*/NULL,
 					  /*declarator_p=*/false,
-					  /*optional_p=*/false);
+					  /*optional_p=*/false,
+					  /*dependent_expression_p=*/NULL);
 	  if (step != error_mark_node)
 	    step = cp_parser_lookup_name_simple (parser, step, token->location);
 	  if (step == error_mark_node)
@@ -38066,7 +38253,8 @@ cp_parser_omp_clause_detach (cp_parser *parser, tree list)
 					  /*check_dependency_p=*/true,
 					  /*template_p=*/NULL,
 					  /*declarator_p=*/false,
-					  /*optional_p=*/false);
+					  /*optional_p=*/false,
+					  /*dependent_expression_p=*/NULL);
   if (name == error_mark_node)
     decl = error_mark_node;
   else
@@ -40509,11 +40697,14 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
 	  cp_parser_abort_tentative_parse (parser);
 	  cp_parser_parse_tentatively (parser);
 	  cp_token *token = cp_lexer_peek_token (parser->lexer);
-	  tree name = cp_parser_id_expression (parser, /*template_p=*/false,
-					       /*check_dependency_p=*/true,
-					       /*template_p=*/NULL,
-					       /*declarator_p=*/false,
-					       /*optional_p=*/false);
+	  tree name
+	    = cp_parser_id_expression (parser,
+				       /*template_p=*/false,
+				       /*check_dependency_p=*/true,
+				       /*template_p=*/NULL,
+				       /*declarator_p=*/false,
+				       /*optional_p=*/false,
+				       /*dependent_expression_p=*/NULL);
 	  if (name != error_mark_node
 	      && last_tok == cp_lexer_peek_token (parser->lexer))
 	    {
@@ -43831,7 +44022,8 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
 			       /*check_dependency_p=*/true,
 			       /*template_p=*/&template_p,
 			       /*declarator_p=*/false,
-			       /*optional_p=*/false);
+			       /*optional_p=*/false,
+			       /*dependent_expression_p=*/NULL);
   parens.require_close (parser);
 
   tree variant;
@@ -44252,11 +44444,14 @@ cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser)
 	  cp_parser_parse_tentatively (parser);
 	  /* Don't create location wrapper nodes here.  */
 	  auto_suppress_location_wrappers sentinel;
-	  tree fn_name = cp_parser_id_expression (parser, /*template_p=*/false,
-						  /*check_dependency_p=*/true,
-						  /*template_p=*/NULL,
-						  /*declarator_p=*/false,
-						  /*optional_p=*/false);
+	  tree fn_name
+	    = cp_parser_id_expression (parser,
+				       /*template_p=*/false,
+				       /*check_dependency_p=*/true,
+				       /*template_p=*/NULL,
+				       /*declarator_p=*/false,
+				       /*optional_p=*/false,
+				       /*dependent_expression_p=*/NULL);
 	  vec<tree, va_gc> *args;
 	  if (fn_name == error_mark_node
 	      || cp_parser_error_occurred (parser)
@@ -44916,7 +45111,8 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 					   /*check_dependency_p=*/false,
 					   /*template_p=*/NULL,
 					   /*declarator_p=*/false,
-					   /*optional_p=*/false);
+					   /*optional_p=*/false,
+					   /*dependent_expression_p=*/NULL);
       tree decl = (identifier_p (name)
 		   ? cp_parser_lookup_name_simple (parser, name, name_loc)
 		   : name);
diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
index 6c0fbdd1c4a..aa8a7725bc4 100644
--- a/gcc/symbol-summary.h
+++ b/gcc/symbol-summary.h
@@ -191,7 +191,7 @@ public:
   template<typename Arg, bool (*f)(const T &, Arg)>
   void traverse (Arg a) const
   {
-    m_map.traverse <f> (a);
+    m_map.template traverse <f> (a);
   }
 
   /* Getter for summary callgraph node pointer.  If a summary for a node
@@ -690,7 +690,7 @@ public:
   template<typename Arg, bool (*f)(const T &, Arg)>
   void traverse (Arg a) const
   {
-    m_map.traverse <f> (a);
+    m_map.template traverse <f> (a);
   }
 
   /* Getter for summary callgraph edge pointer.
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype34.C b/gcc/testsuite/g++.dg/cpp0x/decltype34.C
index 028e50669f9..b5e7a5cba0c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/decltype34.C
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype34.C
@@ -7,13 +7,13 @@ struct impl
 };
 
 template<class T, class U,
-	 class = decltype(impl::create<T>()->impl::create<U>())>
+	 class = decltype(impl::create<T>()->impl::template create<U>())>
 struct tester{};
 
 tester<impl*, int> ti;
 
 template<class T, class U,
-	 class = decltype(impl::create<T>()->impl::create<U>())>
+	 class = decltype(impl::create<T>()->impl::template create<U>())>
 int test() { return 0; }
 
 int i = test<impl*, int>();
diff --git a/gcc/testsuite/g++.dg/parse/template26.C b/gcc/testsuite/g++.dg/parse/template26.C
index aab9763ccaf..add8c64eabb 100644
--- a/gcc/testsuite/g++.dg/parse/template26.C
+++ b/gcc/testsuite/g++.dg/parse/template26.C
@@ -6,13 +6,13 @@ namespace impl
 }
 
 template <class T, class U, __SIZE_TYPE__
-	  = sizeof(impl::create<T>()->*impl::create<U>())>
+	  = sizeof(impl::create<T>()->*impl::template create<U>())>
 struct foo1;
 
 template <class T, class U, __SIZE_TYPE__
-	  = sizeof(impl::create<T>()->impl::create<U>())> // { dg-error "not a class member" }
+	  = sizeof(impl::create<T>()->impl::template create<U>())> // { dg-error "not a class member" }
 struct foo2;
 
 template <class T, class U, __SIZE_TYPE__
-	  = sizeof(impl::create<T>().impl::create<U>())> // { dg-error "not a class member" }
+	  = sizeof(impl::create<T>().impl::template create<U>())> // { dg-error "not a class member" }
 struct foo3;
diff --git a/gcc/testsuite/g++.dg/template/dependent-name15.C b/gcc/testsuite/g++.dg/template/dependent-name15.C
new file mode 100644
index 00000000000..e03971f9b2f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name15.C
@@ -0,0 +1,46 @@
+// C++ PR 70317
+// { dg-do compile }
+
+template<class T> class mytemplateclass
+{
+        public:
+        template<class U> void class_func() {}
+        template<class U> static void class_func_static() {}
+};
+
+class myclass
+{
+        public:
+        int testint;
+        template<class U> void class_func() {}
+        template<class U> static void class_func_static() {}
+};
+
+template<class Y> void tests_func(mytemplateclass<Y> c, myclass c2) 
+{
+        /* Dependent template accessors (ill-formed code).  */
+        c.class_func<Y>(); // { dg-error "keyword before dependent template name" }
+        (&c)->class_func<Y>(); // { dg-error "keyword before dependent template name" }
+        mytemplateclass<Y>::class_func_static<Y>(); // { dg-error "keyword before dependent template name" }
+
+        /* Dependent template accessors (well-formed code).  */
+        c.template class_func<Y>();
+        (&c)->template class_func<Y>();
+        mytemplateclass<Y>::template class_func_static<Y>();
+
+        /* Non-dependent template accessors (well-formed code).  */
+        c2.class_func<myclass>();
+        (&c2)->class_func<myclass>();
+        myclass::class_func_static<myclass>();
+}
+
+int main()
+{
+        mytemplateclass<myclass> c;
+        myclass c2;
+        tests_func<myclass>(c, c2);
+
+	 c2.testint = 53;
+        /* Make sure this isn't treated as a template.  */
+        bool testbool = c2.testint < 999 > 7;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/explicit81.C b/gcc/testsuite/g++.old-deja/g++.pt/explicit81.C
index 576ba5439a6..5232d770720 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/explicit81.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/explicit81.C
@@ -26,10 +26,10 @@ void tf(C *ptr)
 {
   N::nf<N::e0>();
   gf<N::e0>();
-  ptr->X::xfn <N::e0> ();
+  ptr->X::template xfn <N::e0> ();
   ptr->C::template xfn <N::e0> ();
   ptr->template xfn <N::e0> ();
-  ptr->X::sfn <N::e0> ();
+  ptr->X::template sfn <N::e0> ();
   ptr->C::template sfn <N::e0> ();
   ptr->template sfn <N::e0> ();
   X::sfn <N::e0> ();
-- 
2.32.0


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

end of thread, other threads:[~2022-01-17 22:27 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-20 16:56 [PATCH] c++: error message for dependent template members [PR70417] Anthony Sharp
2021-08-27 21:33 ` Jason Merrill
     [not found]   ` <CA+Lh_AnijQzyM6qgwHS7hZqmA8uO8VahG5fmdqFMF6wRcrbefQ@mail.gmail.com>
2021-09-17 22:17     ` Anthony Sharp
2021-09-17 22:22       ` Anthony Sharp
2021-09-22 20:04         ` Jason Merrill
2021-10-10 11:28           ` Anthony Sharp
2021-10-19 19:15             ` Jason Merrill
2021-12-04 17:23               ` Anthony Sharp
2021-12-09 15:51                 ` Jason Merrill
2022-01-13 21:01                   ` Jason Merrill
2022-01-15  8:27                     ` Anthony Sharp
2022-01-17 22:26                       ` 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).