On 10/10/21 07:28, Anthony Sharp wrote:
Hi Jason,

Hope you are well. Thanks for the feedback.

    I like adding the configurability, but I think let's keep committing as
    the default behavior.  And adding the parameter to the rollback function
    seems unnecessary.  For the behavior argument, let's use an enum to be
    clearer.

Sure, all done. The declaration of the enum could have gone in many places it seems but I put it in cp-tree.h.

I'd put it just above the definition of saved_token_sentinel in parser.c.

I've removed the awkward call to cp_parser_require and put it (along with the 
call to cp_parser_skip_to_end_of_template_parameter_list) in its own new 
function called cp_parser_ensure_reached_end_of_template_parameter_list.

Maybe cp_parser_require_end_of_template_parameter_list?  Either way is fine.

    I think we don't want to return early here, we want to go through the
    same <args>( check that we do for regular names.

I have changed it to do that, but could something like "operator- <" ever be intended as something other than the start of a template-id?

Hmm, good point; operators that are member functions must be non-static, so we couldn't be doing a comparison of the address of the function.

+// { dg-warning "expected \"template\" keyword before dependent template name" 
{ target c++20 } .-1 }
+// ^ bogus warning caused by the above being treated as an expression, not a 
declaration >
    Hmm, that's a problem.  Can you avoid it by checking declarator_p?

We actually already check declarator_p in cp_parser_id_expression in that case. The reason it throws a warning is because typename14.C is intentionally malformed; in the eyes of the compiler it's written like an expression because it's missing the return type (although, even adding a return type would not make it valid). I'm not sure there's any worthwhile way around this really.

But it isn't written like an expression: the error comes when trying to diagnose it as an invalid type in a declaration context.

So declarator_p should be true there.  I'll fix that.

Also, some more missing template keywords seemed to crop up in the regression tests, so I had to sprinkle some more template keywords in a few. I guess there was a hidden bug that was missing a few scenarios.

Just out of curiosity, how pedantic would you say I should be when submitting patches to GCC etc? I regression test and bootstrap all my changes, but I'm always worrying about what might happen if I somehow forgot to stage a file in git, or attached the wrong patch file, or interpreted the .sum file wrong etc. Do patches go through another round of testing after submitting?

Not automatically (yet), but often I test other people's patches before applying them.

Sometimes I wonder whether I should be applying the patch locally and then bootstrapping and regression testing it again, although that's hardly fun since that usually takes around 2-3 hours even with -j 12.

Maybe I ought to think about getting a dedicated Linux PC.

You could also apply for a GCC Compile Farm account:
https://gcc.gnu.org/wiki/CompileFarm

+  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 -1;
+    }
+
+  /* Could be a ~ referencing the destructor of a class template.  */
+  if (next_token->type == CPP_COMPL)
+    {
+      /* It could only be a template.  */
+      if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_NAME)
+       return 1;
+
+      return -1;
+    }

Why don't these check for the < ?

+  /* E.g. "::operator- <>". */
+  if (next_token->keyword == RID_OPERATOR)
+    {
+      /* Consume it so it doesn't get in our way.  */
+      cp_lexer_consume_token (parser->lexer);
+      next_token = cp_lexer_peek_token (parser->lexer);
+      found_operator_keyword = true;
+    }
...
+  if (!found_operator_keyword && next_token->type != CPP_NAME)
+    return -1;

These could be if/else if so you don't need the found_operator_keyword variable.

+  if (next_token->type == CPP_TEMPLATE_ID)
+    return 1;

This could move above the saved_token_sentinel; you won't have a CPP_TEMPLATE_ID after RID_OPERATOR.

+  /* If this is a function template then we would see a "(" after the
+     final ">".  It could also be a class template constructor.  */
+  if (next_token->type == CPP_OPEN_PAREN
+      /* If this is a class template then we could see a scope token after
+      the final ">".  */
+      || next_token->type == CPP_SCOPE
+      /* We could see a ";" after a variable template.  */
+      || next_token->type == CPP_SEMICOLON
+      /* We could see something like
+        friend vect<t> (::operator- <>)( const vect<t>&, const vect<t>& );
+        */
+      || next_token->type == CPP_CLOSE_PAREN)
+    return 1;

This seems too limited.  As I was saying before,

      I guess any operator-or-punctuator after the > would suggest a
    template-id.

For instance, the patch doesn't currently warn about

template <class T> struct A
{
  template <class U> static U m;
};

template <class T> int f (T t)
{
  return t.m<int> + 42;
}

This is especially a problem since we use the return value of -1 to skip calling cp_parser_template_id_expr.

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

As I was saying in earlier discussion, this is currently a false positive, because impl:: is a non-dependent scope. If parser->scope is set, we don't need to look at parser->context->object_type...

+      && (dependent_scope_p (parser->context->object_type)
+         /* :: access expressions.  */
+         || (dependent_scope_p (parser->scope)

...here.

+  int looks_like_template_id
+    = next_token_begins_template_id (parser);

We probably want this to just be 0, and not call the function, if !warn_missing_template_keyword.

+         if (template_p)
+           *template_p = true;

Since we unconditionally dereference template_p a few lines below, let's try removing this condition.

+      if (looks_like_template_id == 1)
+       {

Shouldn't this be != -1, as in cp_parser_unqualified_id?

-Foo<T>::operator Foo<U>() {
+Foo<T>::template operator Foo<U>() {

This is wrong; you don't use the template keyword like this in a declarator.

-    n.template Nested<B>::method();
+    n.template Nested<B>::method(); // { dg-error "is not a template" "" { 
target { *-*-* } } }

This is wrong; Nested is indeed a template.

-         = sizeof(impl::create<T>()->*impl::create<U>())>
+         = sizeof(impl::create<T>()->*impl::template create<U>())>

As above, this shouldn't be necessary currently.

-  ptr->X::xfn <N::e0> ();
+  ptr->X::template xfn <N::e0> ();

Or this.

Jason
From ecccf8103b2368564d073d4b85efd9484448a59f Mon Sep 17 00:00:00 2001
From: Jason Merrill <ja...@redhat.com>
Date: Mon, 18 Oct 2021 16:12:15 -0400
Subject: [PATCH] c++: tweak parsing of invalid types
To: gcc-patches@gcc.gnu.org

cp_parser_parse_and_diagnose_invalid_type_name is called during declaration
parsing, so it should pass 'true' for the declarator_p argument.  But that
caused a diagnostic regression on template/pr84789.C due to undesired lookup
in dependent scopes.  To fix that, cp_parser_nested_name_specifier_opt needs
to respect the value of check_dependency_p.

gcc/cp/ChangeLog:

	* parser.c (cp_parser_parse_and_diagnose_invalid_type_name):
	Pass true for declarator_p.
	(cp_parser_nested_name_specifier_opt): Only look through
	TYPENAME_TYPE if check_dependency_p is false.
---
 gcc/cp/parser.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 865778e4d30..f11917259f7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3693,7 +3693,7 @@ cp_parser_parse_and_diagnose_invalid_type_name (cp_parser *parser)
 				/*template_keyword_p=*/false,
 				/*check_dependency_p=*/true,
 				/*template_p=*/NULL,
-				/*declarator_p=*/false,
+				/*declarator_p=*/true,
 				/*optional_p=*/false);
   /* If the next token is a (, this is a function with no explicit return
      type, i.e. constructor, destructor or conversion op.  */
@@ -6605,6 +6605,8 @@ check_template_keyword_in_nested_name_spec (tree name)
    it unchanged if there is no nested-name-specifier.  Returns the new
    scope iff there is a nested-name-specifier, or NULL_TREE otherwise.
 
+   If CHECK_DEPENDENCY_P is FALSE, names are looked up in dependent scopes.
+
    If IS_DECLARATION is TRUE, the nested-name-specifier is known to be
    part of a declaration and/or decl-specifier.  */
 
@@ -6645,9 +6647,10 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
 	  /* Grab the nested-name-specifier and continue the loop.  */
 	  cp_parser_pre_parsed_nested_name_specifier (parser);
 	  /* If we originally encountered this nested-name-specifier
-	     with IS_DECLARATION set to false, we will not have
+	     with CHECK_DEPENDENCY_P set to true, we will not have
 	     resolved TYPENAME_TYPEs, so we must do so here.  */
 	  if (is_declaration
+	      && !check_dependency_p
 	      && TREE_CODE (parser->scope) == TYPENAME_TYPE)
 	    {
 	      new_scope = resolve_typename_type (parser->scope,
@@ -6729,6 +6732,7 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
 	 a template.  So, if we have a typename at this point, we make
 	 an effort to look through it.  */
       if (is_declaration
+	  && !check_dependency_p
 	  && !typename_keyword_p
 	  && parser->scope
 	  && TREE_CODE (parser->scope) == TYPENAME_TYPE)
-- 
2.27.0

Reply via email to