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 <pola...@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. 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;