C++ PATCH for c++/91673 - ICE with noexcept in alias-declaration

Message ID 20190907203735.GE14737@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/91673 - ICE with noexcept in alias-declaration
Related show

Commit Message

Marek Polacek Sept. 7, 2019, 8:37 p.m.
Here we ICE with

  template<bool B> struct S {
    using U = void() noexcept(B);
  };
  S<true> s;

since the delayed noexcept parsing patch.  The problem is that we create
a DEFERRED_PARSE node for the noexcept-specifier, but we never put the
whole declaration into unparsed_noexcepts (cp_parser_save_default_args is
never called here, so we never performed late parsing.  That then crashes
in tsubst_decl when instantiating because tsubst_decl doesn't know what to
do with DEFERRED_PARSE.

That could be fixed but then we have a problem with

  struct S {
    using T = void(int p) noexcept(noexcept(p));
  };

because the TYPE_DECL only has a function type, not a declaration, so we
can't push the PARM_DECL p into the scope, and lookup fails.  However, I
noticed that neither clang or icc perform delayed parsing so this fails:

  struct S {
    using T = void() noexcept(noexcept(i));
    int i;
  };

So instead I opted for this approach, don't delay parsing for such noexcept
specifications, much like we do for friend declarations.  And similarly for
a typedef declaration.

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

2019-09-07  Marek Polacek  <polacek@redhat.com>

	PR c++/91673 - ICE with noexcept in alias-declaration.
	* parser.c (CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT): New parser flag.
	(cp_parser_lambda_declarator_opt): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_exception_specification_opt.
	(cp_parser_alias_declaration): Pass CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT
	to cp_parser_type_id.
	(cp_parser_direct_declarator): Adjust a call to
	cp_parser_exception_specification_opt.  Pass parser flags to it, with
	CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT if FRIEND_P.
	(cp_parser_type_id_1): Maybe pass CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT to
	cp_parser_declarator.
	(cp_parser_member_declaration): Pass CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT
	to cp_parser_declarator for a typedef declaration.
	(cp_parser_late_noexcept_specifier): Adjust a call to
	cp_parser_noexcept_specification_opt.
	(cp_parser_noexcept_specification_opt): New parameter for parser flags,
	drop the FRIEND_P parameter.  Use the new parameter.
	(cp_parser_exception_specification_opt): Likewise.
	(cp_parser_transaction): Adjust a call to
	cp_parser_noexcept_specification_opt.
	(cp_parser_transaction_expression): Likewise.

	* g++.dg/cpp1z/using7.C: New test.
	* g++.dg/cpp1z/using8.C: New test.

Comments

Jason Merrill Sept. 9, 2019, 8:56 p.m. | #1
On 9/7/19 3:37 PM, Marek Polacek wrote:
> 	* parser.c (CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT): New parser flag.


Is it feasible to reverse this, and specifically delay parsing of 
noexcept-specifications on member function declarations?  When else 
would we want it?

Jason
Marek Polacek Sept. 10, 2019, 6:25 p.m. | #2
On Mon, Sep 09, 2019 at 04:56:18PM -0400, Jason Merrill wrote:
> On 9/7/19 3:37 PM, Marek Polacek wrote:

> > 	* parser.c (CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT): New parser flag.

> 

> Is it feasible to reverse this, and specifically delay parsing of

> noexcept-specifications on member function declarations?  When else would we

> want it?


Yes, that's easy enough: we can handle that when parsing a member-declaration,
just disallow it for friend declarations and when we saw 'typedef'.
Most of the patch is the same.  And we don't need to mess with anything when
parsing an alias-decl, now that the default is not to delay.

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

2019-09-10  Marek Polacek  <polacek@redhat.com>

	PR c++/91673 - ICE with noexcept in alias-declaration.
	* parser.c (CP_PARSER_FLAGS_DELAY_NOEXCEPT): New parser flag.
	(cp_parser_lambda_declarator_opt): Pass CP_PARSER_FLAGS_NONE to
	cp_parser_exception_specification_opt.
	(cp_parser_direct_declarator): Adjust a call to
	cp_parser_exception_specification_opt.
	(cp_parser_type_id_1): Maybe pass CP_PARSER_FLAGS_DELAY_NOEXCEPT to
	cp_parser_declarator.
	(cp_parser_member_declaration): Pass CP_PARSER_FLAGS_DELAY_NOEXCEPT
	to cp_parser_declarator if not processing a friend or typedef
	declaration.
	(cp_parser_late_noexcept_specifier): Adjust a call to
	cp_parser_noexcept_specification_opt.
	(cp_parser_noexcept_specification_opt): New parameter for parser flags,
	drop the FRIEND_P parameter.  Use the new parameter.
	(cp_parser_exception_specification_opt): Likewise.
	(cp_parser_transaction): Adjust a call to
	cp_parser_noexcept_specification_opt.
	(cp_parser_transaction_expression): Likewise.

	* g++.dg/cpp1z/using7.C: New test.
	* g++.dg/cpp1z/using8.C: New test.

diff --git gcc/cp/parser.c gcc/cp/parser.c
index baa60b8834e..eaa3765269e 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,8 +247,6 @@ static void cp_lexer_stop_debugging
 
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
-static tree cp_parser_noexcept_specification_opt
-  (cp_parser *, bool, bool *, bool, bool);
 static tree cp_parser_late_noexcept_specifier
   (cp_parser *, tree);
 static void noexcept_override_late_checks
@@ -1830,7 +1828,9 @@ enum
   /* When parsing a decl-specifier-seq, only allow mutable or constexpr.  */
   CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
   /* When parsing a decl-specifier-seq, allow missing typename.  */
-  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20
+  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20,
+  /* When parsing of the noexcept-specifier should be delayed.  */
+  CP_PARSER_FLAGS_DELAY_NOEXCEPT = 0x40
 };
 
 /* This type is used for parameters and variables which hold
@@ -2380,7 +2380,7 @@ static void cp_parser_explicit_instantiation
 static void cp_parser_explicit_specialization
   (cp_parser *);
 
-/* Exception handling [gram.exception] */
+/* Exception handling [gram.except] */
 
 static tree cp_parser_try_block
   (cp_parser *);
@@ -2395,9 +2395,11 @@ static tree cp_parser_exception_declaration
 static tree cp_parser_throw_expression
   (cp_parser *);
 static tree cp_parser_exception_specification_opt
-  (cp_parser *, bool = false);
+  (cp_parser *, cp_parser_flags);
 static tree cp_parser_type_id_list
   (cp_parser *);
+static tree cp_parser_noexcept_specification_opt
+  (cp_parser *, cp_parser_flags, bool, bool *, bool);
 
 /* GNU Extensions */
 
@@ -10938,7 +10940,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       tx_qual = cp_parser_tx_qualifier_opt (parser);
 
       /* Parse optional exception specification.  */
-      exception_spec = cp_parser_exception_specification_opt (parser);
+      exception_spec
+	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
 
       std_attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -20877,7 +20880,7 @@ cp_parser_direct_declarator (cp_parser* parser,
 		  tree tx_qual = cp_parser_tx_qualifier_opt (parser);
 		  /* And the exception-specification.  */
 		  exception_specification
-		    = cp_parser_exception_specification_opt (parser, friend_p);
+		    = cp_parser_exception_specification_opt (parser, flags);
 
 		  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -21870,10 +21873,16 @@ cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags,
 
   /* There might or might not be an abstract declarator.  */
   cp_parser_parse_tentatively (parser);
+  /* Reset the flags, but remember if we should perform delayed parsing
+     of noexcept-specifiers.  */
+  if (flags & CP_PARSER_FLAGS_DELAY_NOEXCEPT)
+    flags = CP_PARSER_FLAGS_DELAY_NOEXCEPT;
+  else
+    flags = CP_PARSER_FLAGS_NONE;
   /* Look for the declarator.  */
   abstract_declarator
     = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT,
-			    CP_PARSER_FLAGS_NONE, NULL,
+			    flags, NULL,
 			    /*parenthesized_p=*/NULL,
 			    /*member_p=*/false,
 			    /*friend_p=*/false,
@@ -24780,11 +24789,15 @@ cp_parser_member_declaration (cp_parser* parser)
 	      tree asm_specification;
 	      int ctor_dtor_or_conv_p;
 	      bool static_p = (decl_specifiers.storage_class == sc_static);
+	      cp_parser_flags flags = CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
+	      if (!friend_p
+		  && !decl_spec_seq_has_spec_p (&decl_specifiers, ds_typedef))
+		flags |= CP_PARSER_FLAGS_DELAY_NOEXCEPT;
 
 	      /* Parse the declarator.  */
 	      declarator
 		= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
-					CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+					flags,
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/true,
@@ -25359,10 +25372,10 @@ cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
   /* Parse the cached noexcept-specifier.  */
   tree parsed_arg
     = cp_parser_noexcept_specification_opt (parser,
+					    CP_PARSER_FLAGS_NONE,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false,
-					    /*friend_p=*/false);
+					    /*return_cond=*/false);
 
   /* Revert to the main lexer.  */
   cp_parser_pop_lexer (parser);
@@ -25411,15 +25424,15 @@ noexcept_override_late_checks (tree type, tree fndecl)
    expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
    there are no parentheses.  CONSUMED_EXPR will be set accordingly.
    Otherwise, returns a noexcept specification unless RETURN_COND is true,
-   in which case a boolean condition is returned instead.  If FRIEND_P is true,
-   the function with this noexcept-specification had the `friend' specifier.  */
+   in which case a boolean condition is returned instead.  The parser flags
+   FLAGS is used to control parsing.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
+				      cp_parser_flags flags,
 				      bool require_constexpr,
 				      bool* consumed_expr,
-				      bool return_cond,
-				      bool friend_p)
+				      bool return_cond)
 {
   cp_token *token;
   const char *saved_message;
@@ -25446,8 +25459,10 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
 	  /* No need to delay parsing for a number literal or true/false.  */
 	  && !literal_p
 	  && at_class_scope_p ()
-	  /* Don't delay parsing for friend member functions.  */
-	  && !friend_p
+	  /* We don't delay parsing for friend member functions,
+	     alias-declarations, and typedefs, even though the standard seems
+	     to require it.  */
+	  && (flags & CP_PARSER_FLAGS_DELAY_NOEXCEPT)
 	  && TYPE_BEING_DEFINED (current_class_type)
 	  && !LAMBDA_TYPE_P (current_class_type))
 	return cp_parser_save_noexcept (parser);
@@ -25522,11 +25537,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
      throw ( type-id-list [opt] )
 
    Returns a TREE_LIST representing the exception-specification.  The
-   TREE_VALUE of each node is a type.  If FRIEND_P is true, the function
-   with this noexcept-specification had the `friend' specifier.  */
+   TREE_VALUE of each node is a type.  The parser flags FLAGS is used to
+   control parsing.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
+cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
 {
   cp_token *token;
   tree type_id_list;
@@ -25537,11 +25552,10 @@ cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
 
   /* Is it a noexcept-specification?  */
   type_id_list
-    = cp_parser_noexcept_specification_opt (parser,
+    = cp_parser_noexcept_specification_opt (parser, flags,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false,
-					    friend_p);
+					    /*return_cond=*/false);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -41162,10 +41176,10 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
     }
   else
     noex = cp_parser_noexcept_specification_opt (parser,
+						 CP_PARSER_FLAGS_NONE,
 						 /*require_constexpr=*/true,
 						 /*consumed_expr=*/NULL,
-						 /*return_cond=*/true,
-						 /*friend_p=*/false);
+						 /*return_cond=*/true);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -41226,10 +41240,10 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
 
   /* Parse a noexcept specification.  */
   noex = cp_parser_noexcept_specification_opt (parser,
+					       CP_PARSER_FLAGS_NONE,
 					       /*require_constexpr=*/false,
 					       &noex_expr,
-					       /*return_cond=*/true,
-					       /*friend_p=*/false);
+					       /*return_cond=*/true);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git gcc/testsuite/g++.dg/cpp1z/using7.C gcc/testsuite/g++.dg/cpp1z/using7.C
new file mode 100644
index 00000000000..f22ac4584a9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/using7.C
@@ -0,0 +1,33 @@
+// PR c++/91673 - ICE with noexcept in alias-declaration.
+// { dg-do compile { target c++17 } }
+
+template<typename T, bool B>
+using U1 = T() noexcept(B);
+
+template<bool B>
+struct S {
+  int I;
+  static constexpr bool b = true;
+
+  template<typename T>
+  using U2 = T() noexcept(B);
+
+  template<typename T>
+  using U8 = T() noexcept(b);
+
+  template<typename T>
+  using U10 = T(int p) noexcept(noexcept(p));
+
+  template<typename T, bool B2>
+  using U11 = T() noexcept(B2);
+
+  using U3 = void() noexcept(B);
+  using U9 = void() noexcept(b);
+  using U4 = void() noexcept(noexcept (I));
+  using U5 = void(int p) noexcept(noexcept(p));
+
+  typedef void(*T1)() noexcept(B);
+  typedef void(*T2)(int p) noexcept(noexcept(p));
+};
+
+S<true> s;
diff --git gcc/testsuite/g++.dg/cpp1z/using8.C gcc/testsuite/g++.dg/cpp1z/using8.C
new file mode 100644
index 00000000000..c3e1a06b297
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/using8.C
@@ -0,0 +1,12 @@
+// PR c++/91673 - ICE with noexcept in alias-declaration.
+// { dg-do compile { target c++17 } }
+
+template<typename Sig>
+struct overload;
+
+template<typename Ret, typename... Args, bool NoExcept>
+struct overload<Ret(Args...) noexcept(NoExcept)> {
+    using signature_t = Ret(Args...) noexcept(NoExcept);
+};
+
+overload<void()> x;
Jason Merrill Sept. 10, 2019, 8:58 p.m. | #3
On 9/10/19 1:25 PM, Marek Polacek wrote:
> @@ -21870,10 +21873,16 @@ cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags,

>  

>    /* There might or might not be an abstract declarator.  */

>    cp_parser_parse_tentatively (parser);

> +  /* Reset the flags, but remember if we should perform delayed parsing

> +     of noexcept-specifiers.  */

> +  if (flags & CP_PARSER_FLAGS_DELAY_NOEXCEPT)

> +    flags = CP_PARSER_FLAGS_DELAY_NOEXCEPT;

> +  else

> +    flags = CP_PARSER_FLAGS_NONE;


Do we want/need to pass the flag through here?  I would think that we 
don't, for the same reason we don't want to delay parsing in an alias.

OK without this hunk.

Jason

Patch

diff --git gcc/cp/parser.c gcc/cp/parser.c
index baa60b8834e..38f5aadaf2f 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,8 +247,6 @@  static void cp_lexer_stop_debugging
 
 static cp_token_cache *cp_token_cache_new
   (cp_token *, cp_token *);
-static tree cp_parser_noexcept_specification_opt
-  (cp_parser *, bool, bool *, bool, bool);
 static tree cp_parser_late_noexcept_specifier
   (cp_parser *, tree);
 static void noexcept_override_late_checks
@@ -1830,7 +1828,9 @@  enum
   /* When parsing a decl-specifier-seq, only allow mutable or constexpr.  */
   CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
   /* When parsing a decl-specifier-seq, allow missing typename.  */
-  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20
+  CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20,
+  /* Don't delay parsing of the noexcept-specifier.  */
+  CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT = 0x40
 };
 
 /* This type is used for parameters and variables which hold
@@ -2380,7 +2380,7 @@  static void cp_parser_explicit_instantiation
 static void cp_parser_explicit_specialization
   (cp_parser *);
 
-/* Exception handling [gram.exception] */
+/* Exception handling [gram.except] */
 
 static tree cp_parser_try_block
   (cp_parser *);
@@ -2395,9 +2395,11 @@  static tree cp_parser_exception_declaration
 static tree cp_parser_throw_expression
   (cp_parser *);
 static tree cp_parser_exception_specification_opt
-  (cp_parser *, bool = false);
+  (cp_parser *, cp_parser_flags);
 static tree cp_parser_type_id_list
   (cp_parser *);
+static tree cp_parser_noexcept_specification_opt
+  (cp_parser *, cp_parser_flags, bool, bool *, bool);
 
 /* GNU Extensions */
 
@@ -10938,7 +10940,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       tx_qual = cp_parser_tx_qualifier_opt (parser);
 
       /* Parse optional exception specification.  */
-      exception_spec = cp_parser_exception_specification_opt (parser);
+      exception_spec
+	= cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
 
       std_attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -19674,7 +19677,9 @@  cp_parser_alias_declaration (cp_parser* parser)
 	G_("types may not be defined in alias template declarations");
     }
 
-  type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+  type = cp_parser_type_id (parser,
+			    (CP_PARSER_FLAGS_TYPENAME_OPTIONAL
+			     | CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT),
 			    &type_location);
 
   /* Restore the error message if need be.  */
@@ -20862,6 +20867,8 @@  cp_parser_direct_declarator (cp_parser* parser,
 		  /* 'this' is not allowed in static member functions.  */
 		  if (static_p || friend_p)
 		    parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+		  if (friend_p)
+		    flags |= CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT;
 
 		  is_declarator = true;
 
@@ -20877,7 +20884,7 @@  cp_parser_direct_declarator (cp_parser* parser,
 		  tree tx_qual = cp_parser_tx_qualifier_opt (parser);
 		  /* And the exception-specification.  */
 		  exception_specification
-		    = cp_parser_exception_specification_opt (parser, friend_p);
+		    = cp_parser_exception_specification_opt (parser, flags);
 
 		  attrs = cp_parser_std_attribute_spec_seq (parser);
 
@@ -21870,10 +21877,16 @@  cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags,
 
   /* There might or might not be an abstract declarator.  */
   cp_parser_parse_tentatively (parser);
+  /* Reset the flags, but remember if we need to avoid delaying parsing
+     of noexcept-specifiers.  */
+  if (flags & CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT)
+    flags = CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT;
+  else
+    flags = CP_PARSER_FLAGS_NONE;
   /* Look for the declarator.  */
   abstract_declarator
     = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT,
-			    CP_PARSER_FLAGS_NONE, NULL,
+			    flags, NULL,
 			    /*parenthesized_p=*/NULL,
 			    /*member_p=*/false,
 			    /*friend_p=*/false,
@@ -24780,11 +24793,14 @@  cp_parser_member_declaration (cp_parser* parser)
 	      tree asm_specification;
 	      int ctor_dtor_or_conv_p;
 	      bool static_p = (decl_specifiers.storage_class == sc_static);
+	      cp_parser_flags flags = CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
+	      if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_typedef))
+		flags |= CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT;
 
 	      /* Parse the declarator.  */
 	      declarator
 		= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
-					CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+					flags,
 					&ctor_dtor_or_conv_p,
 					/*parenthesized_p=*/NULL,
 					/*member_p=*/true,
@@ -25359,10 +25375,10 @@  cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
   /* Parse the cached noexcept-specifier.  */
   tree parsed_arg
     = cp_parser_noexcept_specification_opt (parser,
+					    CP_PARSER_FLAGS_NONE,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false,
-					    /*friend_p=*/false);
+					    /*return_cond=*/false);
 
   /* Revert to the main lexer.  */
   cp_parser_pop_lexer (parser);
@@ -25411,15 +25427,15 @@  noexcept_override_late_checks (tree type, tree fndecl)
    expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
    there are no parentheses.  CONSUMED_EXPR will be set accordingly.
    Otherwise, returns a noexcept specification unless RETURN_COND is true,
-   in which case a boolean condition is returned instead.  If FRIEND_P is true,
-   the function with this noexcept-specification had the `friend' specifier.  */
+   in which case a boolean condition is returned instead.  The parser flags
+   FLAGS is used to control parsing.  */
 
 static tree
 cp_parser_noexcept_specification_opt (cp_parser* parser,
+				      cp_parser_flags flags,
 				      bool require_constexpr,
 				      bool* consumed_expr,
-				      bool return_cond,
-				      bool friend_p)
+				      bool return_cond)
 {
   cp_token *token;
   const char *saved_message;
@@ -25446,8 +25462,10 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
 	  /* No need to delay parsing for a number literal or true/false.  */
 	  && !literal_p
 	  && at_class_scope_p ()
-	  /* Don't delay parsing for friend member functions.  */
-	  && !friend_p
+	  /* We don't delay parsing for friend member functions,
+	     alias-declarations, and typedefs, even though the standard seems
+	     to require it.  */
+	  && !(flags & CP_PARSER_FLAGS_NO_DELAY_NOEXCEPT)
 	  && TYPE_BEING_DEFINED (current_class_type)
 	  && !LAMBDA_TYPE_P (current_class_type))
 	return cp_parser_save_noexcept (parser);
@@ -25522,11 +25540,11 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
      throw ( type-id-list [opt] )
 
    Returns a TREE_LIST representing the exception-specification.  The
-   TREE_VALUE of each node is a type.  If FRIEND_P is true, the function
-   with this noexcept-specification had the `friend' specifier.  */
+   TREE_VALUE of each node is a type.  The parser flags FLAGS is used to
+   control parsing.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
+cp_parser_exception_specification_opt (cp_parser* parser, cp_parser_flags flags)
 {
   cp_token *token;
   tree type_id_list;
@@ -25537,11 +25555,10 @@  cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
 
   /* Is it a noexcept-specification?  */
   type_id_list
-    = cp_parser_noexcept_specification_opt (parser,
+    = cp_parser_noexcept_specification_opt (parser, flags,
 					    /*require_constexpr=*/true,
 					    /*consumed_expr=*/NULL,
-					    /*return_cond=*/false,
-					    friend_p);
+					    /*return_cond=*/false);
   if (type_id_list != NULL_TREE)
     return type_id_list;
 
@@ -41162,10 +41179,10 @@  cp_parser_transaction (cp_parser *parser, cp_token *token)
     }
   else
     noex = cp_parser_noexcept_specification_opt (parser,
+						 CP_PARSER_FLAGS_NONE,
 						 /*require_constexpr=*/true,
 						 /*consumed_expr=*/NULL,
-						 /*return_cond=*/true,
-						 /*friend_p=*/false);
+						 /*return_cond=*/true);
 
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -41226,10 +41243,10 @@  cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
 
   /* Parse a noexcept specification.  */
   noex = cp_parser_noexcept_specification_opt (parser,
+					       CP_PARSER_FLAGS_NONE,
 					       /*require_constexpr=*/false,
 					       &noex_expr,
-					       /*return_cond=*/true,
-					       /*friend_p=*/false);
+					       /*return_cond=*/true);
 
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git gcc/testsuite/g++.dg/cpp1z/using7.C gcc/testsuite/g++.dg/cpp1z/using7.C
new file mode 100644
index 00000000000..f22ac4584a9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/using7.C
@@ -0,0 +1,33 @@ 
+// PR c++/91673 - ICE with noexcept in alias-declaration.
+// { dg-do compile { target c++17 } }
+
+template<typename T, bool B>
+using U1 = T() noexcept(B);
+
+template<bool B>
+struct S {
+  int I;
+  static constexpr bool b = true;
+
+  template<typename T>
+  using U2 = T() noexcept(B);
+
+  template<typename T>
+  using U8 = T() noexcept(b);
+
+  template<typename T>
+  using U10 = T(int p) noexcept(noexcept(p));
+
+  template<typename T, bool B2>
+  using U11 = T() noexcept(B2);
+
+  using U3 = void() noexcept(B);
+  using U9 = void() noexcept(b);
+  using U4 = void() noexcept(noexcept (I));
+  using U5 = void(int p) noexcept(noexcept(p));
+
+  typedef void(*T1)() noexcept(B);
+  typedef void(*T2)(int p) noexcept(noexcept(p));
+};
+
+S<true> s;
diff --git gcc/testsuite/g++.dg/cpp1z/using8.C gcc/testsuite/g++.dg/cpp1z/using8.C
new file mode 100644
index 00000000000..c3e1a06b297
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/using8.C
@@ -0,0 +1,12 @@ 
+// PR c++/91673 - ICE with noexcept in alias-declaration.
+// { dg-do compile { target c++17 } }
+
+template<typename Sig>
+struct overload;
+
+template<typename Ret, typename... Args, bool NoExcept>
+struct overload<Ret(Args...) noexcept(NoExcept)> {
+    using signature_t = Ret(Args...) noexcept(NoExcept);
+};
+
+overload<void()> x;