public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* C++ PATCH to implement C++20 P0634R3, Down with typename!
@ 2018-11-12 15:28 Marek Polacek
  2018-11-28 22:09 ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Marek Polacek @ 2018-11-12 15:28 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill

This patch implements C++20 P0634R3, Down with typename!
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>
which makes 'typename' optional in several contexts specified in [temp.res].

The gist of the patch is in cp_parser_simple_type_specifier, where, if the
context makes typename optional and the id is qualified, we pretend we've
seen the typename keyword.

There's quite a lot of churn because we need to be careful where we want
to make typename optional, and e.g. a flag in cp_parser would be too global.

I'm not sure about some of the bits in typename5.C, not quite sure if the
code is valid, but I didn't have time to investigate deeply and it seems
pretty obscure anyway.  There are preexisting cases when g++ and clang++
disagree.

The resolve_typename_type hunk was to make typename9.C work with -fconcepts.

Bootstrapped/regtested on x86_64-linux.

2018-11-12  Marek Polacek  <polacek@redhat.com>

	Implement P0634R3, Down with typename!
	* parser.c (CP_PARSER_FLAGS_TYPENAME_OPTIONAL): New enumerator.
	(cp_parser_type_name): Remove declaration.
	(cp_parser_postfix_expression): Pass TYPENAME_OPTIONAL_P to
	cp_parser_type_id.
	(cp_parser_new_type_id): Pass TYPENAME_OPTIONAL_P to
	cp_parser_type_specifier_seq.
	(cp_parser_lambda_declarator_opt): Pass TYPENAME_OPTIONAL_P
	to cp_parser_parameter_declaration_clause.
	(cp_parser_condition): Adjust call to cp_parser_declarator.
	(cp_parser_simple_declaration): Adjust call to
	cp_parser_init_declarator.
	(cp_parser_conversion_type_id): Adjust call to
	cp_parser_type_specifier_seq.
	(cp_parser_default_type_template_argument): Pass TYPENAME_OPTIONAL_P
	to cp_parser_type_id.
	(cp_parser_template_parameter): Pass TYPENAME_OPTIONAL_P to
	cp_parser_parameter_declaration.
	(cp_parser_explicit_instantiation): Adjust call to cp_parser_declarator.
	(cp_parser_simple_type_specifier): Adjust call to cp_parser_type_name.
	(cp_parser_type_name): Remove unused function.
	(cp_parser_enum_specifier): Adjust call to cp_parser_type_specifier_seq.
	(cp_parser_alias_declaration): Pass TYPENAME_OPTIONAL_P to
	cp_parser_type_id.
	(cp_parser_init_declarator): New parameter.
	(cp_parser_declarator): New parameter.  Use it.
	(cp_parser_direct_declarator): Likewise.
	(cp_parser_type_id_1): Likewise.
	(cp_parser_type_id): Likewise.
	(cp_parser_template_type_arg): Adjust call to cp_parser_type_id_1.
	(cp_parser_trailing_type_id): Pass TYPENAME_OPTIONAL_P to
	cp_parser_type_id_1.
	(cp_parser_type_specifier_seq): New parameter.  Set flags to
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL.
	(cp_parser_parameter_declaration_clause): New parameter.  Use it.
	(cp_parser_parameter_declaration_list): Likewise.
	(cp_parser_parameter_declaration): Likewise.
	(cp_parser_member_declaration): Set flags to
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL.
	(cp_parser_exception_declaration): Adjust calls to
	cp_parser_type_specifier_seq and cp_parser_declarator.
	(cp_parser_requirement_parameter_list): Adjust call to
	cp_parser_parameter_declaration_clause.
	(cp_parser_constructor_declarator_p): Resolve the TYPENAME_TYPE.
	(cp_parser_single_declaration): Set flags to
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL.  Pass TYPENAME_OPTIONAL_P to
	cp_parser_init_declarator.
	(cp_parser_cache_defarg): Adjust call to cp_parser_declarator.
	(cp_parser_objc_method_tail_params_opt): Adjust call to
	cp_parser_parameter_declaration.
	(cp_parser_objc_class_ivars): Adjust call to cp_parser_declarator.
	(cp_parser_objc_try_catch_finally_statement): Adjust call to
	cp_parser_parameter_declaration.
	(cp_parser_objc_struct_declaration): Adjust call to
	cp_parser_declarator.
	(cp_parser_omp_for_loop_init): Adjust calls to
	cp_parser_type_specifier_seq and cp_parser_declarator.

	* g++.dg/cpp0x/alias-decl-43.C: Adjust dg-error.
	* g++.dg/cpp0x/decltype67.C: Only expect error in c++17_down.
	* g++.dg/cpp1z/typename1.C: New test.
	* g++.dg/cpp2a/typename1.C: New test.
	* g++.dg/cpp2a/typename10.C: New test.
	* g++.dg/cpp2a/typename11.C: New test.
	* g++.dg/cpp2a/typename2.C: New test.
	* g++.dg/cpp2a/typename3.C: New test.
	* g++.dg/cpp2a/typename4.C: New test.
	* g++.dg/cpp2a/typename5.C: New test.
	* g++.dg/cpp2a/typename6.C: New test.
	* g++.dg/cpp2a/typename7.C: New test.
	* g++.dg/cpp2a/typename8.C: New test.
	* g++.dg/cpp2a/typename9.C: New test.
	* g++.dg/diagnostic/missing-typename.C: Only run the test in
	c++17_down.
	* g++.dg/other/crash-9.C: Add template disambiguator.
	* g++.dg/other/nontype-1.C: Only expect error in c++17_down.
	* g++.dg/parse/crash13.C: Likewise.
	* g++.dg/parse/error36.C: Likewise.
	* g++.dg/parse/typedef2.C: Likewise.
	* g++.dg/parse/typename11.C: Likewise.
	* g++.dg/template/crash48.C: Adjust dg-error.
	* g++.dg/template/dependent-name5.C: Only expect error in c++17_down.
	Add dg-error.
	* g++.dg/template/error29.C: Only expect error in c++17_down.
	* g++.dg/template/nested5.C: Add template disambiguator.
	* g++.dg/template/pr84789.C: Only expect error in c++17_down.
	* g++.dg/template/static30.C: Add dg-error.
	* g++.dg/template/typedef6.C: Adjust dg-error.
	* g++.dg/template/typename3.C: Only expect error in c++17_down.

diff --git gcc/cp/parser.c gcc/cp/parser.c
index deaca5cc974..4cc16b3e825 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -1790,7 +1790,9 @@ enum
      constexpr.  */
   CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8,
   /* When parsing a decl-specifier-seq, only allow mutable or constexpr.  */
-  CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10
+  CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
+  /* When parsing a decl-specifier-seq, allow missing typename.  */
+  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20
 };
 
 /* This type is used for parameters and variables which hold
@@ -2153,8 +2155,6 @@ static tree cp_parser_simple_type_specifier
   (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags);
 static tree cp_parser_type_name
   (cp_parser *, bool);
-static tree cp_parser_type_name
-  (cp_parser *);
 static tree cp_parser_nonclass_name 
   (cp_parser* parser);
 static tree cp_parser_elaborated_type_specifier
@@ -2196,11 +2196,11 @@ static tree cp_parser_decomposition_declaration
 
 static tree cp_parser_init_declarator
   (cp_parser *, cp_decl_specifier_seq *, vec<deferred_access_check, va_gc> *,
-   bool, bool, int, bool *, tree *, location_t *, tree *);
+   bool, bool, int, bool *, tree *, location_t *, tree *, bool);
 static cp_declarator *cp_parser_declarator
-  (cp_parser *, cp_parser_declarator_kind, int *, bool *, bool, bool);
+  (cp_parser *, cp_parser_declarator_kind, int *, bool *, bool, bool, bool);
 static cp_declarator *cp_parser_direct_declarator
-  (cp_parser *, cp_parser_declarator_kind, int *, bool, bool);
+  (cp_parser *, cp_parser_declarator_kind, int *, bool, bool, bool);
 static enum tree_code cp_parser_ptr_operator
   (cp_parser *, tree *, cp_cv_quals *, tree *);
 static cp_cv_quals cp_parser_cv_qualifier_seq_opt
@@ -2216,20 +2216,20 @@ static tree cp_parser_late_return_type_opt
 static tree cp_parser_declarator_id
   (cp_parser *, bool);
 static tree cp_parser_type_id
-  (cp_parser *, location_t * = NULL);
+  (cp_parser *, location_t * = NULL, bool = false);
 static tree cp_parser_template_type_arg
   (cp_parser *);
 static tree cp_parser_trailing_type_id (cp_parser *);
 static tree cp_parser_type_id_1
-  (cp_parser *, bool, bool, location_t *);
+  (cp_parser *, bool, bool, bool, location_t *);
 static void cp_parser_type_specifier_seq
-  (cp_parser *, bool, bool, cp_decl_specifier_seq *);
+  (cp_parser *, bool, bool, bool, cp_decl_specifier_seq *);
 static tree cp_parser_parameter_declaration_clause
-  (cp_parser *);
+  (cp_parser *, bool);
 static tree cp_parser_parameter_declaration_list
-  (cp_parser *);
+  (cp_parser *, bool);
 static cp_parameter_declarator *cp_parser_parameter_declaration
-  (cp_parser *, bool, bool *);
+  (cp_parser *, bool, bool *, bool);
 static tree cp_parser_default_argument 
   (cp_parser *, bool);
 static void cp_parser_function_body
@@ -6775,6 +6775,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	cp_expr expression;
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
+	const bool typename_optional_p = (cxx_dialect >= cxx2a);
 
 	/* All of these can be handled in the same way from the point
 	   of view of parsing.  Begin by consuming the token
@@ -6791,7 +6792,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* Parse the type to which we are casting.  */
 	saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
 	parser->in_type_id_in_expr_p = true;
-	type = cp_parser_type_id (parser);
+	type = cp_parser_type_id (parser, NULL, typename_optional_p);
 	parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	/* Look for the closing `>'.  */
 	cp_parser_require (parser, CPP_GREATER, RT_GREATER);
@@ -8585,6 +8586,7 @@ cp_parser_new_type_id (cp_parser* parser, tree *nelts)
   cp_declarator *declarator;
   cp_declarator *outer_declarator;
   const char *saved_message;
+  const bool typename_optional_p = (cxx_dialect >= cxx2a);
 
   /* The type-specifier sequence must not contain type definitions.
      (It cannot contain declarations of new types either, but if they
@@ -8596,6 +8598,7 @@ cp_parser_new_type_id (cp_parser* parser, tree *nelts)
   /* Parse the type-specifier-seq.  */
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
 				/*is_trailing_return=*/false,
+				typename_optional_p,
 				&type_specifier_seq);
   /* Restore the old message.  */
   parser->type_definition_forbidden_message = saved_message;
@@ -10586,13 +10589,15 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
      opening parenthesis if present.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
+      const bool typename_optional_p = (cxx_dialect >= cxx2a);
       matching_parens parens;
       parens.consume_open (parser);
 
       begin_scope (sk_function_parms, /*entity=*/NULL_TREE);
 
       /* Parse parameters.  */
-      param_list = cp_parser_parameter_declaration_clause (parser);
+      param_list
+	= cp_parser_parameter_declaration_clause (parser, typename_optional_p);
 
       /* Default arguments shall not be specified in the
 	 parameter-declaration-clause of a lambda-declarator.  */
@@ -11752,7 +11757,8 @@ cp_parser_condition (cp_parser* parser)
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
-					 /*friend_p=*/false);
+					 /*friend_p=*/false,
+					 /*typename_optional_p=*/false);
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
       /* Parse the asm-specification.  */
@@ -13231,7 +13237,8 @@ cp_parser_simple_declaration (cp_parser* parser,
 					&function_definition_p,
 					maybe_range_for_decl,
 					&init_loc,
-					&auto_result);
+					&auto_result,
+					/*typename_optional_p=*/false);
       /* If an error occurred while parsing tentatively, exit quickly.
 	 (That usually happens when in the body of a function; each
 	 statement is treated as a declaration-statement until proven
@@ -14488,6 +14495,7 @@ cp_parser_conversion_type_id (cp_parser* parser)
   /* Parse the type-specifiers.  */
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
 				/*is_trailing_return=*/false,
+				/*typename_optional_p=*/false,
 				&type_specifiers);
 
   parser->type_definition_forbidden_message = saved_message;
@@ -15563,8 +15571,10 @@ cp_parser_default_type_template_argument (cp_parser *parser)
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
   /* Parse the default-argument.  */
+  const bool typename_optional_p = (cxx_dialect >= cxx2a);
   push_deferring_access_checks (dk_no_deferred);
-  tree default_argument = cp_parser_type_id (parser);
+  tree default_argument = cp_parser_type_id (parser, NULL,
+					     typename_optional_p);
   pop_deferring_access_checks ();
 
   if (flag_concepts && type_uses_auto (default_argument))
@@ -15690,9 +15700,11 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
      template-parameter, the first non-nested `>' is taken as the end
      of the template parameter-list rather than a greater-than
      operator.  */
+  const bool typename_optional_p = (cxx_dialect >= cxx2a);
   parameter_declarator
      = cp_parser_parameter_declaration (parser, /*template_parm_p=*/true,
-					/*parenthesized_p=*/NULL);
+					/*parenthesized_p=*/NULL,
+					typename_optional_p);
 
   if (!parameter_declarator)
     return error_mark_node;
@@ -16841,7 +16853,8 @@ cp_parser_explicit_instantiation (cp_parser* parser)
 				/*ctor_dtor_or_conv_p=*/NULL,
 				/*parenthesized_p=*/NULL,
 				/*member_p=*/false,
-				/*friend_p=*/false);
+				/*friend_p=*/false,
+				/*typename_optional_p=*/false);
       if (declares_class_or_enum & 2)
 	cp_parser_check_for_definition_in_return_type (declarator,
 						       decl_specifiers.type,
@@ -17480,7 +17493,10 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 	}
       /* Otherwise, look for a type-name.  */
       else
-	type = cp_parser_type_name (parser);
+	{
+	  bool typename_p = (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL);
+	  type = cp_parser_type_name (parser, (qualified_p && typename_p));
+	}
       /* Keep track of all name-lookups performed in class scopes.  */
       if (type
 	  && !global_p
@@ -17605,13 +17621,6 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 
    Returns a TYPE_DECL for the type.  */
 
-static tree
-cp_parser_type_name (cp_parser* parser)
-{
-  return cp_parser_type_name (parser, /*typename_keyword_p=*/false);
-}
-
-/* See above. */
 static tree
 cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
 {
@@ -18393,6 +18402,7 @@ cp_parser_enum_specifier (cp_parser* parser)
       /* Parse the type-specifier-seq.  */
       cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
 				    /*is_trailing_return=*/false,
+				    /*typename_optional_p=*/false,
                                     &type_specifiers);
 
       /* At this point this is surely not elaborated type specifier.  */
@@ -19180,6 +19190,7 @@ cp_parser_alias_declaration (cp_parser* parser)
   cp_decl_specifier_seq decl_specs;
   bool member_p;
   const char *saved_message = NULL;
+  const bool typename_optional_p = (cxx_dialect >= cxx2a);
 
   /* Look for the `using' keyword.  */
   cp_token *using_token
@@ -19227,7 +19238,7 @@ cp_parser_alias_declaration (cp_parser* parser)
 	G_("types may not be defined in alias template declarations");
     }
 
-  type = cp_parser_type_id (parser, &type_location);
+  type = cp_parser_type_id (parser, &type_location, typename_optional_p);
 
   /* Restore the error message if need be.  */
   if (parser->num_template_parameter_lists)
@@ -19635,7 +19646,8 @@ strip_declarator_types (tree type, cp_declarator *declarator)
    If INIT_LOC is not NULL, and *INIT_LOC is equal to UNKNOWN_LOCATION,
    and there is an initializer, the pointed location_t is set to the
    location of the '=' or `(', or '{' in C++11 token introducing the
-   initializer.  */
+   initializer.  TYPENAME_OPTIONAL_P is true if the typename keyword is optional
+   in this context.  */
 
 static tree
 cp_parser_init_declarator (cp_parser* parser,
@@ -19647,7 +19659,8 @@ cp_parser_init_declarator (cp_parser* parser,
 			   bool* function_definition_p,
 			   tree* maybe_range_for_decl,
 			   location_t* init_loc,
-			   tree* auto_result)
+			   tree* auto_result,
+			   bool typename_optional_p)
 {
   cp_token *token = NULL, *asm_spec_start_token = NULL,
            *attributes_start_token = NULL;
@@ -19697,7 +19710,7 @@ cp_parser_init_declarator (cp_parser* parser,
     = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
 			    &ctor_dtor_or_conv_p,
 			    /*parenthesized_p=*/NULL,
-			    member_p, friend_p);
+			    member_p, friend_p, typename_optional_p);
   /* Gather up the deferred checks.  */
   stop_deferring_access_checks ();
 
@@ -20113,14 +20126,17 @@ cp_parser_init_declarator (cp_parser* parser,
 
    MEMBER_P is true iff this declarator is a member-declarator.
 
-   FRIEND_P is true iff this declarator is a friend.  */
+   FRIEND_P is true iff this declarator is a friend.
+
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in this
+   context.  */
 
 static cp_declarator *
 cp_parser_declarator (cp_parser* parser,
 		      cp_parser_declarator_kind dcl_kind,
 		      int* ctor_dtor_or_conv_p,
 		      bool* parenthesized_p,
-		      bool member_p, bool friend_p)
+		      bool member_p, bool friend_p, bool typename_optional_p)
 {
   cp_declarator *declarator;
   enum tree_code code;
@@ -20161,7 +20177,8 @@ cp_parser_declarator (cp_parser* parser,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
-					 friend_p);
+					 friend_p,
+					 /*typename_optional_p=*/false);
 
       /* If we are parsing an abstract-declarator, we must handle the
 	 case where the dependent declarator is absent.  */
@@ -20180,7 +20197,8 @@ cp_parser_declarator (cp_parser* parser,
 						   CPP_OPEN_PAREN);
       declarator = cp_parser_direct_declarator (parser, dcl_kind,
 						ctor_dtor_or_conv_p,
-						member_p, friend_p);
+						member_p, friend_p,
+						typename_optional_p);
     }
 
   if (gnu_attributes && declarator && declarator != cp_error_declarator)
@@ -20215,13 +20233,15 @@ cp_parser_declarator (cp_parser* parser,
    CP_PARSER_DECLARATOR_EITHER, if we can accept either - in the case
    of ambiguity we prefer an abstract declarator, as per
    [dcl.ambig.res].  CTOR_DTOR_OR_CONV_P, MEMBER_P, and FRIEND_P are
-   as for cp_parser_declarator.  */
+   as for cp_parser_declarator.  TYPENAME_OPTIONAL_P is true if the
+   typename keyword is optional in this context.  */
 
 static cp_declarator *
 cp_parser_direct_declarator (cp_parser* parser,
 			     cp_parser_declarator_kind dcl_kind,
 			     int* ctor_dtor_or_conv_p,
-			     bool member_p, bool friend_p)
+			     bool member_p, bool friend_p,
+			     bool typename_optional_p)
 {
   cp_token *token;
   cp_declarator *declarator = NULL;
@@ -20306,7 +20326,9 @@ cp_parser_direct_declarator (cp_parser* parser,
 	      begin_scope (sk_function_parms, NULL_TREE);
 
 	      /* Parse the parameter-declaration-clause.  */
-	      params = cp_parser_parameter_declaration_clause (parser);
+	      params
+		= cp_parser_parameter_declaration_clause (parser,
+							  typename_optional_p);
 
 	      /* Consume the `)'.  */
 	      parens.require_close (parser);
@@ -20399,7 +20421,8 @@ cp_parser_direct_declarator (cp_parser* parser,
 	      declarator
 		= cp_parser_declarator (parser, dcl_kind, ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
-					member_p, friend_p);
+					member_p, friend_p,
+					typename_optional_p);
 	      parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	      first = false;
 	      /* Expect a `)'.  */
@@ -21248,11 +21271,20 @@ cp_parser_declarator_id (cp_parser* parser, bool optional_p)
    type-id:
      type-specifier-seq abstract-declarator [opt]
 
+   If IS_TEMPLATE_ARG is true, we are parsing a template argument.
+
+   If IS_TRAILING_RETURN is true, we are in a trailing-return-type,
+   i.e. we've just seen "->".
+
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
+   this context.
+
    Returns the TYPE specified.  */
 
 static tree
-cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
-		     bool is_trailing_return, location_t * type_location)
+cp_parser_type_id_1 (cp_parser *parser, bool is_template_arg,
+		     bool is_trailing_return, bool typename_optional_p,
+		     location_t *type_location)
 {
   cp_decl_specifier_seq type_specifier_seq;
   cp_declarator *abstract_declarator;
@@ -21260,6 +21292,7 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
   /* Parse the type-specifier-seq.  */
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
 				is_trailing_return,
+				typename_optional_p,
 				&type_specifier_seq);
   if (type_location)
     *type_location = type_specifier_seq.locations[ds_type_spec];
@@ -21284,7 +21317,8 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
     = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, NULL,
 			    /*parenthesized_p=*/NULL,
 			    /*member_p=*/false,
-			    /*friend_p=*/false);
+			    /*friend_p=*/false,
+			    /*typename_optional_p=*/false);
   /* Check to see if there really was a declarator.  */
   if (!cp_parser_parse_definitely (parser))
     abstract_declarator = NULL;
@@ -21328,12 +21362,18 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
 		       is_template_arg);
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
-cp_parser_type_id (cp_parser *parser, location_t * type_location)
+cp_parser_type_id (cp_parser *parser, location_t *type_location,
+		   bool typename_optional_p)
 {
-  return cp_parser_type_id_1 (parser, false, false, type_location);
+  return cp_parser_type_id_1 (parser, false, false, typename_optional_p,
+			      type_location);
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
 cp_parser_template_type_arg (cp_parser *parser)
 {
@@ -21341,7 +21381,7 @@ cp_parser_template_type_arg (cp_parser *parser)
   const char *saved_message = parser->type_definition_forbidden_message;
   parser->type_definition_forbidden_message
     = G_("types may not be defined in template arguments");
-  r = cp_parser_type_id_1 (parser, true, false, NULL);
+  r = cp_parser_type_id_1 (parser, true, false, false, NULL);
   parser->type_definition_forbidden_message = saved_message;
   if (cxx_dialect >= cxx14 && !flag_concepts && type_uses_auto (r))
     {
@@ -21351,10 +21391,13 @@ cp_parser_template_type_arg (cp_parser *parser)
   return r;
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
 cp_parser_trailing_type_id (cp_parser *parser)
 {
-  return cp_parser_type_id_1 (parser, false, true, NULL);
+  const bool typename_optional_p = (cxx_dialect >= cxx2a);
+  return cp_parser_type_id_1 (parser, false, true, typename_optional_p, NULL);
 }
 
 /* Parse a type-specifier-seq.
@@ -21373,12 +21416,16 @@ cp_parser_trailing_type_id (cp_parser *parser)
    If IS_TRAILING_RETURN is true, we are in a trailing-return-type,
    i.e. we've just seen "->".
 
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
+   this context.
+
    Sets *TYPE_SPECIFIER_SEQ to represent the sequence.  */
 
 static void
 cp_parser_type_specifier_seq (cp_parser* parser,
 			      bool is_declaration,
 			      bool is_trailing_return,
+			      bool typename_optional_p,
 			      cp_decl_specifier_seq *type_specifier_seq)
 {
   bool seen_type_specifier = false;
@@ -21394,6 +21441,9 @@ cp_parser_type_specifier_seq (cp_parser* parser,
   if (is_trailing_return)
     flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS;
 
+  if (typename_optional_p)
+    flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
+
   /* Parse the type-specifiers and attributes.  */
   while (true)
     {
@@ -21495,10 +21545,12 @@ function_being_declared_is_template_p (cp_parser* parser)
 
    Returns a representation for the parameter declarations.  A return
    value of NULL indicates a parameter-declaration-clause consisting
-   only of an ellipsis.  */
+   only of an ellipsis.  TYPENAME_OPTIONAL_P is true if the typename
+   keyword is optional in this context.  */
 
 static tree
-cp_parser_parameter_declaration_clause (cp_parser* parser)
+cp_parser_parameter_declaration_clause (cp_parser* parser,
+					bool typename_optional_p)
 {
   tree parameters;
   cp_token *token;
@@ -21541,7 +21593,8 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
     }
 
   /* Parse the parameter-declaration-list.  */
-  parameters = cp_parser_parameter_declaration_list (parser);
+  parameters = cp_parser_parameter_declaration_list (parser,
+						     typename_optional_p);
   /* If a parse error occurred while parsing the
      parameter-declaration-list, then the entire
      parameter-declaration-clause is erroneous.  */
@@ -21586,10 +21639,13 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
 
    Returns a representation of the parameter-declaration-list, as for
    cp_parser_parameter_declaration_clause.  However, the
-   `void_list_node' is never appended to the list.  */
+   `void_list_node' is never appended to the list.
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
+   this context.  */
 
 static tree
-cp_parser_parameter_declaration_list (cp_parser* parser)
+cp_parser_parameter_declaration_list (cp_parser* parser,
+				      bool typename_optional_p)
 {
   tree parameters = NULL_TREE;
   tree *tail = &parameters;
@@ -21614,7 +21670,8 @@ cp_parser_parameter_declaration_list (cp_parser* parser)
       parameter
 	= cp_parser_parameter_declaration (parser,
 					   /*template_parm_p=*/false,
-					   &parenthesized_p);
+					   &parenthesized_p,
+					   typename_optional_p);
 
       /* We don't know yet if the enclosing context is deprecated, so wait
 	 and warn in grokparms if appropriate.  */
@@ -21751,12 +21808,14 @@ cp_parser_parameter_declaration_list (cp_parser* parser)
 
    Returns a representation of the parameter, or NULL if an error
    occurs.  If PARENTHESIZED_P is non-NULL, *PARENTHESIZED_P is set to
-   true iff the declarator is of the form "(p)".  */
+   true iff the declarator is of the form "(p)".  TYPENAME_OPTIONAL_P is
+   true if the typename keyword is optional in this context.  */
 
 static cp_parameter_declarator *
 cp_parser_parameter_declaration (cp_parser *parser,
 				 bool template_parm_p,
-				 bool *parenthesized_p)
+				 bool *parenthesized_p,
+				 bool typename_optional_p)
 {
   int declares_class_or_enum;
   cp_decl_specifier_seq decl_specifiers;
@@ -21786,8 +21845,11 @@ cp_parser_parameter_declaration (cp_parser *parser,
 
   /* Parse the declaration-specifiers.  */
   cp_token *decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
+  cp_parser_flags flags = CP_PARSER_FLAGS_NONE;
+  if (typename_optional_p)
+    flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_NONE,
+				flags,
 				&decl_specifiers,
 				&declares_class_or_enum);
 
@@ -21849,7 +21911,8 @@ cp_parser_parameter_declaration (cp_parser *parser,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 parenthesized_p,
 					 /*member_p=*/false,
-					 /*friend_p=*/false);
+					 /*friend_p=*/false,
+					 /*typename_optional_p=*/false);
       parser->default_arg_ok_p = saved_default_arg_ok_p;
       /* After the declarator, allow more attributes.  */
       decl_specifiers.attributes
@@ -23725,6 +23788,7 @@ cp_parser_member_declaration (cp_parser* parser)
   cp_token *initializer_token_start = NULL;
   int saved_pedantic;
   bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+  cp_parser_flags flags = CP_PARSER_FLAGS_OPTIONAL;
 
   /* Check for the `__extension__' keyword.  */
   if (cp_parser_extension_opt (parser, &saved_pedantic))
@@ -23818,8 +23882,10 @@ cp_parser_member_declaration (cp_parser* parser)
 
   /* Parse the decl-specifier-seq.  */
   decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
+  if (cxx_dialect >= cxx2a)
+    flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_OPTIONAL,
+				flags,
 				&decl_specifiers,
 				&declares_class_or_enum);
   /* Check for an invalid type-name.  */
@@ -24049,6 +24115,7 @@ cp_parser_member_declaration (cp_parser* parser)
 	      cp_declarator *declarator;
 	      tree asm_specification;
 	      int ctor_dtor_or_conv_p;
+	      const bool typename_optional_p = (cxx_dialect >= cxx2a);
 
 	      /* Parse the declarator.  */
 	      declarator
@@ -24056,7 +24123,7 @@ cp_parser_member_declaration (cp_parser* parser)
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/true,
-					friend_p);
+					friend_p, typename_optional_p);
 
 	      /* If something went wrong parsing the declarator, make sure
 		 that we at least consume some tokens.  */
@@ -24917,6 +24984,7 @@ cp_parser_exception_declaration (cp_parser* parser)
   /* Parse the type-specifier-seq.  */
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/true,
 				/*is_trailing_return=*/false,
+				/*typename_optional_p=*/false,
 				&type_specifiers);
   /* If it's a `)', then there is no declarator.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
@@ -24926,7 +24994,8 @@ cp_parser_exception_declaration (cp_parser* parser)
 				       /*ctor_dtor_or_conv_p=*/NULL,
 				       /*parenthesized_p=*/NULL,
 				       /*member_p=*/false,
-				       /*friend_p=*/false);
+				       /*friend_p=*/false,
+				       /*typename_optional_p=*/false);
 
   /* Restore the saved message.  */
   parser->type_definition_forbidden_message = saved_message;
@@ -26078,7 +26147,9 @@ cp_parser_requirement_parameter_list (cp_parser *parser)
   if (!parens.require_open (parser))
     return error_mark_node;
 
-  tree parms = cp_parser_parameter_declaration_clause (parser);
+  tree parms
+    = cp_parser_parameter_declaration_clause (parser,
+					      /*typename_optional_p=*/false);
 
   if (!parens.require_close (parser))
     return error_mark_node;
@@ -26832,6 +26903,16 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p)
 					    /*type_p=*/false,
 					    /*is_declaration=*/false));
 
+  /* Resolve the TYPENAME_TYPE, because the call above didn't do it.  */
+  if (nested_name_specifier
+      && TREE_CODE (nested_name_specifier) == TYPENAME_TYPE)
+    {
+      tree s = resolve_typename_type (nested_name_specifier,
+				      /*only_current_p=*/false);
+      if (TREE_CODE (s) != TYPENAME_TYPE)
+	nested_name_specifier = s;
+    }
+
   outside_class_specifier_p = (!at_class_scope_p ()
 			       || !TYPE_BEING_DEFINED (current_class_type)
 			       || friend_p);
@@ -27509,8 +27590,11 @@ cp_parser_single_declaration (cp_parser* parser,
   /* Try the `decl-specifier-seq [opt] init-declarator [opt]'
      alternative.  */
   decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
+  cp_parser_flags flags = CP_PARSER_FLAGS_OPTIONAL;
+  if (cxx_dialect >= cxx2a)
+    flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_OPTIONAL,
+				flags,
 				&decl_specifiers,
 				&declares_class_or_enum);
   if (friend_p)
@@ -27599,6 +27683,7 @@ cp_parser_single_declaration (cp_parser* parser,
       && (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)
 	  || decl_specifiers.type != error_mark_node))
     {
+      const bool typename_optional_p = (cxx_dialect >= cxx2a);
       decl = cp_parser_init_declarator (parser,
 				        &decl_specifiers,
 				        checks,
@@ -27606,7 +27691,8 @@ cp_parser_single_declaration (cp_parser* parser,
 				        member_p,
 				        declares_class_or_enum,
 				        &function_definition_p,
-					NULL, NULL, NULL);
+					NULL, NULL, NULL,
+					typename_optional_p);
 
     /* 7.1.1-1 [dcl.stc]
 
@@ -29244,7 +29330,8 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi)
 					    &ctor_dtor_or_conv_p,
 					    /*parenthesized_p=*/NULL,
 					    /*member_p=*/true,
-					    /*friend_p=*/false);
+					    /*friend_p=*/false,
+					    /*typename_optional_p=*/false);
 		      peek = cp_lexer_peek_token (parser->lexer);
 		      if (cp_parser_error_occurred (parser))
 			break;
@@ -29260,7 +29347,7 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi)
 		{
 		  cp_lexer_consume_token (parser->lexer);
 		  begin_scope (sk_function_parms, NULL_TREE);
-		  if (cp_parser_parameter_declaration_list (parser)
+		  if (cp_parser_parameter_declaration_list (parser, false)
 		      == error_mark_node)
 		    error = true;
 		  pop_bindings_and_leave_scope ();
@@ -30276,7 +30363,7 @@ cp_parser_objc_method_tail_params_opt (cp_parser* parser, bool *ellipsisp,
 	}
 
       /* TODO: parse attributes for tail parameters.  */
-      parmdecl = cp_parser_parameter_declaration (parser, false, NULL);
+      parmdecl = cp_parser_parameter_declaration (parser, false, NULL, false);
       parm = grokdeclarator (parmdecl->declarator,
 			     &parmdecl->decl_specifiers,
 			     PARM, /*initialized=*/0,
@@ -30608,7 +30695,8 @@ cp_parser_objc_class_ivars (cp_parser* parser)
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/false,
-					/*friend_p=*/false);
+					/*friend_p=*/false,
+					/*typename_optional_p=*/false);
 	    }
 
 	  /* Look for attributes that apply to the ivar.  */
@@ -30958,7 +31046,7 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
 	{
 	  /* We have "@catch (NSException *exception)" or something
 	     like that.  Parse the parameter declaration.  */
-	  parm = cp_parser_parameter_declaration (parser, false, NULL);
+	  parm = cp_parser_parameter_declaration (parser, false, NULL, false);
 	  if (parm == NULL)
 	    parameter_declaration = error_mark_node;
 	  else
@@ -31164,7 +31252,7 @@ cp_parser_objc_struct_declaration (cp_parser *parser)
 
       /* Parse the declarator.  */
       declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
-					 NULL, NULL, false, false);
+					 NULL, NULL, false, false, false);
 
       /* Look for attributes that apply to the ivar.  */
       attributes = cp_parser_attributes_opt (parser);
@@ -35774,6 +35862,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
   cp_parser_parse_tentatively (parser);
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/true,
 				/*is_trailing_return=*/false,
+				/*typename_optional_p=*/false,
 				&type_specifiers);
   if (cp_parser_parse_definitely (parser))
     {
@@ -35787,7 +35876,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
-					 /*friend_p=*/false);
+					 /*friend_p=*/false,
+					 /*typename_optional_p=*/false);
       attributes = cp_parser_attributes_opt (parser);
       asm_specification = cp_parser_asm_specification_opt (parser);
 
diff --git gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
index 02eb33643ac..f9b4477448b 100644
--- gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
+++ gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
@@ -1,4 +1,4 @@
 // PR c++/59120
 // { dg-do compile { target c++11 } }
 
-template<typename T> using X = int T::T*;  // { dg-error "expected" }
+template<typename T> using X = int T::T*;  // { dg-error "expected|two or more" }
diff --git gcc/testsuite/g++.dg/cpp0x/decltype67.C gcc/testsuite/g++.dg/cpp0x/decltype67.C
index e8042ac59e7..f3bf9a24b04 100644
--- gcc/testsuite/g++.dg/cpp0x/decltype67.C
+++ gcc/testsuite/g++.dg/cpp0x/decltype67.C
@@ -3,5 +3,5 @@
 
 template<typename T> struct A
 {
-  void foo(decltype(T())::Y);	// { dg-error {decltype\(T\(\)\)::Y} }
+  void foo(decltype(T())::Y);	// { dg-error "decltype\\(T\\(\\)\\)::Y" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp1z/typename1.C gcc/testsuite/g++.dg/cpp1z/typename1.C
new file mode 100644
index 00000000000..4c598c839f2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/typename1.C
@@ -0,0 +1,117 @@
+// P0634R3
+// { dg-do compile { target c++17_only } }
+
+// (5.2.1) simple-declaration or a function-definition in namespace scope
+
+template<typename T>
+T::X fn1 (int); // { dg-error "need .typename." }
+
+template<typename T>
+T::X fn1 (int) // { dg-error "need .typename." }
+{
+  return 42;
+}
+
+template<typename T>
+T::X v1; // { dg-error "need .typename." }
+
+namespace N {
+  template<typename T>
+  T::X v2; // { dg-error "need .typename." }
+}
+
+// (5.2.2) member-declaration
+
+template<typename T>
+struct S {
+  [[noreturn]] T::X fn2 (); // { dg-error "need .typename." }
+  T::X fn3 (); // { dg-error "need .typename." }
+  T::X fn4 () { return 5; } // { dg-error "need .typename." }
+  T::X fn5 () final; // { dg-error "need .typename." }
+  T::X fn6 () = 0; // { dg-error "need .typename." }
+  T::X fn8 () override; // { dg-error "need .typename." }
+  T::X v3; // { dg-error "need .typename." }
+  T::X *v4; // { dg-error "need .typename." }
+  T::X v5[5]; // { dg-error "need .typename." }
+  T::X v6 = 0; // { dg-error "need .typename." }
+  T::X v7{0}; // { dg-error "need .typename.|;" }
+  T::X v8 : 16; // { dg-error "need .typename." }
+  static constexpr T::X v9 = 0; // { dg-error "need .typename." }
+  typedef T::X T2; // { dg-error "need .typename." }
+  friend T::X fn7<int> (); // { dg-error "need .typename." }
+  static inline T::X v10; // { dg-error "need .typename." }
+};
+
+// (5.2.3) parameter-declaration in a member-declaration,
+// unless that parameter-declaration appears in a default argument
+
+template<typename T>
+struct S2 {
+  friend int fn1<T::X> (); // { dg-error "" }
+  int fn2 (T::X p); // { dg-error "not a type" }
+  int fn5 (int = T::X);
+};
+
+// (5.2.4) parameter-declaration in a declarator of a function or function
+// template declaration whose declarator-id is qualified,
+// unless that parameter-declaration appears in a default argument
+template<typename T>
+int fn3 (T::X);
+
+template<typename T>
+int fn4 (T::X p) { return p; } // { dg-error "" }
+
+// (5.2.6) parameter-declaration of a (non-type) template-parameter
+
+template<typename T, T::X N> // { dg-error "not a type" }
+struct S3 { };
+
+// default argument of a type-parameter of a template
+template<typename T, typename U = T::X> // { dg-error "need .typename." }
+struct S4 { };
+
+// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast
+template<typename T>
+struct S5 {
+  void fn6 (T::X p) // { dg-error "not a type" }
+  {
+    int i = static_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = dynamic_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = reinterpret_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = const_cast<T::Y>(p); // { dg-error "need .typename." }
+  }
+};
+
+template<typename T>
+void fn7 (T::X p) // { dg-error "" }
+{
+  int i = static_cast<T::Y>(p);
+  i = dynamic_cast<T::Y>(p);
+  i = reinterpret_cast<T::Y>(p);
+  i = const_cast<T::Y>(p);
+}
+
+// new-type-id
+template<typename T>
+void
+fn8 ()
+{
+  new T::X[10]; // { dg-error "need .typename." }
+}
+
+// defining-type-id
+
+template<typename T>
+struct W { typedef int M; };
+
+template<typename T>
+struct S6 {
+  using TT = T::X; // { dg-error "need .typename." }
+  using TT2 = W<T>::M; // { dg-error "need .typename." }
+};
+
+// trailing-return-type
+template<typename T>
+struct S7 {
+  auto fn9() -> W<T>::M; // { dg-error "need .typename." }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename1.C gcc/testsuite/g++.dg/cpp2a/typename1.C
new file mode 100644
index 00000000000..833d3b86093
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename1.C
@@ -0,0 +1,42 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// OK, return type of a function declaration at global scope.
+template<class T> T::R f();
+
+// Ill-formed (no diagnostic required), attempt to declare
+// a void variable template
+template<class T> void f(T::R);
+
+template <class T> struct A;
+template <class T> using B = A<T>::U;
+
+template<typename T>
+struct PtrTraits { typedef int Ptr; };
+
+template<class T> struct S {
+  // OK, in a defining-type-id.
+  using Ptr = PtrTraits<T>::Ptr;
+
+  // OK, trailing-return-type.
+  auto g() -> S<T*>::Ptr;
+
+  // OK, class scope
+  T::R
+  f (T::P p)
+  {
+    // OK, type-id of a static_cast
+    return static_cast<T::R>(p);
+  }
+};
+
+template<typename T>
+void f ()
+{
+  // Variable pf of type void* initialized with T::X
+  void (*pf)(T::X);
+
+  // Error: T::X at block scope does not denote a type
+  // (attempt to declare a void variable)
+  void g(T::X); // { dg-error "declared void" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename10.C gcc/testsuite/g++.dg/cpp2a/typename10.C
new file mode 100644
index 00000000000..fa2cd000b5d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename10.C
@@ -0,0 +1,20 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+namespace N {
+ // template<typename T> extern T::type v; // #1a
+  template<typename T> T::type v(typename T::value); // #1b
+}
+template<typename T> T::type N::v(T::value); // #2
+
+namespace N2 {
+  template<typename T> extern T::type v; // #1a
+  //template<typename T> T::type v(typename T::value); // #1b
+}
+template<typename T> T::type N2::v(T::value); // { dg-error "" }
+
+namespace A {
+  inline namespace B { template<typename T> int f(typename T::foo); }
+  inline namespace C { template<typename T> extern int f; }
+}
+template<typename T> int A::f(T::foo); // { dg-error "ambiguous" }
diff --git gcc/testsuite/g++.dg/cpp2a/typename11.C gcc/testsuite/g++.dg/cpp2a/typename11.C
new file mode 100644
index 00000000000..ed7ad958f62
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename11.C
@@ -0,0 +1,17 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+struct A
+{
+  typedef int I;
+};
+
+template<typename T> struct B
+{
+  A<T>::I i;
+  typename A<T>::I i2;
+
+  A<int>::I i3;
+  typename A<int>::I i4;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename2.C gcc/testsuite/g++.dg/cpp2a/typename2.C
new file mode 100644
index 00000000000..7c926177004
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename2.C
@@ -0,0 +1,22 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T> typename T::R f();
+
+template <class T> struct A;
+template <class T> using B = typename A<T>::U;
+
+template<typename T>
+struct PtrTraits { typedef int Ptr; };
+
+template<class T> struct S {
+  using Ptr = typename PtrTraits<T>::Ptr;
+
+  auto g() -> typename S<T*>::Ptr;
+
+  typename T::R
+  f (typename T::P p)
+  {
+    return static_cast<typename T::R>(p);
+  }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename3.C gcc/testsuite/g++.dg/cpp2a/typename3.C
new file mode 100644
index 00000000000..e64aa0316ef
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename3.C
@@ -0,0 +1,23 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T>
+void f(int i)
+{
+  T::x * i; // { dg-error "dependent-name" }
+}
+
+struct Foo {
+  typedef int x;
+};
+
+struct Bar {
+  static int const x = 5;
+};
+
+int
+main ()
+{
+  f<Bar>(1);
+  f<Foo>(1);
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename4.C gcc/testsuite/g++.dg/cpp2a/typename4.C
new file mode 100644
index 00000000000..69154e7daaf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename4.C
@@ -0,0 +1,8 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T>
+struct A {
+  typedef int B;
+  B b;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename5.C gcc/testsuite/g++.dg/cpp2a/typename5.C
new file mode 100644
index 00000000000..90f71028c65
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename5.C
@@ -0,0 +1,72 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+struct X {
+  template<typename T>
+  struct N { };
+};
+
+template <typename T>
+struct Y {
+  template<typename U>
+  struct N { };
+};
+
+template <typename T>
+struct A
+{
+  template <typename U>
+  struct N { };
+
+  // ??? Not sure if these are (in)valid.
+  typedef typename A::template N<int> a1;
+  typedef typename A::template N<T> a2;
+  typename A::template N<int> a3;
+  typename A::template N<T> a4;
+  typedef A::N<int> a5; // { dg-error "not a type|invalid" }
+  typedef A::N<T> a6; // { dg-error "not a type|invalid" }
+  typedef typename A::N<int> a7; // { dg-error "not a type" }
+  typedef typename A::N<T> a8; // { dg-error "not a type" }
+  A::N<int> a9; // { dg-error "not a type|invalid" }
+  A::N<T> a10; // { dg-error "not a type|invalid" }
+  typename A::N<int> a11; // { dg-error "not a type" }
+  typename A::N<T> a12; // { dg-error "not a type" }
+  typedef A<T>::N<int> a13; // { dg-error "not a type|invalid" }
+  typedef A<T>::N<T> a14; // { dg-error "not a type|invalid" }
+
+  typedef typename X::template N<int> x1;
+  typedef typename X::template N<T> x2;
+  typename X::template N<int> x3;
+  typename X::template N<T> x4;
+  typedef X::N<int> x5;
+  typedef X::N<T> x6;
+  typedef typename X::N<int> x7;
+  typedef typename X::N<T> x8;
+  X::N<int> x9;
+  X::N<T> x10;
+  typename X::N<int> x11;
+  typename X::N<T> x12;
+
+  typedef typename Y<int>::template N<int> y1;
+  typedef typename Y<int>::template N<T> y2;
+  typedef typename Y<T>::template N<int> y3;
+  typedef typename Y<T>::template N<T> y4;
+  typename Y<int>::template N<int> y5;
+  typename Y<int>::template N<T> y6;
+  typename Y<T>::template N<int> y7;
+  typename Y<T>::template N<T> y8;
+  typedef Y<int>::N<int> y9;
+  typedef Y<int>::N<T> y10;
+  typedef Y<T>::N<int> y11; // { dg-error "need .typename." }
+  typedef Y<T>::N<T> y12; // { dg-error "need .typename." }
+  typedef typename Y<int>::N<int> y13;
+  typedef typename Y<int>::N<T> y14;
+  Y<int>::N<int> y17;
+  Y<int>::N<T> y18;
+  typename Y<int>::N<int> y21;
+  typename Y<int>::N<T> y22;
+  typedef Y<int>::N<int> y25;
+  typedef Y<int>::N<T> y26;
+  typedef Y<T>::N<int> y27; // { dg-error "need .typename." }
+  typedef Y<T>::N<T> y28; // { dg-error "need .typename." }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename6.C gcc/testsuite/g++.dg/cpp2a/typename6.C
new file mode 100644
index 00000000000..49e2235a53d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename6.C
@@ -0,0 +1,126 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// (5.2.1) simple-declaration or a function-definition in namespace scope
+
+template<typename T>
+T::X fn1 (int);
+
+template<typename T>
+T::X fn1 (int)
+{
+  return 42;
+}
+
+template<typename T>
+T::X v1;
+
+namespace N {
+  template<typename T>
+  T::X v2;
+}
+
+// (5.2.2) member-declaration
+
+template<typename T>
+struct S {
+  [[noreturn]] T::X fn2 ();
+  T::X fn3 ();
+  T::X fn4 () { return 5; }
+  T::X fn5 () final;
+  T::X fn6 () = 0;
+  T::X fn8 () override;
+  T::X v3;
+  T::X *v4;
+  T::X v5[5];
+  T::X v6 = 0;
+  T::X v7{0};
+  T::X v8 : 16;
+  static constexpr T::X v9 = 0;
+  typedef T::X T2;
+  friend T::X fn7<int> ();
+  static inline T::X v10;
+};
+
+// (5.2.3) parameter-declaration in a member-declaration,
+// unless that parameter-declaration appears in a default argument
+
+template<typename T>
+struct S2 {
+  friend int fn1<T::X> ();
+  int fn2 (T::X p);
+  int fn5 (int = T::X);
+};
+
+// (5.2.4) parameter-declaration in a declarator of a function or function
+// template declaration whose declarator-id is qualified,
+// unless that parameter-declaration appears in a default argument
+template<typename T>
+int fn3 (T::X);
+
+template<typename T>
+int fn4 (T::X p) { return p; }
+
+// (5.2.5) parameter-declaration in a lambda-declarator,
+// unless that parameter-declaration appears in a default argument
+
+void
+fn5 ()
+{
+  auto j = []<typename T>(T::X t, int i) { return i; };
+}
+
+// (5.2.6) parameter-declaration of a (non-type) template-parameter
+
+template<typename T, T::X N>
+struct S3 { };
+
+// default argument of a type-parameter of a template
+template<typename T, typename U = T::X>
+struct S4 { };
+
+// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast
+template<typename T>
+struct S5 {
+  void fn6 (T::X p)
+  {
+    int i = static_cast<T::Y>(p);
+    i = dynamic_cast<T::Y>(p);
+    i = reinterpret_cast<T::Y>(p);
+    i = const_cast<T::Y>(p);
+  }
+};
+
+template<typename T>
+void fn7 (T::X p)
+{
+  int i = static_cast<T::Y>(p);
+  i = dynamic_cast<T::Y>(p);
+  i = reinterpret_cast<T::Y>(p);
+  i = const_cast<T::Y>(p);
+}
+
+// new-type-id
+template<typename T>
+void
+fn8 ()
+{
+  new T::X[10];
+}
+
+// defining-type-id
+
+template<typename T>
+struct W { typedef int M; };
+
+template<typename T>
+struct S6 {
+  using TT = T::X;
+  using TT2 = W<T>::M;
+};
+
+// trailing-return-type
+template<typename T>
+struct S7 {
+  auto fn9() -> W<T>::M;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename7.C gcc/testsuite/g++.dg/cpp2a/typename7.C
new file mode 100644
index 00000000000..713db51d972
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename7.C
@@ -0,0 +1,26 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// Not in namespace scope.
+template<typename T>
+void fn1 ()
+{
+  // init-statement -> simple-declaration
+  if (T::X r = 0; 0) // { dg-error "need .typename.|expected" }
+    ;
+
+  for (T::X g = 0; ;) // { dg-error "need .typename.|expected" }
+    ;
+}
+
+template<typename T>
+void
+fn2 ()
+{
+  T::X fn3 (); // { dg-error "need .typename.|expected" }
+  T::X v1; // { dg-error "need .typename.|expected" }
+  T::X v2 = 0; // { dg-error "need .typename.|expected" }
+  T::X v3{0}; // { dg-error "need .typename.|expected" }
+  static constexpr T::X v4 = 0; // { dg-error "need .typename." }
+  typedef T::X T2; // { dg-error "need .typename." }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename8.C gcc/testsuite/g++.dg/cpp2a/typename8.C
new file mode 100644
index 00000000000..3ebfde45ec5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename8.C
@@ -0,0 +1,20 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+struct S {
+  static int foo ();
+};
+
+template<typename T>
+int
+f ()
+{
+  return S<T>::foo();
+}
+
+void
+test ()
+{
+  f<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename9.C gcc/testsuite/g++.dg/cpp2a/typename9.C
new file mode 100644
index 00000000000..7b1865222b3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename9.C
@@ -0,0 +1,12 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+// { dg-options "-fconcepts" }
+
+template <typename, typename> class A { class B; };
+
+template <typename T, typename U> class A<T, U>::B {
+  B(A &);
+};
+
+template <typename T, typename U>
+A<T, U>::B::B(A<T, U> &) {}
diff --git gcc/testsuite/g++.dg/diagnostic/missing-typename.C gcc/testsuite/g++.dg/diagnostic/missing-typename.C
index 21d1ed18a60..b088bebf92f 100644
--- gcc/testsuite/g++.dg/diagnostic/missing-typename.C
+++ gcc/testsuite/g++.dg/diagnostic/missing-typename.C
@@ -1,4 +1,5 @@
 // fix-it hint for missing "typename" (PR c++/63392)
+// { dg-do compile { target c++17_down } }
 // { dg-options "-fdiagnostics-show-caret" }
 
 template<typename T>
diff --git gcc/testsuite/g++.dg/other/crash-9.C gcc/testsuite/g++.dg/other/crash-9.C
index 0953fcbc46b..f5c4b2af329 100644
--- gcc/testsuite/g++.dg/other/crash-9.C
+++ gcc/testsuite/g++.dg/other/crash-9.C
@@ -11,5 +11,5 @@ class A
   };
 };
 template <typename T> template <typename U>
-A<T>::B<U> A<T>::B<U>::foo() {}
+A<T>::template B<U> A<T>::B<U>::foo() {}
 
diff --git gcc/testsuite/g++.dg/other/nontype-1.C gcc/testsuite/g++.dg/other/nontype-1.C
index 11bbfb82968..8d90c322a7e 100644
--- gcc/testsuite/g++.dg/other/nontype-1.C
+++ gcc/testsuite/g++.dg/other/nontype-1.C
@@ -1,7 +1,7 @@
 template <class Op>
 bool asfun(Op f,
-           Op::first_argument_type a, // { dg-error "not a type" }
-           Op::second_argument_type b) // { dg-error "not a type" }
+           Op::first_argument_type a, // { dg-error "not a type" "" { target c++17_down } }
+           Op::second_argument_type b) // { dg-error "not a type" "" { target c++17_down } }
 {
    return Op(a, b);
 }
diff --git gcc/testsuite/g++.dg/parse/crash13.C gcc/testsuite/g++.dg/parse/crash13.C
index 3c298ec8ede..7a4939a462d 100644
--- gcc/testsuite/g++.dg/parse/crash13.C
+++ gcc/testsuite/g++.dg/parse/crash13.C
@@ -12,11 +12,11 @@ struct A
 };
 
 template <typename T> 
-void func(A<T>::B* )	// { dg-error "variable|template|expression" }
+void func(A<T>::B* )	// { dg-error "variable|template|expression" "" { target c++17_down } }
 {
 }
 
 int main() 
 {
-  func<void>(0);	// { dg-error "not declared|expression|;" }
+  func<void>(0);	// { dg-error "not declared|expression|;" "" { target c++17_down } }
 }
diff --git gcc/testsuite/g++.dg/parse/error36.C gcc/testsuite/g++.dg/parse/error36.C
index 46080b4835d..7e52d1537e0 100644
--- gcc/testsuite/g++.dg/parse/error36.C
+++ gcc/testsuite/g++.dg/parse/error36.C
@@ -25,7 +25,7 @@ template <class T> struct B
 
 // PR c++/40738
 template <class T>
-void g(const A<T>::type &t);	// { dg-error "typename" "typename" }
+void g(const A<T>::type &t);	// { dg-error "typename" "" { target c++17_down } }
 
 // PR c++/18451
-template <class T> A<T>::B A<T>::b; // { dg-error "typename" }
+template <class T> A<T>::B A<T>::b; // { dg-error "typename" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/parse/typedef2.C gcc/testsuite/g++.dg/parse/typedef2.C
index 1cc1ea0d58f..fd7554e19ac 100644
--- gcc/testsuite/g++.dg/parse/typedef2.C
+++ gcc/testsuite/g++.dg/parse/typedef2.C
@@ -1,2 +1,2 @@
 template <typename T> struct B { typedef typename T::X X; };
-template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" }
+template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/parse/typename11.C gcc/testsuite/g++.dg/parse/typename11.C
index 22f300707c8..832913ffd67 100644
--- gcc/testsuite/g++.dg/parse/typename11.C
+++ gcc/testsuite/g++.dg/parse/typename11.C
@@ -10,7 +10,7 @@ template <int dim> struct Y : X<dim> {
 
 // note: I is nested type in X, not Y!
 template <int dim>
-Y<dim>::I::I () {}		// { dg-error "dependent typedef" "typedef" }
-// { dg-error "no type|dependent type" "no type" { target *-*-* } .-1 }
+Y<dim>::I::I () {}		// { dg-error "expected|dependent typedef" "typedef" }
+// { dg-error "no type|dependent type" "no type" { target c++17_down } .-1 }
 
 template struct Y<1>;
diff --git gcc/testsuite/g++.dg/template/crash48.C gcc/testsuite/g++.dg/template/crash48.C
index 6aa3aa3ed26..acbe4342542 100644
--- gcc/testsuite/g++.dg/template/crash48.C
+++ gcc/testsuite/g++.dg/template/crash48.C
@@ -7,4 +7,4 @@ template<typename T> struct A
   typedef typename T::X X;
 };
 
-template<typename T> A<T>::X::X() {} // { dg-error "no type|invalid use|not a type|dependent" }
+template<typename T> A<T>::X::X() {} // { dg-error "expected|no type|invalid use|not a type|dependent" }
diff --git gcc/testsuite/g++.dg/template/dependent-name5.C gcc/testsuite/g++.dg/template/dependent-name5.C
index 681060c7002..3b99077edc9 100644
--- gcc/testsuite/g++.dg/template/dependent-name5.C
+++ gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -17,12 +17,14 @@ struct A
   typedef Bar          type1;
   typedef A::Bar       type2;
   typedef A<T>::Bar    type3;
-  typedef A<T*>::Bar    type4;  // { dg-error "" }
+  typedef A<T*>::Bar    type4;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::Bar type5;
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
+// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
+// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" }
   typedef typename A<T*>::template N<int> type10;
 
@@ -36,7 +38,7 @@ struct A
 
   typedef A::N2 type12;
   typedef typename type12::K k2;
-  typedef type12::K k1;  // { dg-error "" }
+  typedef type12::K k1;  // { dg-error "" "" { target c++17_down } }
 
   // Check that A::Bar2 is not considered dependent even if we use
   // the typename keyword.
diff --git gcc/testsuite/g++.dg/template/error29.C gcc/testsuite/g++.dg/template/error29.C
index 2e2291d7e87..6e335487224 100644
--- gcc/testsuite/g++.dg/template/error29.C
+++ gcc/testsuite/g++.dg/template/error29.C
@@ -1,5 +1,5 @@
 // PR c++/33209
 
-template<typename T> void foo(int, T::x); // { dg-error "T::x" }
+template<typename T> void foo(int, T::x); // { dg-error "T::x" "" { target c++17_down } }
 
-template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" }
+template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/template/nested5.C gcc/testsuite/g++.dg/template/nested5.C
index 3850fdace3a..e3e871e6d85 100644
--- gcc/testsuite/g++.dg/template/nested5.C
+++ gcc/testsuite/g++.dg/template/nested5.C
@@ -6,7 +6,7 @@ template <typename T> struct A
   {
     template <typename U> struct D {};
   };
-  template <typename S> static C::D<S> bar (S const &);
+  template <typename S> static C::template D<S> bar (S const &);
 };
 
 struct E {};
diff --git gcc/testsuite/g++.dg/template/pr84789.C gcc/testsuite/g++.dg/template/pr84789.C
index 63b9832fecf..bdf80dc3edf 100644
--- gcc/testsuite/g++.dg/template/pr84789.C
+++ gcc/testsuite/g++.dg/template/pr84789.C
@@ -9,5 +9,5 @@ template<typename> struct B : A {};
 
 template<typename T> struct C : B<T>
 {
-  B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" }
+  B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/template/static30.C gcc/testsuite/g++.dg/template/static30.C
index 07dafe23ffa..8b8637a1abe 100644
--- gcc/testsuite/g++.dg/template/static30.C
+++ gcc/testsuite/g++.dg/template/static30.C
@@ -6,5 +6,5 @@ template <int> struct A
   static const int i2;
 };
 
-template <int N> const int A<N>::i1(A<N>::i);
+template <int N> const int A<N>::i1(A<N>::i); // { dg-error "no declaration matches" "" { target c++2a } }
 template <int N> const int A<N>::i2(3, A<N>::i); // { dg-error "expression list" }
diff --git gcc/testsuite/g++.dg/template/typedef6.C gcc/testsuite/g++.dg/template/typedef6.C
index c95945966fa..a6202b55181 100644
--- gcc/testsuite/g++.dg/template/typedef6.C
+++ gcc/testsuite/g++.dg/template/typedef6.C
@@ -5,4 +5,4 @@ template<typename T> struct A
   typedef struct typename T::X X;       // { dg-error "expected identifier|two or more" }
 };
 
-template<typename T> A<T>::X::X() {}    // { dg-error "not a type|forbids declaration|invalid use of" }
+template<typename T> A<T>::X::X() {}    // { dg-error "expected|not a type|forbids declaration|invalid use of" }
diff --git gcc/testsuite/g++.dg/template/typename3.C gcc/testsuite/g++.dg/template/typename3.C
index 0ad9a2a0c41..ae10af27763 100644
--- gcc/testsuite/g++.dg/template/typename3.C
+++ gcc/testsuite/g++.dg/template/typename3.C
@@ -3,5 +3,5 @@
 
 template <class A>
 struct B {
- typedef A::C::D E;  // { dg-error "" }
+ typedef A::C::D E;  // { dg-error "" "" { target c++17_down } }
 };

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

* Re: C++ PATCH to implement C++20 P0634R3, Down with typename!
  2018-11-12 15:28 C++ PATCH to implement C++20 P0634R3, Down with typename! Marek Polacek
@ 2018-11-28 22:09 ` Jason Merrill
  2018-11-30 23:08   ` Marek Polacek
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2018-11-28 22:09 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches

On 11/12/18 10:27 AM, Marek Polacek wrote:
> This patch implements C++20 P0634R3, Down with typename!
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>
> which makes 'typename' optional in several contexts specified in [temp.res].
> 
> The gist of the patch is in cp_parser_simple_type_specifier, where, if the
> context makes typename optional and the id is qualified, we pretend we've
> seen the typename keyword.

Sounds good.

> There's quite a lot of churn because we need to be careful where we want
> to make typename optional, and e.g. a flag in cp_parser would be too global.

Did you consider adding a cp_parser_flags parameter rather than a 
specific bool?  I don't feel strongly about it, but it would simplify 
things the next time we want to do something like this.

> The resolve_typename_type hunk was to make typename9.C work with -fconcepts.

Makes sense.

> +	const bool typename_optional_p = (cxx_dialect >= cxx2a);

I'm uncomfortable with repeating this in lots of places in the parser; 
I'd prefer to have one place where we control whether the feature is 
enabled or not.  This seems like the right place:

> +	  bool typename_p = (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL);
> +	  type = cp_parser_type_name (parser, (qualified_p && typename_p));

> I'm not sure about some of the bits in typename5.C, not quite sure if the
> code is valid, but I didn't have time to investigate deeply and it seems
> pretty obscure anyway.  There are preexisting cases when g++ and clang++
> disagree.

> +  // ??? Not sure if these are (in)valid.

I think these are all valid, since the qualified-ids all appear as a 
decl-specifier of a member-declaration.

> +  typedef typename A::template N<int> a1;
> +  typedef typename A::template N<T> a2;
> +  typename A::template N<int> a3;
> +  typename A::template N<T> a4;
> +  typedef A::N<int> a5; // { dg-error "not a type|invalid" }
> +  typedef A::N<T> a6; // { dg-error "not a type|invalid" }

The ::template shouldn't be required here anymore: [temp.names] "In a 
qualified-id used as the name in a typename-specifier (12.7), 
elaborated-type-specifier (9.1.7.3), using-declaration (9.8), or 
class-or-decltype (10.6), an optional keyword template appearing at the 
top level is ignored. In these contexts, a < token is always assumed to 
introduce a template-argument-list."

This is C++17 DR 1710.  You don't need to implement it in this patch, 
but let's not test for the wrong behavior.  :)

Jason

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

* Re: C++ PATCH to implement C++20 P0634R3, Down with typename!
  2018-11-28 22:09 ` Jason Merrill
@ 2018-11-30 23:08   ` Marek Polacek
  2018-12-01 15:57     ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Marek Polacek @ 2018-11-30 23:08 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Wed, Nov 28, 2018 at 05:09:23PM -0500, Jason Merrill wrote:
> On 11/12/18 10:27 AM, Marek Polacek wrote:
> > This patch implements C++20 P0634R3, Down with typename!
> > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>
> > which makes 'typename' optional in several contexts specified in [temp.res].
> > 
> > The gist of the patch is in cp_parser_simple_type_specifier, where, if the
> > context makes typename optional and the id is qualified, we pretend we've
> > seen the typename keyword.
> 
> Sounds good.

\o/

> > There's quite a lot of churn because we need to be careful where we want
> > to make typename optional, and e.g. a flag in cp_parser would be too global.
> 
> Did you consider adding a cp_parser_flags parameter rather than a specific
> bool?  I don't feel strongly about it, but it would simplify things the next
> time we want to do something like this.

I think I did but ran out of time.  I've changed the boolean parameters to
cp_parser_flags in this patch.

> > The resolve_typename_type hunk was to make typename9.C work with -fconcepts.
> 
> Makes sense.
> 
> > +	const bool typename_optional_p = (cxx_dialect >= cxx2a);
> 
> I'm uncomfortable with repeating this in lots of places in the parser; I'd
> prefer to have one place where we control whether the feature is enabled or
> not.  This seems like the right place:
> > +	  bool typename_p = (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL);
> > +	  type = cp_parser_type_name (parser, (qualified_p && typename_p));

Indeed, this was a nice cleanup.

> > I'm not sure about some of the bits in typename5.C, not quite sure if the
> > code is valid, but I didn't have time to investigate deeply and it seems
> > pretty obscure anyway.  There are preexisting cases when g++ and clang++
> > disagree.
> 
> > +  // ??? Not sure if these are (in)valid.
> 
> I think these are all valid, since the qualified-ids all appear as a
> decl-specifier of a member-declaration.

Ok, I found a bug in the previous version of this patch: when we saw the
template keyword, we didn't call cp_parser_type_name in
cp_parser_simple_type_specifier, as a result, typename was needed in
a member-declaration even though it now shouldn't.
 
Fixed by calling cp_parser_make_typename_type.

> > +  typedef typename A::template N<int> a1;
> > +  typedef typename A::template N<T> a2;
> > +  typename A::template N<int> a3;
> > +  typename A::template N<T> a4;
> > +  typedef A::N<int> a5; // { dg-error "not a type|invalid" }
> > +  typedef A::N<T> a6; // { dg-error "not a type|invalid" }
> 
> The ::template shouldn't be required here anymore: [temp.names] "In a
> qualified-id used as the name in a typename-specifier (12.7),
> elaborated-type-specifier (9.1.7.3), using-declaration (9.8), or
> class-or-decltype (10.6), an optional keyword template appearing at the top
> level is ignored. In these contexts, a < token is always assumed to
> introduce a template-argument-list."
> 
> This is C++17 DR 1710.  You don't need to implement it in this patch, but
> let's not test for the wrong behavior.  :)

Thanks for the pointer to that!  With ::template it now passes, as shown in
typename5.C.

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

2018-11-30  Marek Polacek  <polacek@redhat.com>

	Implement P0634R3, Down with typename!
	* parser.c (CP_PARSER_FLAGS_TYPENAME_OPTIONAL): New enumerator.
	(cp_parser_type_name): Remove declaration.
	(cp_parser_postfix_expression): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL
	to cp_parser_type_id.
	(cp_parser_new_type_id): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL to
	cp_parser_type_specifier_seq.
	(cp_parser_lambda_declarator_opt): Pass
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL to
	cp_parser_parameter_declaration_clause.
	(cp_parser_condition): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.
	(cp_parser_simple_declaration): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_init_declarator.
	(cp_parser_conversion_type_id): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_type_specifier_seq.
	(cp_parser_default_type_template_argument): Pass
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL to cp_parser_type_id.
	(cp_parser_template_parameter): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL
	to cp_parser_parameter_declaration.
	(cp_parser_explicit_instantiation): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.
	(cp_parser_simple_type_specifier): Adjust call to cp_parser_type_name
	to relay if we should treat the typename keyword as optional.  Maybe
	call cp_parser_make_typename_type is parsing a template-id and it's
	not a TYPE_DECL.
	(cp_parser_type_name): Remove unused function.
	(cp_parser_enum_specifier): Pass to CP_PARSER_FLAGS_NONE
	cp_parser_type_specifier_seq.
	(cp_parser_alias_declaration): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL
	to cp_parser_type_id.
	(cp_parser_init_declarator): New parameter.  Pass it down to
	cp_parser_declarator.
	(cp_parser_declarator): New parameter.  Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.  Pass the new parameter to
	cp_parser_direct_declarator.
	(cp_parser_direct_declarator): New parameter.  Pass it to
	cp_parser_parameter_declaration_clause and cp_parser_declarator.
	(cp_parser_declarator_id):
	(cp_parser_type_id_1): New parameter.  Pass it to
	cp_parser_type_specifier_seq.  Adjust call to cp_parser_declarator.
	(cp_parser_type_id): New parameter.  Pass it to cp_parser_type_id_1.
	(cp_parser_template_type_arg): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_type_id_1.
	(cp_parser_trailing_type_id): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL
	to cp_parser_type_id_1.
	(cp_parser_type_specifier_seq): New parameter.
	(function_being_declared_is_template_p):
	(cp_parser_parameter_declaration_clause): New parameter.  Pass it to
	cp_parser_parameter_declaration_list.
	(cp_parser_parameter_declaration_list): New parameter.  Pass it to
	cp_parser_parameter_declaration.
	(cp_parser_parameter_declaration): New parameter.  Pass it to
	cp_parser_decl_specifier_seq.  Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.
	(cp_parser_member_declaration): Adjust call to
	cp_parser_decl_specifier_seq to also include
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL.  Pass
	CP_PARSER_FLAGS_TYPENAME_OPTIONAL to cp_parser_declarator.
	(cp_parser_exception_declaration): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_type_specifier_seq and cp_parser_declarator.
	(cp_parser_requirement_parameter_list): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_parameter_declaration_clause.
	(cp_parser_constructor_declarator_p): Resolve the TYPENAME_TYPE.
	(cp_parser_single_declaration): Pass CP_PARSER_FLAGS_TYPENAME_OPTIONAL
	to cp_parser_decl_specifier_seq and cp_parser_init_declarator.
	(cp_parser_cache_defarg): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator and cp_parser_parameter_declaration_list.
	(cp_parser_objc_method_tail_params_opt): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_parameter_declaration.
	(cp_parser_objc_class_ivars): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.
	(cp_parser_objc_try_catch_finally_statement): Pass CP_PARSER_FLAGS_NONE
	to cp_parser_parameter_declaration
	(cp_parser_objc_struct_declaration): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator.
	(cp_parser_omp_for_loop_init): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_declarator and cp_parser_type_specifier_seq.

	* g++.dg/cpp0x/alias-decl-43.C: Adjust dg-error.
	* g++.dg/cpp0x/decltype67.C: Only expect error in c++17_down.
	* g++.dg/cpp1z/typename1.C: New test.
	* g++.dg/cpp2a/typename1.C: New test.
	* g++.dg/cpp2a/typename10.C: New test.
	* g++.dg/cpp2a/typename11.C: New test.
	* g++.dg/cpp2a/typename2.C: New test.
	* g++.dg/cpp2a/typename3.C: New test.
	* g++.dg/cpp2a/typename4.C: New test.
	* g++.dg/cpp2a/typename5.C: New test.
	* g++.dg/cpp2a/typename6.C: New test.
	* g++.dg/cpp2a/typename7.C: New test.
	* g++.dg/cpp2a/typename8.C: New test.
	* g++.dg/cpp2a/typename9.C: New test.
	* g++.dg/diagnostic/missing-typename.C: Only run the test in
	c++17_down.
	* g++.dg/other/crash-9.C: Add template disambiguator.
	* g++.dg/other/nontype-1.C: Only expect error in c++17_down.
	* g++.dg/parse/crash13.C: Likewise.
	* g++.dg/parse/error36.C: Likewise.
	* g++.dg/parse/no-typename1.C: Likewise.
	* g++.dg/parse/typedef2.C: Likewise.
	* g++.dg/parse/typename11.C: Likewise.
	* g++.dg/template/crash48.C: Adjust dg-error.
	* g++.dg/template/dependent-name5.C: Only expect error in c++17_down.
	Add dg-error.
	* g++.dg/template/error29.C: Only expect error in c++17_down.
	* g++.dg/template/nested5.C: Add template disambiguator.
	* g++.dg/template/pr84789.C: Only expect error in c++17_down.
	* g++.dg/template/static30.C: Add dg-error.
	* g++.dg/template/typedef6.C: Adjust dg-error.
	* g++.dg/template/typename3.C: Only expect error in c++17_down.

diff --git gcc/cp/parser.c gcc/cp/parser.c
index 634485b5a8c..91d010bc366 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -1791,7 +1791,9 @@ enum
      constexpr.  */
   CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8,
   /* When parsing a decl-specifier-seq, only allow mutable or constexpr.  */
-  CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10
+  CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
+  /* When parsing a decl-specifier-seq, allow missing typename.  */
+  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20
 };
 
 /* This type is used for parameters and variables which hold
@@ -2156,8 +2158,6 @@ static tree cp_parser_simple_type_specifier
   (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags);
 static tree cp_parser_type_name
   (cp_parser *, bool);
-static tree cp_parser_type_name
-  (cp_parser *);
 static tree cp_parser_nonclass_name 
   (cp_parser* parser);
 static tree cp_parser_elaborated_type_specifier
@@ -2198,12 +2198,14 @@ static tree cp_parser_decomposition_declaration
 /* Declarators [gram.dcl.decl] */
 
 static tree cp_parser_init_declarator
-  (cp_parser *, cp_decl_specifier_seq *, vec<deferred_access_check, va_gc> *,
-   bool, bool, int, bool *, tree *, location_t *, tree *);
+  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *,
+   vec<deferred_access_check, va_gc> *, bool, bool, int, bool *, tree *,
+   location_t *, tree *);
 static cp_declarator *cp_parser_declarator
-  (cp_parser *, cp_parser_declarator_kind, int *, bool *, bool, bool);
+  (cp_parser *, cp_parser_declarator_kind, cp_parser_flags, int *, bool *,
+   bool, bool);
 static cp_declarator *cp_parser_direct_declarator
-  (cp_parser *, cp_parser_declarator_kind, int *, bool, bool);
+  (cp_parser *, cp_parser_declarator_kind, cp_parser_flags, int *, bool, bool);
 static enum tree_code cp_parser_ptr_operator
   (cp_parser *, tree *, cp_cv_quals *, tree *);
 static cp_cv_quals cp_parser_cv_qualifier_seq_opt
@@ -2219,20 +2221,20 @@ static tree cp_parser_late_return_type_opt
 static tree cp_parser_declarator_id
   (cp_parser *, bool);
 static tree cp_parser_type_id
-  (cp_parser *, location_t * = NULL);
+  (cp_parser *, cp_parser_flags = CP_PARSER_FLAGS_NONE, location_t * = NULL);
 static tree cp_parser_template_type_arg
   (cp_parser *);
 static tree cp_parser_trailing_type_id (cp_parser *);
 static tree cp_parser_type_id_1
-  (cp_parser *, bool, bool, location_t *);
+  (cp_parser *, cp_parser_flags, bool, bool, location_t *);
 static void cp_parser_type_specifier_seq
-  (cp_parser *, bool, bool, cp_decl_specifier_seq *);
+  (cp_parser *, cp_parser_flags, bool, bool, cp_decl_specifier_seq *);
 static tree cp_parser_parameter_declaration_clause
-  (cp_parser *);
+  (cp_parser *, cp_parser_flags);
 static tree cp_parser_parameter_declaration_list
-  (cp_parser *);
+  (cp_parser *, cp_parser_flags);
 static cp_parameter_declarator *cp_parser_parameter_declaration
-  (cp_parser *, bool, bool *);
+  (cp_parser *, cp_parser_flags, bool, bool *);
 static tree cp_parser_default_argument 
   (cp_parser *, bool);
 static void cp_parser_function_body
@@ -6789,7 +6791,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* Parse the type to which we are casting.  */
 	saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
 	parser->in_type_id_in_expr_p = true;
-	type = cp_parser_type_id (parser);
+	type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+				  NULL);
 	parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	/* Look for the closing `>'.  */
 	cp_parser_require (parser, CPP_GREATER, RT_GREATER);
@@ -8710,7 +8713,8 @@ cp_parser_new_type_id (cp_parser* parser, tree *nelts)
   parser->type_definition_forbidden_message
     = G_("types may not be defined in a new-type-id");
   /* Parse the type-specifier-seq.  */
-  cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
+  cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+				/*is_declaration=*/false,
 				/*is_trailing_return=*/false,
 				&type_specifier_seq);
   /* Restore the old message.  */
@@ -10727,7 +10731,9 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       begin_scope (sk_function_parms, /*entity=*/NULL_TREE);
 
       /* Parse parameters.  */
-      param_list = cp_parser_parameter_declaration_clause (parser);
+      param_list
+	= cp_parser_parameter_declaration_clause
+	    (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL);
 
       /* Default arguments shall not be specified in the
 	 parameter-declaration-clause of a lambda-declarator.  */
@@ -11913,6 +11919,7 @@ cp_parser_condition (cp_parser* parser)
 
       /* Parse the declarator.  */
       declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					 CP_PARSER_FLAGS_NONE,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
@@ -13387,7 +13394,9 @@ cp_parser_simple_declaration (cp_parser* parser,
 	saw_declarator = true;
 
       /* Parse the init-declarator.  */
-      decl = cp_parser_init_declarator (parser, &decl_specifiers,
+      decl = cp_parser_init_declarator (parser,
+					CP_PARSER_FLAGS_NONE,
+					&decl_specifiers,
 					/*checks=*/NULL,
 					function_definition_allowed_p,
 					/*member_p=*/false,
@@ -14653,7 +14662,8 @@ cp_parser_conversion_type_id (cp_parser* parser)
     = G_("types may not be defined in a conversion-type-id");
 
   /* Parse the type-specifiers.  */
-  cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
+  cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE,
+				/*is_declaration=*/false,
 				/*is_trailing_return=*/false,
 				&type_specifiers);
 
@@ -15742,7 +15752,9 @@ cp_parser_default_type_template_argument (cp_parser *parser)
 
   /* Parse the default-argument.  */
   push_deferring_access_checks (dk_no_deferred);
-  tree default_argument = cp_parser_type_id (parser);
+  tree default_argument = cp_parser_type_id (parser,
+					     CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+					     NULL);
   pop_deferring_access_checks ();
 
   if (flag_concepts && type_uses_auto (default_argument))
@@ -15869,7 +15881,9 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
      of the template parameter-list rather than a greater-than
      operator.  */
   parameter_declarator
-     = cp_parser_parameter_declaration (parser, /*template_parm_p=*/true,
+     = cp_parser_parameter_declaration (parser,
+					CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+					/*template_parm_p=*/true,
 					/*parenthesized_p=*/NULL);
 
   if (!parameter_declarator)
@@ -17016,6 +17030,7 @@ cp_parser_explicit_instantiation (cp_parser* parser)
       /* Parse the declarator.  */
       declarator
 	= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+				CP_PARSER_FLAGS_NONE,
 				/*ctor_dtor_or_conv_p=*/NULL,
 				/*parenthesized_p=*/NULL,
 				/*member_p=*/false,
@@ -17616,6 +17631,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
     {
       bool qualified_p;
       bool global_p;
+      const bool typename_p = (cxx_dialect >= cxx2a
+			       && (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL));
 
       /* Don't gobble tokens or issue error messages if this is an
 	 optional type-specifier.  */
@@ -17652,13 +17669,21 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 	     luck.  */
 	  if (TREE_CODE (type) != TYPE_DECL)
 	    {
-	      cp_parser_error (parser, "expected template-id for type");
-	      type = NULL_TREE;
+	      /* ...unless we pretend we have seen 'typename'.  */
+	      if (typename_p)
+		type = cp_parser_make_typename_type (parser, type,
+						     token->location);
+	      else
+		{
+		  cp_parser_error (parser, "expected template-id for type");
+		  type = NULL_TREE;
+		}
 	    }
 	}
       /* Otherwise, look for a type-name.  */
       else
-	type = cp_parser_type_name (parser);
+	type = cp_parser_type_name (parser, (qualified_p && typename_p));
+
       /* Keep track of all name-lookups performed in class scopes.  */
       if (type
 	  && !global_p
@@ -17783,13 +17808,6 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 
    Returns a TYPE_DECL for the type.  */
 
-static tree
-cp_parser_type_name (cp_parser* parser)
-{
-  return cp_parser_type_name (parser, /*typename_keyword_p=*/false);
-}
-
-/* See above. */
 static tree
 cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
 {
@@ -18577,7 +18595,8 @@ cp_parser_enum_specifier (cp_parser* parser)
       cp_lexer_consume_token (parser->lexer);
 
       /* Parse the type-specifier-seq.  */
-      cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
+      cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE,
+				    /*is_declaration=*/false,
 				    /*is_trailing_return=*/false,
                                     &type_specifiers);
 
@@ -19434,7 +19453,8 @@ cp_parser_alias_declaration (cp_parser* parser)
 	G_("types may not be defined in alias template declarations");
     }
 
-  type = cp_parser_type_id (parser, &type_location);
+  type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+			    &type_location);
 
   /* Restore the error message if need be.  */
   if (parser->num_template_parameter_lists)
@@ -19814,6 +19834,8 @@ strip_declarator_types (tree type, cp_declarator *declarator)
    function-definition:
      decl-specifier-seq [opt] declarator function-transaction-block
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    The DECL_SPECIFIERS apply to this declarator.  Returns a
    representation of the entity declared.  If MEMBER_P is TRUE, then
    this declarator appears in a class scope.  The new DECL created by
@@ -19845,6 +19867,7 @@ strip_declarator_types (tree type, cp_declarator *declarator)
 
 static tree
 cp_parser_init_declarator (cp_parser* parser,
+			   cp_parser_flags flags,
 			   cp_decl_specifier_seq *decl_specifiers,
 			   vec<deferred_access_check, va_gc> *checks,
 			   bool function_definition_allowed_p,
@@ -19901,7 +19924,7 @@ cp_parser_init_declarator (cp_parser* parser,
   /* Parse the declarator.  */
   declarator
     = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
-			    &ctor_dtor_or_conv_p,
+			    flags, &ctor_dtor_or_conv_p,
 			    /*parenthesized_p=*/NULL,
 			    member_p, friend_p);
   /* Gather up the deferred checks.  */
@@ -20300,6 +20323,8 @@ cp_parser_init_declarator (cp_parser* parser,
      attributes [opt] ptr-operator abstract-declarator [opt]
      attributes [opt] direct-abstract-declarator
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    If CTOR_DTOR_OR_CONV_P is not NULL, *CTOR_DTOR_OR_CONV_P is used to
    detect constructors, destructors, deduction guides, or conversion operators.
    It is set to -1 if the declarator is a name, and +1 if it is a
@@ -20319,11 +20344,15 @@ cp_parser_init_declarator (cp_parser* parser,
 
    MEMBER_P is true iff this declarator is a member-declarator.
 
-   FRIEND_P is true iff this declarator is a friend.  */
+   FRIEND_P is true iff this declarator is a friend.
+
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in this
+   context.  */
 
 static cp_declarator *
 cp_parser_declarator (cp_parser* parser,
 		      cp_parser_declarator_kind dcl_kind,
+		      cp_parser_flags flags,
 		      int* ctor_dtor_or_conv_p,
 		      bool* parenthesized_p,
 		      bool member_p, bool friend_p)
@@ -20364,6 +20393,7 @@ cp_parser_declarator (cp_parser* parser,
 
       /* Parse the dependent declarator.  */
       declarator = cp_parser_declarator (parser, dcl_kind,
+					 CP_PARSER_FLAGS_NONE,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
@@ -20385,7 +20415,7 @@ cp_parser_declarator (cp_parser* parser,
 	*parenthesized_p = cp_lexer_next_token_is (parser->lexer,
 						   CPP_OPEN_PAREN);
       declarator = cp_parser_direct_declarator (parser, dcl_kind,
-						ctor_dtor_or_conv_p,
+						flags, ctor_dtor_or_conv_p,
 						member_p, friend_p);
     }
 
@@ -20420,12 +20450,15 @@ cp_parser_declarator (cp_parser* parser,
    we are parsing a direct-declarator.  It is
    CP_PARSER_DECLARATOR_EITHER, if we can accept either - in the case
    of ambiguity we prefer an abstract declarator, as per
-   [dcl.ambig.res].  CTOR_DTOR_OR_CONV_P, MEMBER_P, and FRIEND_P are
+   [dcl.ambig.res].
+   The parser flags FLAGS is used to control type-specifier parsing.
+   CTOR_DTOR_OR_CONV_P, MEMBER_P, and FRIEND_P are
    as for cp_parser_declarator.  */
 
 static cp_declarator *
 cp_parser_direct_declarator (cp_parser* parser,
 			     cp_parser_declarator_kind dcl_kind,
+			     cp_parser_flags flags,
 			     int* ctor_dtor_or_conv_p,
 			     bool member_p, bool friend_p)
 {
@@ -20512,7 +20545,8 @@ cp_parser_direct_declarator (cp_parser* parser,
 	      begin_scope (sk_function_parms, NULL_TREE);
 
 	      /* Parse the parameter-declaration-clause.  */
-	      params = cp_parser_parameter_declaration_clause (parser);
+	      params
+		= cp_parser_parameter_declaration_clause (parser, flags);
 
 	      /* Consume the `)'.  */
 	      parens.require_close (parser);
@@ -20603,7 +20637,8 @@ cp_parser_direct_declarator (cp_parser* parser,
 	      saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
 	      parser->in_type_id_in_expr_p = true;
 	      declarator
-		= cp_parser_declarator (parser, dcl_kind, ctor_dtor_or_conv_p,
+		= cp_parser_declarator (parser, dcl_kind, flags,
+					ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					member_p, friend_p);
 	      parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
@@ -21453,17 +21488,27 @@ cp_parser_declarator_id (cp_parser* parser, bool optional_p)
    type-id:
      type-specifier-seq abstract-declarator [opt]
 
+   If IS_TEMPLATE_ARG is true, we are parsing a template argument.
+
+   If IS_TRAILING_RETURN is true, we are in a trailing-return-type,
+   i.e. we've just seen "->".
+
+   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
+   this context.
+
    Returns the TYPE specified.  */
 
 static tree
-cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
-		     bool is_trailing_return, location_t * type_location)
+cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags,
+		     bool is_template_arg, bool is_trailing_return,
+		     location_t *type_location)
 {
   cp_decl_specifier_seq type_specifier_seq;
   cp_declarator *abstract_declarator;
 
   /* Parse the type-specifier-seq.  */
-  cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
+  cp_parser_type_specifier_seq (parser, flags,
+				/*is_declaration=*/false,
 				is_trailing_return,
 				&type_specifier_seq);
   if (type_location)
@@ -21486,7 +21531,8 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
   cp_parser_parse_tentatively (parser);
   /* Look for the declarator.  */
   abstract_declarator
-    = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, NULL,
+    = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT,
+			    CP_PARSER_FLAGS_NONE, NULL,
 			    /*parenthesized_p=*/NULL,
 			    /*member_p=*/false,
 			    /*friend_p=*/false);
@@ -21533,12 +21579,17 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
 		       is_template_arg);
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
-cp_parser_type_id (cp_parser *parser, location_t * type_location)
+cp_parser_type_id (cp_parser *parser, cp_parser_flags flags,
+		   location_t *type_location)
 {
-  return cp_parser_type_id_1 (parser, false, false, type_location);
+  return cp_parser_type_id_1 (parser, flags, false, false, type_location);
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
 cp_parser_template_type_arg (cp_parser *parser)
 {
@@ -21546,7 +21597,7 @@ cp_parser_template_type_arg (cp_parser *parser)
   const char *saved_message = parser->type_definition_forbidden_message;
   parser->type_definition_forbidden_message
     = G_("types may not be defined in template arguments");
-  r = cp_parser_type_id_1 (parser, true, false, NULL);
+  r = cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_NONE, true, false, NULL);
   parser->type_definition_forbidden_message = saved_message;
   if (cxx_dialect >= cxx14 && !flag_concepts && type_uses_auto (r))
     {
@@ -21556,10 +21607,13 @@ cp_parser_template_type_arg (cp_parser *parser)
   return r;
 }
 
+/* Wrapper for cp_parser_type_id_1.  */
+
 static tree
 cp_parser_trailing_type_id (cp_parser *parser)
 {
-  return cp_parser_type_id_1 (parser, false, true, NULL);
+  return cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+			      false, true, NULL);
 }
 
 /* Parse a type-specifier-seq.
@@ -21572,6 +21626,8 @@ cp_parser_trailing_type_id (cp_parser *parser)
    type-specifier-seq:
      attributes type-specifier-seq [opt]
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    If IS_DECLARATION is true, we are at the start of a "condition" or
    exception-declaration, so we might be followed by a declarator-id.
 
@@ -21582,17 +21638,18 @@ cp_parser_trailing_type_id (cp_parser *parser)
 
 static void
 cp_parser_type_specifier_seq (cp_parser* parser,
+			      cp_parser_flags flags,
 			      bool is_declaration,
 			      bool is_trailing_return,
 			      cp_decl_specifier_seq *type_specifier_seq)
 {
   bool seen_type_specifier = false;
-  cp_parser_flags flags = CP_PARSER_FLAGS_OPTIONAL;
   cp_token *start_token = NULL;
 
   /* Clear the TYPE_SPECIFIER_SEQ.  */
   clear_decl_specs (type_specifier_seq);
 
+  flags |= CP_PARSER_FLAGS_OPTIONAL;
   /* In the context of a trailing return type, enum E { } is an
      elaborated-type-specifier followed by a function-body, not an
      enum-specifier.  */
@@ -21698,12 +21755,15 @@ function_being_declared_is_template_p (cp_parser* parser)
      parameter-declaration-list [opt] ... [opt]
      parameter-declaration-list , ...
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    Returns a representation for the parameter declarations.  A return
    value of NULL indicates a parameter-declaration-clause consisting
    only of an ellipsis.  */
 
 static tree
-cp_parser_parameter_declaration_clause (cp_parser* parser)
+cp_parser_parameter_declaration_clause (cp_parser* parser,
+					cp_parser_flags flags)
 {
   tree parameters;
   cp_token *token;
@@ -21746,7 +21806,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
     }
 
   /* Parse the parameter-declaration-list.  */
-  parameters = cp_parser_parameter_declaration_list (parser);
+  parameters = cp_parser_parameter_declaration_list (parser, flags);
   /* If a parse error occurred while parsing the
      parameter-declaration-list, then the entire
      parameter-declaration-clause is erroneous.  */
@@ -21789,12 +21849,14 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
      parameter-declaration
      parameter-declaration-list , parameter-declaration
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    Returns a representation of the parameter-declaration-list, as for
    cp_parser_parameter_declaration_clause.  However, the
    `void_list_node' is never appended to the list.  */
 
 static tree
-cp_parser_parameter_declaration_list (cp_parser* parser)
+cp_parser_parameter_declaration_list (cp_parser* parser, cp_parser_flags flags)
 {
   tree parameters = NULL_TREE;
   tree *tail = &parameters;
@@ -21817,7 +21879,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser)
 
       /* Parse the parameter.  */
       parameter
-	= cp_parser_parameter_declaration (parser,
+	= cp_parser_parameter_declaration (parser, flags,
 					   /*template_parm_p=*/false,
 					   &parenthesized_p);
 
@@ -21949,6 +22011,8 @@ cp_parser_parameter_declaration_list (cp_parser* parser)
      decl-specifier-seq ... [opt] abstract-declarator [opt]
      decl-specifier-seq abstract-declarator [opt] = assignment-expression
 
+   The parser flags FLAGS is used to control type-specifier parsing.
+
    If TEMPLATE_PARM_P is TRUE, then this parameter-declaration
    declares a template parameter.  (In that case, a non-nested `>'
    token encountered during the parsing of the assignment-expression
@@ -21960,6 +22024,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser)
 
 static cp_parameter_declarator *
 cp_parser_parameter_declaration (cp_parser *parser,
+				 cp_parser_flags flags,
 				 bool template_parm_p,
 				 bool *parenthesized_p)
 {
@@ -21992,7 +22057,7 @@ cp_parser_parameter_declaration (cp_parser *parser,
   /* Parse the declaration-specifiers.  */
   cp_token *decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_NONE,
+				flags,
 				&decl_specifiers,
 				&declares_class_or_enum);
 
@@ -22051,6 +22116,7 @@ cp_parser_parameter_declaration (cp_parser *parser,
       declarator_token_start = token;
       declarator = cp_parser_declarator (parser,
 					 CP_PARSER_DECLARATOR_EITHER,
+					 CP_PARSER_FLAGS_NONE,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 parenthesized_p,
 					 /*member_p=*/false,
@@ -24024,7 +24090,8 @@ cp_parser_member_declaration (cp_parser* parser)
   /* Parse the decl-specifier-seq.  */
   decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_OPTIONAL,
+				(CP_PARSER_FLAGS_OPTIONAL
+				 | CP_PARSER_FLAGS_TYPENAME_OPTIONAL),
 				&decl_specifiers,
 				&declares_class_or_enum);
   /* Check for an invalid type-name.  */
@@ -24261,6 +24328,7 @@ cp_parser_member_declaration (cp_parser* parser)
 	      /* Parse the declarator.  */
 	      declarator
 		= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/true,
@@ -25132,7 +25200,8 @@ cp_parser_exception_declaration (cp_parser* parser)
     = G_("types may not be defined in exception-declarations");
 
   /* Parse the type-specifier-seq.  */
-  cp_parser_type_specifier_seq (parser, /*is_declaration=*/true,
+  cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE,
+				/*is_declaration=*/true,
 				/*is_trailing_return=*/false,
 				&type_specifiers);
   /* If it's a `)', then there is no declarator.  */
@@ -25140,6 +25209,7 @@ cp_parser_exception_declaration (cp_parser* parser)
     declarator = NULL;
   else
     declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_EITHER,
+				       CP_PARSER_FLAGS_NONE,
 				       /*ctor_dtor_or_conv_p=*/NULL,
 				       /*parenthesized_p=*/NULL,
 				       /*member_p=*/false,
@@ -26295,7 +26365,8 @@ cp_parser_requirement_parameter_list (cp_parser *parser)
   if (!parens.require_open (parser))
     return error_mark_node;
 
-  tree parms = cp_parser_parameter_declaration_clause (parser);
+  tree parms
+    = cp_parser_parameter_declaration_clause (parser, CP_PARSER_FLAGS_NONE);
 
   if (!parens.require_close (parser))
     return error_mark_node;
@@ -27049,6 +27120,16 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p)
 					    /*type_p=*/false,
 					    /*is_declaration=*/false));
 
+  /* Resolve the TYPENAME_TYPE, because the call above didn't do it.  */
+  if (nested_name_specifier
+      && TREE_CODE (nested_name_specifier) == TYPENAME_TYPE)
+    {
+      tree s = resolve_typename_type (nested_name_specifier,
+				      /*only_current_p=*/false);
+      if (TREE_CODE (s) != TYPENAME_TYPE)
+	nested_name_specifier = s;
+    }
+
   outside_class_specifier_p = (!at_class_scope_p ()
 			       || !TYPE_BEING_DEFINED (current_class_type)
 			       || friend_p);
@@ -27727,7 +27808,8 @@ cp_parser_single_declaration (cp_parser* parser,
      alternative.  */
   decl_spec_token_start = cp_lexer_peek_token (parser->lexer);
   cp_parser_decl_specifier_seq (parser,
-				CP_PARSER_FLAGS_OPTIONAL,
+				(CP_PARSER_FLAGS_OPTIONAL
+				 | CP_PARSER_FLAGS_TYPENAME_OPTIONAL),
 				&decl_specifiers,
 				&declares_class_or_enum);
   if (friend_p)
@@ -27817,6 +27899,7 @@ cp_parser_single_declaration (cp_parser* parser,
 	  || decl_specifiers.type != error_mark_node))
     {
       decl = cp_parser_init_declarator (parser,
+					CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
 				        &decl_specifiers,
 				        checks,
 				        /*function_definition_allowed_p=*/true,
@@ -29451,6 +29534,7 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi)
 		      int ctor_dtor_or_conv_p;
 		      cp_lexer_consume_token (parser->lexer);
 		      cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					    CP_PARSER_FLAGS_NONE,
 					    &ctor_dtor_or_conv_p,
 					    /*parenthesized_p=*/NULL,
 					    /*member_p=*/true,
@@ -29470,8 +29554,9 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi)
 		{
 		  cp_lexer_consume_token (parser->lexer);
 		  begin_scope (sk_function_parms, NULL_TREE);
-		  if (cp_parser_parameter_declaration_list (parser)
-		      == error_mark_node)
+		  tree t = cp_parser_parameter_declaration_list
+			    (parser, CP_PARSER_FLAGS_NONE);
+		  if (t == error_mark_node)
 		    error = true;
 		  pop_bindings_and_leave_scope ();
 		}
@@ -30486,7 +30571,8 @@ cp_parser_objc_method_tail_params_opt (cp_parser* parser, bool *ellipsisp,
 	}
 
       /* TODO: parse attributes for tail parameters.  */
-      parmdecl = cp_parser_parameter_declaration (parser, false, NULL);
+      parmdecl = cp_parser_parameter_declaration (parser, CP_PARSER_FLAGS_NONE,
+						  false, NULL);
       parm = grokdeclarator (parmdecl->declarator,
 			     &parmdecl->decl_specifiers,
 			     PARM, /*initialized=*/0,
@@ -30815,6 +30901,7 @@ cp_parser_objc_class_ivars (cp_parser* parser)
 	      /* Parse the declarator.  */
 	      declarator
 		= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					CP_PARSER_FLAGS_NONE,
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/false,
@@ -31168,7 +31255,8 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
 	{
 	  /* We have "@catch (NSException *exception)" or something
 	     like that.  Parse the parameter declaration.  */
-	  parm = cp_parser_parameter_declaration (parser, false, NULL);
+	  parm = cp_parser_parameter_declaration (parser, CP_PARSER_FLAGS_NONE,
+						  false, NULL);
 	  if (parm == NULL)
 	    parameter_declaration = error_mark_node;
 	  else
@@ -31374,6 +31462,7 @@ cp_parser_objc_struct_declaration (cp_parser *parser)
 
       /* Parse the declarator.  */
       declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					 CP_PARSER_FLAGS_NONE,
 					 NULL, NULL, false, false);
 
       /* Look for attributes that apply to the ivar.  */
@@ -35985,7 +36074,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
      cp_parser_condition, from whence the bulk of this is copied.  */
 
   cp_parser_parse_tentatively (parser);
-  cp_parser_type_specifier_seq (parser, /*is_declaration=*/true,
+  cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE,
+				/*is_declaration=*/true,
 				/*is_trailing_return=*/false,
 				&type_specifiers);
   if (cp_parser_parse_definitely (parser))
@@ -35997,6 +36087,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
 
       declarator = cp_parser_declarator (parser,
 					 CP_PARSER_DECLARATOR_NAMED,
+					 CP_PARSER_FLAGS_NONE,
 					 /*ctor_dtor_or_conv_p=*/NULL,
 					 /*parenthesized_p=*/NULL,
 					 /*member_p=*/false,
diff --git gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
index 02eb33643ac..f9b4477448b 100644
--- gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
+++ gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C
@@ -1,4 +1,4 @@
 // PR c++/59120
 // { dg-do compile { target c++11 } }
 
-template<typename T> using X = int T::T*;  // { dg-error "expected" }
+template<typename T> using X = int T::T*;  // { dg-error "expected|two or more" }
diff --git gcc/testsuite/g++.dg/cpp0x/decltype67.C gcc/testsuite/g++.dg/cpp0x/decltype67.C
index e8042ac59e7..f3bf9a24b04 100644
--- gcc/testsuite/g++.dg/cpp0x/decltype67.C
+++ gcc/testsuite/g++.dg/cpp0x/decltype67.C
@@ -3,5 +3,5 @@
 
 template<typename T> struct A
 {
-  void foo(decltype(T())::Y);	// { dg-error {decltype\(T\(\)\)::Y} }
+  void foo(decltype(T())::Y);	// { dg-error "decltype\\(T\\(\\)\\)::Y" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp1z/typename1.C gcc/testsuite/g++.dg/cpp1z/typename1.C
new file mode 100644
index 00000000000..4c598c839f2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/typename1.C
@@ -0,0 +1,117 @@
+// P0634R3
+// { dg-do compile { target c++17_only } }
+
+// (5.2.1) simple-declaration or a function-definition in namespace scope
+
+template<typename T>
+T::X fn1 (int); // { dg-error "need .typename." }
+
+template<typename T>
+T::X fn1 (int) // { dg-error "need .typename." }
+{
+  return 42;
+}
+
+template<typename T>
+T::X v1; // { dg-error "need .typename." }
+
+namespace N {
+  template<typename T>
+  T::X v2; // { dg-error "need .typename." }
+}
+
+// (5.2.2) member-declaration
+
+template<typename T>
+struct S {
+  [[noreturn]] T::X fn2 (); // { dg-error "need .typename." }
+  T::X fn3 (); // { dg-error "need .typename." }
+  T::X fn4 () { return 5; } // { dg-error "need .typename." }
+  T::X fn5 () final; // { dg-error "need .typename." }
+  T::X fn6 () = 0; // { dg-error "need .typename." }
+  T::X fn8 () override; // { dg-error "need .typename." }
+  T::X v3; // { dg-error "need .typename." }
+  T::X *v4; // { dg-error "need .typename." }
+  T::X v5[5]; // { dg-error "need .typename." }
+  T::X v6 = 0; // { dg-error "need .typename." }
+  T::X v7{0}; // { dg-error "need .typename.|;" }
+  T::X v8 : 16; // { dg-error "need .typename." }
+  static constexpr T::X v9 = 0; // { dg-error "need .typename." }
+  typedef T::X T2; // { dg-error "need .typename." }
+  friend T::X fn7<int> (); // { dg-error "need .typename." }
+  static inline T::X v10; // { dg-error "need .typename." }
+};
+
+// (5.2.3) parameter-declaration in a member-declaration,
+// unless that parameter-declaration appears in a default argument
+
+template<typename T>
+struct S2 {
+  friend int fn1<T::X> (); // { dg-error "" }
+  int fn2 (T::X p); // { dg-error "not a type" }
+  int fn5 (int = T::X);
+};
+
+// (5.2.4) parameter-declaration in a declarator of a function or function
+// template declaration whose declarator-id is qualified,
+// unless that parameter-declaration appears in a default argument
+template<typename T>
+int fn3 (T::X);
+
+template<typename T>
+int fn4 (T::X p) { return p; } // { dg-error "" }
+
+// (5.2.6) parameter-declaration of a (non-type) template-parameter
+
+template<typename T, T::X N> // { dg-error "not a type" }
+struct S3 { };
+
+// default argument of a type-parameter of a template
+template<typename T, typename U = T::X> // { dg-error "need .typename." }
+struct S4 { };
+
+// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast
+template<typename T>
+struct S5 {
+  void fn6 (T::X p) // { dg-error "not a type" }
+  {
+    int i = static_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = dynamic_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = reinterpret_cast<T::Y>(p); // { dg-error "need .typename." }
+    i = const_cast<T::Y>(p); // { dg-error "need .typename." }
+  }
+};
+
+template<typename T>
+void fn7 (T::X p) // { dg-error "" }
+{
+  int i = static_cast<T::Y>(p);
+  i = dynamic_cast<T::Y>(p);
+  i = reinterpret_cast<T::Y>(p);
+  i = const_cast<T::Y>(p);
+}
+
+// new-type-id
+template<typename T>
+void
+fn8 ()
+{
+  new T::X[10]; // { dg-error "need .typename." }
+}
+
+// defining-type-id
+
+template<typename T>
+struct W { typedef int M; };
+
+template<typename T>
+struct S6 {
+  using TT = T::X; // { dg-error "need .typename." }
+  using TT2 = W<T>::M; // { dg-error "need .typename." }
+};
+
+// trailing-return-type
+template<typename T>
+struct S7 {
+  auto fn9() -> W<T>::M; // { dg-error "need .typename." }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename1.C gcc/testsuite/g++.dg/cpp2a/typename1.C
new file mode 100644
index 00000000000..833d3b86093
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename1.C
@@ -0,0 +1,42 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// OK, return type of a function declaration at global scope.
+template<class T> T::R f();
+
+// Ill-formed (no diagnostic required), attempt to declare
+// a void variable template
+template<class T> void f(T::R);
+
+template <class T> struct A;
+template <class T> using B = A<T>::U;
+
+template<typename T>
+struct PtrTraits { typedef int Ptr; };
+
+template<class T> struct S {
+  // OK, in a defining-type-id.
+  using Ptr = PtrTraits<T>::Ptr;
+
+  // OK, trailing-return-type.
+  auto g() -> S<T*>::Ptr;
+
+  // OK, class scope
+  T::R
+  f (T::P p)
+  {
+    // OK, type-id of a static_cast
+    return static_cast<T::R>(p);
+  }
+};
+
+template<typename T>
+void f ()
+{
+  // Variable pf of type void* initialized with T::X
+  void (*pf)(T::X);
+
+  // Error: T::X at block scope does not denote a type
+  // (attempt to declare a void variable)
+  void g(T::X); // { dg-error "declared void" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename10.C gcc/testsuite/g++.dg/cpp2a/typename10.C
new file mode 100644
index 00000000000..fa2cd000b5d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename10.C
@@ -0,0 +1,20 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+namespace N {
+ // template<typename T> extern T::type v; // #1a
+  template<typename T> T::type v(typename T::value); // #1b
+}
+template<typename T> T::type N::v(T::value); // #2
+
+namespace N2 {
+  template<typename T> extern T::type v; // #1a
+  //template<typename T> T::type v(typename T::value); // #1b
+}
+template<typename T> T::type N2::v(T::value); // { dg-error "" }
+
+namespace A {
+  inline namespace B { template<typename T> int f(typename T::foo); }
+  inline namespace C { template<typename T> extern int f; }
+}
+template<typename T> int A::f(T::foo); // { dg-error "ambiguous" }
diff --git gcc/testsuite/g++.dg/cpp2a/typename11.C gcc/testsuite/g++.dg/cpp2a/typename11.C
new file mode 100644
index 00000000000..ed7ad958f62
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename11.C
@@ -0,0 +1,17 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+struct A
+{
+  typedef int I;
+};
+
+template<typename T> struct B
+{
+  A<T>::I i;
+  typename A<T>::I i2;
+
+  A<int>::I i3;
+  typename A<int>::I i4;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename2.C gcc/testsuite/g++.dg/cpp2a/typename2.C
new file mode 100644
index 00000000000..7c926177004
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename2.C
@@ -0,0 +1,22 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T> typename T::R f();
+
+template <class T> struct A;
+template <class T> using B = typename A<T>::U;
+
+template<typename T>
+struct PtrTraits { typedef int Ptr; };
+
+template<class T> struct S {
+  using Ptr = typename PtrTraits<T>::Ptr;
+
+  auto g() -> typename S<T*>::Ptr;
+
+  typename T::R
+  f (typename T::P p)
+  {
+    return static_cast<typename T::R>(p);
+  }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename3.C gcc/testsuite/g++.dg/cpp2a/typename3.C
new file mode 100644
index 00000000000..e64aa0316ef
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename3.C
@@ -0,0 +1,23 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T>
+void f(int i)
+{
+  T::x * i; // { dg-error "dependent-name" }
+}
+
+struct Foo {
+  typedef int x;
+};
+
+struct Bar {
+  static int const x = 5;
+};
+
+int
+main ()
+{
+  f<Bar>(1);
+  f<Foo>(1);
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename4.C gcc/testsuite/g++.dg/cpp2a/typename4.C
new file mode 100644
index 00000000000..69154e7daaf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename4.C
@@ -0,0 +1,8 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<class T>
+struct A {
+  typedef int B;
+  B b;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename5.C gcc/testsuite/g++.dg/cpp2a/typename5.C
new file mode 100644
index 00000000000..97c27adafcc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename5.C
@@ -0,0 +1,65 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+struct X {
+  template<typename T>
+  struct N { };
+};
+
+template <typename T>
+struct Y {
+  template<typename U>
+  struct N { };
+};
+
+template <typename T>
+struct A
+{
+  template <typename U>
+  struct N { };
+
+  typedef typename A::template N<int> a1;
+  typedef typename A::template N<T> a2;
+  typename A::template N<int> a3;
+  typename A::template N<T> a4;
+  A::template N<int> a9;
+  A::template N<T> a10;
+  typedef A<T>::template N<int> a13;
+  typedef A<T>::template N<T> a14;
+
+  typedef typename X::template N<int> x1;
+  typedef typename X::template N<T> x2;
+  typename X::template N<int> x3;
+  typename X::template N<T> x4;
+  typedef X::N<int> x5;
+  typedef X::N<T> x6;
+  typedef typename X::N<int> x7;
+  typedef typename X::N<T> x8;
+  X::N<int> x9;
+  X::N<T> x10;
+  typename X::N<int> x11;
+  typename X::N<T> x12;
+
+  typedef typename Y<int>::template N<int> y1;
+  typedef typename Y<int>::template N<T> y2;
+  typedef typename Y<T>::template N<int> y3;
+  typedef typename Y<T>::template N<T> y4;
+  typename Y<int>::template N<int> y5;
+  typename Y<int>::template N<T> y6;
+  typename Y<T>::template N<int> y7;
+  typename Y<T>::template N<T> y8;
+  typedef Y<int>::N<int> y9;
+  typedef Y<int>::N<T> y10;
+  typedef Y<T>::template N<int> y11;
+  typedef Y<T>::template N<T> y12;
+  typedef typename Y<int>::N<int> y13;
+  typedef typename Y<int>::N<T> y14;
+  Y<int>::N<int> y17;
+  Y<int>::N<T> y18;
+  typename Y<int>::N<int> y21;
+  typename Y<int>::N<T> y22;
+  typedef Y<int>::N<int> y25;
+  typedef Y<int>::N<T> y26;
+  typedef Y<T>::template N<int> y27;
+  typedef Y<T>::template N<T> y28;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename6.C gcc/testsuite/g++.dg/cpp2a/typename6.C
new file mode 100644
index 00000000000..49e2235a53d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename6.C
@@ -0,0 +1,126 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// (5.2.1) simple-declaration or a function-definition in namespace scope
+
+template<typename T>
+T::X fn1 (int);
+
+template<typename T>
+T::X fn1 (int)
+{
+  return 42;
+}
+
+template<typename T>
+T::X v1;
+
+namespace N {
+  template<typename T>
+  T::X v2;
+}
+
+// (5.2.2) member-declaration
+
+template<typename T>
+struct S {
+  [[noreturn]] T::X fn2 ();
+  T::X fn3 ();
+  T::X fn4 () { return 5; }
+  T::X fn5 () final;
+  T::X fn6 () = 0;
+  T::X fn8 () override;
+  T::X v3;
+  T::X *v4;
+  T::X v5[5];
+  T::X v6 = 0;
+  T::X v7{0};
+  T::X v8 : 16;
+  static constexpr T::X v9 = 0;
+  typedef T::X T2;
+  friend T::X fn7<int> ();
+  static inline T::X v10;
+};
+
+// (5.2.3) parameter-declaration in a member-declaration,
+// unless that parameter-declaration appears in a default argument
+
+template<typename T>
+struct S2 {
+  friend int fn1<T::X> ();
+  int fn2 (T::X p);
+  int fn5 (int = T::X);
+};
+
+// (5.2.4) parameter-declaration in a declarator of a function or function
+// template declaration whose declarator-id is qualified,
+// unless that parameter-declaration appears in a default argument
+template<typename T>
+int fn3 (T::X);
+
+template<typename T>
+int fn4 (T::X p) { return p; }
+
+// (5.2.5) parameter-declaration in a lambda-declarator,
+// unless that parameter-declaration appears in a default argument
+
+void
+fn5 ()
+{
+  auto j = []<typename T>(T::X t, int i) { return i; };
+}
+
+// (5.2.6) parameter-declaration of a (non-type) template-parameter
+
+template<typename T, T::X N>
+struct S3 { };
+
+// default argument of a type-parameter of a template
+template<typename T, typename U = T::X>
+struct S4 { };
+
+// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast
+template<typename T>
+struct S5 {
+  void fn6 (T::X p)
+  {
+    int i = static_cast<T::Y>(p);
+    i = dynamic_cast<T::Y>(p);
+    i = reinterpret_cast<T::Y>(p);
+    i = const_cast<T::Y>(p);
+  }
+};
+
+template<typename T>
+void fn7 (T::X p)
+{
+  int i = static_cast<T::Y>(p);
+  i = dynamic_cast<T::Y>(p);
+  i = reinterpret_cast<T::Y>(p);
+  i = const_cast<T::Y>(p);
+}
+
+// new-type-id
+template<typename T>
+void
+fn8 ()
+{
+  new T::X[10];
+}
+
+// defining-type-id
+
+template<typename T>
+struct W { typedef int M; };
+
+template<typename T>
+struct S6 {
+  using TT = T::X;
+  using TT2 = W<T>::M;
+};
+
+// trailing-return-type
+template<typename T>
+struct S7 {
+  auto fn9() -> W<T>::M;
+};
diff --git gcc/testsuite/g++.dg/cpp2a/typename7.C gcc/testsuite/g++.dg/cpp2a/typename7.C
new file mode 100644
index 00000000000..713db51d972
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename7.C
@@ -0,0 +1,26 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+// Not in namespace scope.
+template<typename T>
+void fn1 ()
+{
+  // init-statement -> simple-declaration
+  if (T::X r = 0; 0) // { dg-error "need .typename.|expected" }
+    ;
+
+  for (T::X g = 0; ;) // { dg-error "need .typename.|expected" }
+    ;
+}
+
+template<typename T>
+void
+fn2 ()
+{
+  T::X fn3 (); // { dg-error "need .typename.|expected" }
+  T::X v1; // { dg-error "need .typename.|expected" }
+  T::X v2 = 0; // { dg-error "need .typename.|expected" }
+  T::X v3{0}; // { dg-error "need .typename.|expected" }
+  static constexpr T::X v4 = 0; // { dg-error "need .typename." }
+  typedef T::X T2; // { dg-error "need .typename." }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename8.C gcc/testsuite/g++.dg/cpp2a/typename8.C
new file mode 100644
index 00000000000..3ebfde45ec5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename8.C
@@ -0,0 +1,20 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+struct S {
+  static int foo ();
+};
+
+template<typename T>
+int
+f ()
+{
+  return S<T>::foo();
+}
+
+void
+test ()
+{
+  f<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/typename9.C gcc/testsuite/g++.dg/cpp2a/typename9.C
new file mode 100644
index 00000000000..7b1865222b3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/typename9.C
@@ -0,0 +1,12 @@
+// P0634R3
+// { dg-do compile { target c++2a } }
+// { dg-options "-fconcepts" }
+
+template <typename, typename> class A { class B; };
+
+template <typename T, typename U> class A<T, U>::B {
+  B(A &);
+};
+
+template <typename T, typename U>
+A<T, U>::B::B(A<T, U> &) {}
diff --git gcc/testsuite/g++.dg/diagnostic/missing-typename.C gcc/testsuite/g++.dg/diagnostic/missing-typename.C
index 21d1ed18a60..b088bebf92f 100644
--- gcc/testsuite/g++.dg/diagnostic/missing-typename.C
+++ gcc/testsuite/g++.dg/diagnostic/missing-typename.C
@@ -1,4 +1,5 @@
 // fix-it hint for missing "typename" (PR c++/63392)
+// { dg-do compile { target c++17_down } }
 // { dg-options "-fdiagnostics-show-caret" }
 
 template<typename T>
diff --git gcc/testsuite/g++.dg/other/crash-9.C gcc/testsuite/g++.dg/other/crash-9.C
index 0953fcbc46b..f5c4b2af329 100644
--- gcc/testsuite/g++.dg/other/crash-9.C
+++ gcc/testsuite/g++.dg/other/crash-9.C
@@ -11,5 +11,5 @@ class A
   };
 };
 template <typename T> template <typename U>
-A<T>::B<U> A<T>::B<U>::foo() {}
+A<T>::template B<U> A<T>::B<U>::foo() {}
 
diff --git gcc/testsuite/g++.dg/other/nontype-1.C gcc/testsuite/g++.dg/other/nontype-1.C
index 11bbfb82968..8d90c322a7e 100644
--- gcc/testsuite/g++.dg/other/nontype-1.C
+++ gcc/testsuite/g++.dg/other/nontype-1.C
@@ -1,7 +1,7 @@
 template <class Op>
 bool asfun(Op f,
-           Op::first_argument_type a, // { dg-error "not a type" }
-           Op::second_argument_type b) // { dg-error "not a type" }
+           Op::first_argument_type a, // { dg-error "not a type" "" { target c++17_down } }
+           Op::second_argument_type b) // { dg-error "not a type" "" { target c++17_down } }
 {
    return Op(a, b);
 }
diff --git gcc/testsuite/g++.dg/parse/crash13.C gcc/testsuite/g++.dg/parse/crash13.C
index 3c298ec8ede..7a4939a462d 100644
--- gcc/testsuite/g++.dg/parse/crash13.C
+++ gcc/testsuite/g++.dg/parse/crash13.C
@@ -12,11 +12,11 @@ struct A
 };
 
 template <typename T> 
-void func(A<T>::B* )	// { dg-error "variable|template|expression" }
+void func(A<T>::B* )	// { dg-error "variable|template|expression" "" { target c++17_down } }
 {
 }
 
 int main() 
 {
-  func<void>(0);	// { dg-error "not declared|expression|;" }
+  func<void>(0);	// { dg-error "not declared|expression|;" "" { target c++17_down } }
 }
diff --git gcc/testsuite/g++.dg/parse/error36.C gcc/testsuite/g++.dg/parse/error36.C
index 46080b4835d..7e52d1537e0 100644
--- gcc/testsuite/g++.dg/parse/error36.C
+++ gcc/testsuite/g++.dg/parse/error36.C
@@ -25,7 +25,7 @@ template <class T> struct B
 
 // PR c++/40738
 template <class T>
-void g(const A<T>::type &t);	// { dg-error "typename" "typename" }
+void g(const A<T>::type &t);	// { dg-error "typename" "" { target c++17_down } }
 
 // PR c++/18451
-template <class T> A<T>::B A<T>::b; // { dg-error "typename" }
+template <class T> A<T>::B A<T>::b; // { dg-error "typename" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/parse/no-typename1.C gcc/testsuite/g++.dg/parse/no-typename1.C
index 42059ce7b80..711c621e26a 100644
--- gcc/testsuite/g++.dg/parse/no-typename1.C
+++ gcc/testsuite/g++.dg/parse/no-typename1.C
@@ -6,6 +6,6 @@ template <typename T> struct A
 {
     template <typename U> struct B
     {
-        A<T>::template B<U> foo(); // { dg-error "" }
+        A<T>::template B<U> foo(); // { dg-error "" "" { target c++17_down } }
     };
 };
diff --git gcc/testsuite/g++.dg/parse/typedef2.C gcc/testsuite/g++.dg/parse/typedef2.C
index 1cc1ea0d58f..fd7554e19ac 100644
--- gcc/testsuite/g++.dg/parse/typedef2.C
+++ gcc/testsuite/g++.dg/parse/typedef2.C
@@ -1,2 +1,2 @@
 template <typename T> struct B { typedef typename T::X X; };
-template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" }
+template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/parse/typename11.C gcc/testsuite/g++.dg/parse/typename11.C
index 22f300707c8..832913ffd67 100644
--- gcc/testsuite/g++.dg/parse/typename11.C
+++ gcc/testsuite/g++.dg/parse/typename11.C
@@ -10,7 +10,7 @@ template <int dim> struct Y : X<dim> {
 
 // note: I is nested type in X, not Y!
 template <int dim>
-Y<dim>::I::I () {}		// { dg-error "dependent typedef" "typedef" }
-// { dg-error "no type|dependent type" "no type" { target *-*-* } .-1 }
+Y<dim>::I::I () {}		// { dg-error "expected|dependent typedef" "typedef" }
+// { dg-error "no type|dependent type" "no type" { target c++17_down } .-1 }
 
 template struct Y<1>;
diff --git gcc/testsuite/g++.dg/template/crash48.C gcc/testsuite/g++.dg/template/crash48.C
index 6aa3aa3ed26..acbe4342542 100644
--- gcc/testsuite/g++.dg/template/crash48.C
+++ gcc/testsuite/g++.dg/template/crash48.C
@@ -7,4 +7,4 @@ template<typename T> struct A
   typedef typename T::X X;
 };
 
-template<typename T> A<T>::X::X() {} // { dg-error "no type|invalid use|not a type|dependent" }
+template<typename T> A<T>::X::X() {} // { dg-error "expected|no type|invalid use|not a type|dependent" }
diff --git gcc/testsuite/g++.dg/template/dependent-name5.C gcc/testsuite/g++.dg/template/dependent-name5.C
index 681060c7002..fc78983324b 100644
--- gcc/testsuite/g++.dg/template/dependent-name5.C
+++ gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -17,13 +17,15 @@ struct A
   typedef Bar          type1;
   typedef A::Bar       type2;
   typedef A<T>::Bar    type3;
-  typedef A<T*>::Bar    type4;  // { dg-error "" }
+  typedef A<T*>::Bar    type4;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::Bar type5;
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
+// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-  typedef A<T*>::template N<int> type9;  // { dg-error "" }
+// { dg-error "" "" { target c++2a } .-1 }
+  typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
   typedef D Bar2;
@@ -36,7 +38,7 @@ struct A
 
   typedef A::N2 type12;
   typedef typename type12::K k2;
-  typedef type12::K k1;  // { dg-error "" }
+  typedef type12::K k1;  // { dg-error "" "" { target c++17_down } }
 
   // Check that A::Bar2 is not considered dependent even if we use
   // the typename keyword.
diff --git gcc/testsuite/g++.dg/template/error29.C gcc/testsuite/g++.dg/template/error29.C
index 2e2291d7e87..6e335487224 100644
--- gcc/testsuite/g++.dg/template/error29.C
+++ gcc/testsuite/g++.dg/template/error29.C
@@ -1,5 +1,5 @@
 // PR c++/33209
 
-template<typename T> void foo(int, T::x); // { dg-error "T::x" }
+template<typename T> void foo(int, T::x); // { dg-error "T::x" "" { target c++17_down } }
 
-template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" }
+template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" "" { target c++17_down } }
diff --git gcc/testsuite/g++.dg/template/nested5.C gcc/testsuite/g++.dg/template/nested5.C
index 3850fdace3a..e3e871e6d85 100644
--- gcc/testsuite/g++.dg/template/nested5.C
+++ gcc/testsuite/g++.dg/template/nested5.C
@@ -6,7 +6,7 @@ template <typename T> struct A
   {
     template <typename U> struct D {};
   };
-  template <typename S> static C::D<S> bar (S const &);
+  template <typename S> static C::template D<S> bar (S const &);
 };
 
 struct E {};
diff --git gcc/testsuite/g++.dg/template/pr84789.C gcc/testsuite/g++.dg/template/pr84789.C
index 63b9832fecf..bdf80dc3edf 100644
--- gcc/testsuite/g++.dg/template/pr84789.C
+++ gcc/testsuite/g++.dg/template/pr84789.C
@@ -9,5 +9,5 @@ template<typename> struct B : A {};
 
 template<typename T> struct C : B<T>
 {
-  B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" }
+  B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/template/static30.C gcc/testsuite/g++.dg/template/static30.C
index 07dafe23ffa..8b8637a1abe 100644
--- gcc/testsuite/g++.dg/template/static30.C
+++ gcc/testsuite/g++.dg/template/static30.C
@@ -6,5 +6,5 @@ template <int> struct A
   static const int i2;
 };
 
-template <int N> const int A<N>::i1(A<N>::i);
+template <int N> const int A<N>::i1(A<N>::i); // { dg-error "no declaration matches" "" { target c++2a } }
 template <int N> const int A<N>::i2(3, A<N>::i); // { dg-error "expression list" }
diff --git gcc/testsuite/g++.dg/template/typedef6.C gcc/testsuite/g++.dg/template/typedef6.C
index c95945966fa..a6202b55181 100644
--- gcc/testsuite/g++.dg/template/typedef6.C
+++ gcc/testsuite/g++.dg/template/typedef6.C
@@ -5,4 +5,4 @@ template<typename T> struct A
   typedef struct typename T::X X;       // { dg-error "expected identifier|two or more" }
 };
 
-template<typename T> A<T>::X::X() {}    // { dg-error "not a type|forbids declaration|invalid use of" }
+template<typename T> A<T>::X::X() {}    // { dg-error "expected|not a type|forbids declaration|invalid use of" }
diff --git gcc/testsuite/g++.dg/template/typename3.C gcc/testsuite/g++.dg/template/typename3.C
index 0ad9a2a0c41..ae10af27763 100644
--- gcc/testsuite/g++.dg/template/typename3.C
+++ gcc/testsuite/g++.dg/template/typename3.C
@@ -3,5 +3,5 @@
 
 template <class A>
 struct B {
- typedef A::C::D E;  // { dg-error "" }
+ typedef A::C::D E;  // { dg-error "" "" { target c++17_down } }
 };

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

* Re: C++ PATCH to implement C++20 P0634R3, Down with typename!
  2018-11-30 23:08   ` Marek Polacek
@ 2018-12-01 15:57     ` Jason Merrill
  2018-12-01 16:26       ` Marek Polacek
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2018-12-01 15:57 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On 11/30/18 6:08 PM, Marek Polacek wrote:
> +   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in this
> +   context.  */
...
> +   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
> +   this context.

You have a couple of these left over from the previous patch.  OK with 
that fixed.

Jason

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

* Re: C++ PATCH to implement C++20 P0634R3, Down with typename!
  2018-12-01 15:57     ` Jason Merrill
@ 2018-12-01 16:26       ` Marek Polacek
  0 siblings, 0 replies; 5+ messages in thread
From: Marek Polacek @ 2018-12-01 16:26 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Sat, Dec 01, 2018 at 10:57:12AM -0500, Jason Merrill wrote:
> On 11/30/18 6:08 PM, Marek Polacek wrote:
> > +   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in this
> > +   context.  */
> ...
> > +   TYPENAME_OPTIONAL_P is true if the typename keyword is optional in
> > +   this context.
> 
> You have a couple of these left over from the previous patch.  OK with that
> fixed.

One day I'll learn how to use grep.

Thanks for the reviews!

Marek

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

end of thread, other threads:[~2018-12-01 16:26 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-12 15:28 C++ PATCH to implement C++20 P0634R3, Down with typename! Marek Polacek
2018-11-28 22:09 ` Jason Merrill
2018-11-30 23:08   ` Marek Polacek
2018-12-01 15:57     ` Jason Merrill
2018-12-01 16:26       ` Marek Polacek

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).