From 78f7607db4c53f8cfe4653a6c91c0328bc2854c3 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Sat, 22 Jun 2019 15:14:30 +0000 Subject: [PATCH] PR c++/86476 - noexcept-specifier is a complete-class context. PR c++/52869 * cp-tree.def (DEFAULT_ARG): Update commentary. * cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro. (tree_default_arg): Use tree_base instead of tree_common. (do_push_parm_decls, maybe_check_overriding_exception_spec): Declare. * decl.c (do_push_parm_decls): New function, broken out of... (store_parm_decls): ...here. Call it. * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert. * parser.c (cp_parser_noexcept_specification_opt, cp_parser_late_noexcept_specifier, noexcept_override_late_checks): Forward-declare. (unparsed_noexcepts): New macro. (push_unparsed_function_queues): Update initializer. (cp_parser_direct_declarator): Pass FRIEND_P to cp_parser_exception_specification_opt. (inject_parm_decls): New. (pop_injected_parms): New. (cp_parser_class_specifier_1): Implement delayed parsing of noexcept-specifiers. (cp_parser_save_noexcept): New. (cp_parser_late_noexcept_specifier): New. (noexcept_override_late_checks): New. (cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call cp_parser_save_noexcept instead of the normal processing if needed. (cp_parser_exception_specification_opt): Add FRIEND_P parameter and pass it to cp_parser_noexcept_specification_opt. (cp_parser_save_member_function_body): Fix comment. (cp_parser_save_default_args): Maybe save the noexcept-specifier to post process. (cp_parser_transaction): Update call to cp_parser_noexcept_specification_opt. (cp_parser_transaction_expression): Likewise. * parser.h (cp_unparsed_functions_entry): Add new field to carry a noexcept-specifier. * pt.c (dependent_type_p_r): Handle unparsed noexcept expression. * search.c (maybe_check_overriding_exception_spec): New function, broken out of... (check_final_overrider): ...here. Call maybe_check_overriding_exception_spec. * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P. (cp_tree_equal): Handle DEFAULT_ARG. * g++.dg/cpp0x/noexcept45.C: New test. * g++.dg/cpp0x/noexcept46.C: New test. * g++.dg/cpp0x/noexcept47.C: New test. * g++.dg/cpp0x/noexcept48.C: New test. * g++.dg/cpp0x/noexcept49.C: New test. * g++.dg/cpp0x/noexcept50.C: New test. * g++.dg/cpp0x/noexcept51.C: New test. * g++.dg/cpp0x/noexcept52.C: New test. * g++.dg/cpp0x/noexcept53.C: New test. * g++.dg/eh/shadow1.C: Adjust dg-error. From-SVN: r272586 --- gcc/cp/ChangeLog | 43 +++++ gcc/cp/cp-tree.def | 3 +- gcc/cp/cp-tree.h | 7 +- gcc/cp/decl.c | 59 +++--- gcc/cp/except.c | 1 + gcc/cp/parser.c | 240 ++++++++++++++++++++++-- gcc/cp/parser.h | 3 + gcc/cp/pt.c | 3 +- gcc/cp/search.c | 51 +++-- gcc/cp/tree.c | 2 + gcc/testsuite/ChangeLog | 13 ++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C | 23 +++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C | 28 +++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C | 83 ++++++++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C | 11 ++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C | 12 ++ gcc/testsuite/g++.dg/cpp0x/noexcept50.C | 147 +++++++++++++++ gcc/testsuite/g++.dg/cpp0x/noexcept51.C | 14 ++ gcc/testsuite/g++.dg/cpp0x/noexcept52.C | 9 + gcc/testsuite/g++.dg/cpp0x/noexcept53.C | 26 +++ gcc/testsuite/g++.dg/eh/shadow1.C | 2 +- 21 files changed, 722 insertions(+), 58 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept45.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept46.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept47.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept48.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept49.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept50.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept51.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept52.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept53.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 31336f954c3..800ae29a380 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,48 @@ 2019-06-22 Marek Polacek + PR c++/86476 - noexcept-specifier is a complete-class context. + PR c++/52869 + * cp-tree.def (DEFAULT_ARG): Update commentary. + * cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro. + (tree_default_arg): Use tree_base instead of tree_common. + (do_push_parm_decls, maybe_check_overriding_exception_spec): Declare. + * decl.c (do_push_parm_decls): New function, broken out of... + (store_parm_decls): ...here. Call it. + * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert. + * parser.c (cp_parser_noexcept_specification_opt, + cp_parser_late_noexcept_specifier, noexcept_override_late_checks): + Forward-declare. + (unparsed_noexcepts): New macro. + (push_unparsed_function_queues): Update initializer. + (cp_parser_direct_declarator): Pass FRIEND_P to + cp_parser_exception_specification_opt. + (inject_parm_decls): New. + (pop_injected_parms): New. + (cp_parser_class_specifier_1): Implement delayed parsing of + noexcept-specifiers. + (cp_parser_save_noexcept): New. + (cp_parser_late_noexcept_specifier): New. + (noexcept_override_late_checks): New. + (cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call + cp_parser_save_noexcept instead of the normal processing if needed. + (cp_parser_exception_specification_opt): Add FRIEND_P parameter and + pass it to cp_parser_noexcept_specification_opt. + (cp_parser_save_member_function_body): Fix comment. + (cp_parser_save_default_args): Maybe save the noexcept-specifier to + post process. + (cp_parser_transaction): Update call to + cp_parser_noexcept_specification_opt. + (cp_parser_transaction_expression): Likewise. + * parser.h (cp_unparsed_functions_entry): Add new field to carry + a noexcept-specifier. + * pt.c (dependent_type_p_r): Handle unparsed noexcept expression. + * search.c (maybe_check_overriding_exception_spec): New function, broken + out of... + (check_final_overrider): ...here. Call + maybe_check_overriding_exception_spec. + * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P. + (cp_tree_equal): Handle DEFAULT_ARG. + PR c++/90881 - bogus -Wunused-value in unevaluated context. * cvt.c (convert_to_void): Don't emit unused warnings in an unevaluated context. diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 03c105b5c4c..475c584fd4c 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -209,7 +209,8 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1) /* An un-parsed default argument. Holds a vector of input tokens and a vector of places where the argument was instantiated before - parsing had occurred. */ + parsing had occurred. This is also used for delayed NSDMIs and + noexcept-specifier parsing. */ DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0) /* An uninstantiated/unevaluated noexcept-specification. For the diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 98f7a0c0cd0..2c05e638915 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1190,7 +1190,7 @@ enum cp_identifier_kind { (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations) struct GTY (()) tree_default_arg { - struct tree_common common; + struct tree_base base; struct cp_token_cache *tokens; vec *instantiations; }; @@ -1206,6 +1206,9 @@ struct GTY (()) tree_default_arg { #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \ (DEFERRED_NOEXCEPT_SPEC_P (NODE) \ && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE) +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \ + ((NODE) && (TREE_PURPOSE (NODE)) \ + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG)) struct GTY (()) tree_deferred_noexcept { struct tree_base base; @@ -6467,6 +6470,7 @@ extern bool check_array_designated_initializer (constructor_elt *, unsigned HOST_WIDE_INT); extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t); extern tree build_explicit_specifier (tree, tsubst_flags_t); +extern void do_push_parm_decls (tree, tree, tree *); /* in decl2.c */ extern void record_mangling (tree, bool); @@ -6932,6 +6936,7 @@ extern tree copied_binfo (tree, tree); extern tree original_binfo (tree, tree); extern int shared_member_p (tree); extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ()); +extern bool maybe_check_overriding_exception_spec (tree, tree); /* The representation of a deferred access check. */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index bcebdc9c0f5..8a82c754ef7 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -15761,6 +15761,39 @@ use_eh_spec_block (tree fn) && !DECL_DEFAULTED_FN (fn)); } +/* Helper function to push ARGS into the current lexical scope. DECL + is the function declaration. NONPARMS is used to handle enum + constants. */ + +void +do_push_parm_decls (tree decl, tree args, tree *nonparms) +{ + /* If we're doing semantic analysis, then we'll call pushdecl + for each of these. We must do them in reverse order so that + they end in the correct forward order. */ + args = nreverse (args); + + tree next; + for (tree parm = args; parm; parm = next) + { + next = DECL_CHAIN (parm); + if (TREE_CODE (parm) == PARM_DECL) + pushdecl (parm); + else if (nonparms) + { + /* If we find an enum constant or a type tag, put it aside for + the moment. */ + TREE_CHAIN (parm) = NULL_TREE; + *nonparms = chainon (*nonparms, parm); + } + } + + /* Get the decls in their original chain order and record in the + function. This is all and only the PARM_DECLs that were + pushed into scope by the loop above. */ + DECL_ARGUMENTS (decl) = get_local_decls (); +} + /* Store the parameter declarations into the current function declaration. This is called after parsing the parameter declarations, before digesting the body of the function. @@ -15771,7 +15804,6 @@ static void store_parm_decls (tree current_function_parms) { tree fndecl = current_function_decl; - tree parm; /* This is a chain of any other decls that came in among the parm declarations. If a parm is declared with enum {foo, bar} x; @@ -15786,35 +15818,12 @@ store_parm_decls (tree current_function_parms) and complain if any redundant old-style parm decls were written. */ tree specparms = current_function_parms; - tree next; /* Must clear this because it might contain TYPE_DECLs declared at class level. */ current_binding_level->names = NULL; - /* If we're doing semantic analysis, then we'll call pushdecl - for each of these. We must do them in reverse order so that - they end in the correct forward order. */ - specparms = nreverse (specparms); - - for (parm = specparms; parm; parm = next) - { - next = DECL_CHAIN (parm); - if (TREE_CODE (parm) == PARM_DECL) - pushdecl (parm); - else - { - /* If we find an enum constant or a type tag, - put it aside for the moment. */ - TREE_CHAIN (parm) = NULL_TREE; - nonparms = chainon (nonparms, parm); - } - } - - /* Get the decls in their original chain order and record in the - function. This is all and only the PARM_DECLs that were - pushed into scope by the loop above. */ - DECL_ARGUMENTS (fndecl) = get_local_decls (); + do_push_parm_decls (fndecl, specparms, &nonparms); } else DECL_ARGUMENTS (fndecl) = NULL_TREE; diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 71f5d609f10..1f87c5ab695 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec) || TREE_VALUE (spec) || spec == noexcept_false_spec || TREE_PURPOSE (spec) == error_mark_node + || UNPARSED_NOEXCEPT_SPEC_P (spec) || processing_template_decl); return false; diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 5cbc4551d1a..4d4d32973d9 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -247,6 +247,12 @@ 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 + (tree, tree); static void cp_parser_initial_pragma (cp_token *); @@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next) parser->unparsed_queues->last ().nsdmis #define unparsed_classes \ parser->unparsed_queues->last ().classes +#define unparsed_noexcepts \ + parser->unparsed_queues->last ().noexcepts static void push_unparsed_function_queues (cp_parser *parser) { - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL}; + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL, + NULL }; vec_safe_push (parser->unparsed_queues, e); } @@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration static tree cp_parser_throw_expression (cp_parser *); static tree cp_parser_exception_specification_opt - (cp_parser *); + (cp_parser *, bool = false); static tree cp_parser_type_id_list (cp_parser *); @@ -20816,7 +20825,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); + = cp_parser_exception_specification_opt (parser, friend_p); attrs = cp_parser_std_attribute_spec_seq (parser); @@ -23310,6 +23319,34 @@ cp_parser_class_name (cp_parser *parser, return decl; } +/* Make sure that any member-function parameters are in scope. + For instance, a function's noexcept-specifier can use the function's + parameters: + + struct S { + void fn (int p) noexcept(noexcept(p)); + }; + + so we need to make sure name lookup can find them. This is used + when we delay parsing of the noexcept-specifier. */ + +static void +inject_parm_decls (tree decl) +{ + begin_scope (sk_function_parms, decl); + tree args = DECL_ARGUMENTS (decl); + + do_push_parm_decls (decl, args, /*nonparms=*/NULL); +} + +/* Undo the effects of inject_parm_decls. */ + +static void +pop_injected_parms (void) +{ + pop_bindings_and_leave_scope (); +} + /* Parse a class-specifier. class-specifier: @@ -23620,6 +23657,60 @@ cp_parser_class_specifier_1 (cp_parser* parser) vec_safe_truncate (unparsed_classes, 0); after_nsdmi_defaulted_late_checks (type); + /* If there are noexcept-specifiers that have not yet been processed, + take care of them now. */ + class_type = NULL_TREE; + pushed_scope = NULL_TREE; + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl) + { + tree ctx = DECL_CONTEXT (decl); + if (class_type != ctx) + { + if (pushed_scope) + pop_scope (pushed_scope); + class_type = ctx; + pushed_scope = push_scope (class_type); + } + + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + spec = TREE_PURPOSE (spec); + + /* Make sure that any template parameters are in scope. */ + maybe_begin_member_template_processing (decl); + + /* Make sure that any member-function parameters are in scope. */ + inject_parm_decls (decl); + + /* 'this' is not allowed in static member functions. */ + unsigned char local_variables_forbidden_p + = parser->local_variables_forbidden_p; + if (DECL_THIS_STATIC (decl)) + parser->local_variables_forbidden_p |= THIS_FORBIDDEN; + + /* Now we can parse the noexcept-specifier. */ + spec = cp_parser_late_noexcept_specifier (parser, spec); + + if (spec != error_mark_node) + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec); + + /* Restore the state of local_variables_forbidden_p. */ + parser->local_variables_forbidden_p = local_variables_forbidden_p; + + /* The finish_struct call above performed various override checking, + but it skipped unparsed noexcept-specifier operands. Now that we + have resolved them, check again. */ + noexcept_override_late_checks (type, decl); + + /* Remove any member-function parameters from the symbol table. */ + pop_injected_parms (); + + /* Remove any template parameters from the symbol table. */ + maybe_end_member_template_processing (); + } + vec_safe_truncate (unparsed_noexcepts, 0); + if (pushed_scope) + pop_scope (pushed_scope); + /* Now parse the body of the functions. */ if (flag_openmp) { @@ -25175,6 +25266,89 @@ cp_parser_base_specifier (cp_parser* parser) /* Exception handling [gram.exception] */ +/* Save the tokens that make up the noexcept-specifier for a member-function. + Returns a DEFAULT_ARG. */ + +static tree +cp_parser_save_noexcept (cp_parser *parser) +{ + cp_token *first = parser->lexer->next_token; + /* We want everything up to, including, the final ')'. */ + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0); + cp_token *last = parser->lexer->next_token; + + /* As with default arguments and NSDMIs, make use of DEFAULT_ARG + to carry the information we will need. */ + tree expr = make_node (DEFAULT_ARG); + /* Save away the noexcept-specifier; we will process it when the + class is complete. */ + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last); + expr = build_tree_list (expr, NULL_TREE); + return expr; +} + +/* Used for late processing of noexcept-specifiers of member-functions. + DEFAULT_ARG is the unparsed operand of a noexcept-specifier which + we saved for later; parse it now. */ + +static tree +cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg) +{ + /* Make sure we've gotten something that hasn't been parsed yet. */ + gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG); + + push_unparsed_function_queues (parser); + + /* Push the saved tokens for the noexcept-specifier onto the parser's + lexer stack. */ + cp_token_cache *tokens = DEFARG_TOKENS (default_arg); + cp_parser_push_lexer_for_tokens (parser, tokens); + + /* Parse the cached noexcept-specifier. */ + tree parsed_arg + = cp_parser_noexcept_specification_opt (parser, + /*require_constexpr=*/true, + /*consumed_expr=*/NULL, + /*return_cond=*/false, + /*friend_p=*/false); + + /* Revert to the main lexer. */ + cp_parser_pop_lexer (parser); + + /* Restore the queue. */ + pop_unparsed_function_queues (parser); + + /* And we're done. */ + return parsed_arg; +} + +/* Perform late checking of overriding function with respect to their + noexcept-specifiers. TYPE is the class and FNDECL is the function + that potentially overrides some virtual function with the same + signature. */ + +static void +noexcept_override_late_checks (tree type, tree fndecl) +{ + tree binfo = TYPE_BINFO (type); + tree base_binfo; + + if (DECL_STATIC_FUNCTION_P (fndecl)) + return; + + for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) + { + tree basetype = BINFO_TYPE (base_binfo); + + if (!TYPE_POLYMORPHIC_P (basetype)) + continue; + + tree fn = look_for_overrides_here (basetype, fndecl); + if (fn) + maybe_check_overriding_exception_spec (fndecl, fn); + } +} + /* Parse an (optional) noexcept-specification. noexcept-specification: @@ -25185,13 +25359,15 @@ cp_parser_base_specifier (cp_parser* parser) 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. */ + in which case a boolean condition is returned instead. If FRIEND_P is true, + the function with this noexcept-specification had the `friend' specifier. */ static tree cp_parser_noexcept_specification_opt (cp_parser* parser, bool require_constexpr, bool* consumed_expr, - bool return_cond) + bool return_cond, + bool friend_p) { cp_token *token; const char *saved_message; @@ -25203,6 +25379,27 @@ cp_parser_noexcept_specification_opt (cp_parser* parser, if (cp_parser_is_keyword (token, RID_NOEXCEPT)) { tree expr; + + /* [class.mem]/6 says that a noexcept-specifer (within the + member-specification of the class) is a complete-class context of + a class. So, if the noexcept-specifier has the optional expression, + just save the tokens, and reparse this after we're done with the + class. */ + const bool literal_p + = ((cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER) + || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)) + && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_PAREN)); + + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN) + /* 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 + && TYPE_BEING_DEFINED (current_class_type) + && !LAMBDA_TYPE_P (current_class_type)) + return cp_parser_save_noexcept (parser); + cp_lexer_consume_token (parser->lexer); if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) @@ -25273,10 +25470,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. */ + TREE_VALUE of each node is a type. If FRIEND_P is true, the function + with this noexcept-specification had the `friend' specifier. */ static tree -cp_parser_exception_specification_opt (cp_parser* parser) +cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p) { cp_token *token; tree type_id_list; @@ -25286,8 +25484,12 @@ cp_parser_exception_specification_opt (cp_parser* parser) token = cp_lexer_peek_token (parser->lexer); /* Is it a noexcept-specification? */ - type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL, - false); + type_id_list + = cp_parser_noexcept_specification_opt (parser, + /*require_constexpr=*/true, + /*consumed_expr=*/NULL, + /*return_cond=*/false, + friend_p); if (type_id_list != NULL_TREE) return type_id_list; @@ -28435,7 +28637,7 @@ cp_parser_save_member_function_body (cp_parser* parser, return error_mark_node; } - /* Remember it, if there default args to post process. */ + /* Remember it, if there are default args to post process. */ cp_parser_save_default_args (parser, fn); /* Save away the tokens that make up the body of the @@ -28728,6 +28930,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl) vec_safe_push (unparsed_funs_with_default_args, entry); break; } + + /* Remember if there is a noexcept-specifier to post process. */ + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) + vec_safe_push (unparsed_noexcepts, decl); } /* DEFAULT_ARG contains the saved tokens for the initializer of DECL, @@ -40599,7 +40806,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token) noex = NULL_TREE; } else - noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true); + noex = cp_parser_noexcept_specification_opt (parser, + /*require_constexpr=*/true, + /*consumed_expr=*/NULL, + /*return_cond=*/true, + /*friend_p=*/false); /* Keep track if we're in the lexical scope of an outer transaction. */ new_in = this_in | (old_in & TM_STMT_ATTR_OUTER); @@ -40659,8 +40870,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword) parser->in_transaction = this_in; /* Parse a noexcept specification. */ - noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr, - true); + noex = cp_parser_noexcept_specification_opt (parser, + /*require_constexpr=*/false, + &noex_expr, + /*return_cond=*/true, + /*friend_p=*/false); if (!noex || !noex_expr || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index c03a9d87af5..2890788f489 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry { /* Nested classes go in this vector, so that we can do some final processing after parsing any NSDMIs. */ vec *classes; + + /* Functions with noexcept-specifiers that require post-processing. */ + vec *noexcepts; }; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 69de55369dd..fb89b933524 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -25313,8 +25313,9 @@ dependent_type_p_r (tree type) if (tree noex = TREE_PURPOSE (spec)) /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't affect overload resolution and treating it as dependent breaks - things. */ + things. Same for an unparsed noexcept expression. */ if (TREE_CODE (noex) != DEFERRED_NOEXCEPT + && TREE_CODE (noex) != DEFAULT_ARG && value_dependent_expression_p (noex)) return true; return false; diff --git a/gcc/cp/search.c b/gcc/cp/search.c index dac08d44d76..372c4424747 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p) NULL, &lfd); } +/* Check throw specifier of OVERRIDER is at least as strict as + the one of BASEFN. */ + +bool +maybe_check_overriding_exception_spec (tree overrider, tree basefn) +{ + maybe_instantiate_noexcept (basefn); + maybe_instantiate_noexcept (overrider); + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn)); + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider)); + + if (DECL_INVALID_OVERRIDER_P (overrider)) + return true; + + /* Can't check this yet. Pretend this is fine and let + noexcept_override_late_checks check this later. */ + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw) + || UNPARSED_NOEXCEPT_SPEC_P (over_throw)) + return true; + + if (!comp_except_specs (base_throw, over_throw, ce_derived)) + { + auto_diagnostic_group d; + error ("looser exception specification on overriding virtual function " + "%q+#F", overrider); + inform (DECL_SOURCE_LOCATION (basefn), + "overridden function is %q#F", basefn); + DECL_INVALID_OVERRIDER_P (overrider) = 1; + return false; + } + return true; +} + /* Check that virtual overrider OVERRIDER is acceptable for base function BASEFN. Issue diagnostic, and return zero, if unacceptable. */ @@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn) tree base_type = TREE_TYPE (basefn); tree over_return = fndecl_declared_return_type (overrider); tree base_return = fndecl_declared_return_type (basefn); - tree over_throw, base_throw; int fail = 0; @@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn) return 0; } - /* Check throw specifier is at least as strict. */ - maybe_instantiate_noexcept (basefn); - maybe_instantiate_noexcept (overrider); - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn)); - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider)); - - if (!comp_except_specs (base_throw, over_throw, ce_derived)) - { - auto_diagnostic_group d; - error ("looser throw specifier for %q+#F", overrider); - inform (DECL_SOURCE_LOCATION (basefn), - "overridden function is %q#F", basefn); - DECL_INVALID_OVERRIDER_P (overrider) = 1; - return 0; - } + if (!maybe_check_overriding_exception_spec (overrider, basefn)) + return 0; /* Check for conflicting type attributes. But leave transaction_safe for set_one_vmethod_tm_attributes. */ diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 978aea56193..ebfe362595f 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises) if (raises == NULL_TREE) return raises; else if (DEFERRED_NOEXCEPT_SPEC_P (raises) + || UNPARSED_NOEXCEPT_SPEC_P (raises) || uses_template_parms (raises) || uses_template_parms (TREE_PURPOSE (raises))) /* Keep a dependent or deferred exception specification. */ @@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2) case IDENTIFIER_NODE: case SSA_NAME: case USING_DECL: + case DEFAULT_ARG: return false; case BASELINK: diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b0b1d4ba912..e38abbf7be3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,18 @@ 2019-06-22 Marek Polacek + PR c++/86476 - noexcept-specifier is a complete-class context. + PR c++/52869 + * g++.dg/cpp0x/noexcept45.C: New test. + * g++.dg/cpp0x/noexcept46.C: New test. + * g++.dg/cpp0x/noexcept47.C: New test. + * g++.dg/cpp0x/noexcept48.C: New test. + * g++.dg/cpp0x/noexcept49.C: New test. + * g++.dg/cpp0x/noexcept50.C: New test. + * g++.dg/cpp0x/noexcept51.C: New test. + * g++.dg/cpp0x/noexcept52.C: New test. + * g++.dg/cpp0x/noexcept53.C: New test. + * g++.dg/eh/shadow1.C: Adjust dg-error. + PR c++/90881 - bogus -Wunused-value in unevaluated context. * g++.dg/cpp0x/Wunused-value1.C: New test. diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept45.C b/gcc/testsuite/g++.dg/cpp0x/noexcept45.C new file mode 100644 index 00000000000..39df4a6571e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept45.C @@ -0,0 +1,23 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +struct A +{ + virtual void f(); + virtual void g() noexcept; + virtual void h() noexcept(false); +}; + +struct B : A +{ + void f() noexcept(true); + void g() noexcept(true); + void h() noexcept(true); +}; + +struct D : A +{ + void f() noexcept(false); + void g() noexcept(false); // { dg-error "looser exception specification" } + void h() noexcept(false); +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept46.C b/gcc/testsuite/g++.dg/cpp0x/noexcept46.C new file mode 100644 index 00000000000..da7490d651c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept46.C @@ -0,0 +1,28 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +void f() noexcept(false); +void g() noexcept(true); +void h() noexcept; + +struct B { + friend void f() noexcept(false); + friend void g() noexcept(false); // { dg-error "different exception specifier" } + friend void h() noexcept(false); // { dg-error "different exception specifier" } +}; + +struct C { + friend void f() noexcept(true); // { dg-error "different exception specifier" } + friend void g() noexcept(true); // { dg-error "different exception specifier" } + friend void h() noexcept(true); // { dg-error "different exception specifier" } +}; + +void o() noexcept(false); +void p() noexcept(true); +void q() noexcept; + +struct D { + friend void o() noexcept(true); // { dg-error "different exception specifier" } + friend void p() noexcept(true); + friend void q() noexcept(true); +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept47.C b/gcc/testsuite/g++.dg/cpp0x/noexcept47.C new file mode 100644 index 00000000000..0848e68f9b1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept47.C @@ -0,0 +1,83 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +int fn1 (); +int fn2 () noexcept; +int fn3 () noexcept; + +void g() noexcept(noexcept (fn2())); + +struct S1 { + friend void g1() noexcept(noexcept(fn2())); + friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" } +}; + +struct S2 { + friend void g2() noexcept(noexcept(fn1())); + friend void g2() noexcept(noexcept(fn1())); + friend void g2() noexcept(noexcept(fn1())); +}; + +struct S3 { + friend void g3() noexcept(noexcept(fn1())); + friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" } +}; + +struct S4 { + friend void g4() noexcept(noexcept(fn2())); + friend void g4() noexcept(noexcept(fn3())); +}; + +struct S5 { + friend void g() noexcept(noexcept(fn3())); +}; + +struct S6 { + friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" } +}; + +struct S7 { + friend void gg() noexcept(noexcept(fn3())); +}; + +void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" } + +struct S8 { + friend void g8(); + friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" } +}; + +struct S9 { + friend void g9(); + friend void g9() noexcept(noexcept(fn1())); +}; + +struct S10 { + friend void g10() noexcept(noexcept(fn1())); + friend void g10(); +}; + +struct S11 { + friend void g11() noexcept(noexcept(fn2())); + friend void g11(); // { dg-error "different exception specifier" } +}; + +struct S12 { + friend void g12() noexcept(false); + friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" } +}; + +struct S13 { + friend void g13() noexcept(false); + friend void g13() noexcept(noexcept(fn1())); +}; + +struct S14 { + friend void g14() noexcept(noexcept(fn1())); + friend void g14() noexcept(false); +}; + +struct S15 { + friend void g15() noexcept(noexcept(fn2())); + friend void g15() noexcept(false); // { dg-error "different exception specifier" } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept48.C b/gcc/testsuite/g++.dg/cpp0x/noexcept48.C new file mode 100644 index 00000000000..134212c3613 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept48.C @@ -0,0 +1,11 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +int g; + +struct S { + int b; + friend void fn1(int n) noexcept(noexcept(n)); + friend void fn2() noexcept(noexcept(g)); + friend void fn3() noexcept(noexcept(b)); +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept49.C b/gcc/testsuite/g++.dg/cpp0x/noexcept49.C new file mode 100644 index 00000000000..6da7ff3361f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept49.C @@ -0,0 +1,12 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert(X, #X) + +struct S { + static void f1() noexcept(b); + static constexpr auto b = true; +}; + +S s; +SA(noexcept(s.f1())); diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept50.C b/gcc/testsuite/g++.dg/cpp0x/noexcept50.C new file mode 100644 index 00000000000..43b38c2446f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept50.C @@ -0,0 +1,147 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert(X, #X) + +struct S { + void f1() noexcept(noexcept(i)) { } + void f2() noexcept(noexcept(fn())) { } + void f3() noexcept(noexcept(fnx())) { } + void f4() noexcept(noexcept(i)); + void f5() noexcept(noexcept(fn())); + void f6() noexcept(noexcept(fnx())); + + void f7() noexcept(1); + void f8() noexcept(0); + void f9() noexcept(b); + void f10() noexcept(!b); + + int i; + static constexpr auto b = true; + void fny() noexcept(noexcept(fn())); + void fn(); + void fnx() noexcept; +}; + +S s; +SA(noexcept(s.f1())); +SA(!noexcept(s.f2())); +SA(noexcept(s.f3())); +SA(noexcept(s.f4())); +SA(!noexcept(s.f5())); +SA(noexcept(s.f6())); +SA(noexcept(s.f7())); +SA(!noexcept(s.f8())); +SA(noexcept(s.f9())); +SA(!noexcept(s.f10())); + +struct S2 { + struct V { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void f3() noexcept(noexcept(fn())) { } + void f4() noexcept(noexcept(fnx())) { } + void fn(); + void fnx() noexcept; + } v; + void fn(); + void fnx(); +}; + +S2 s2; +SA(!noexcept(s2.v.f1())); +SA(noexcept(s2.v.f2())); +SA(!noexcept(s2.v.f3())); +SA(noexcept(s2.v.f4())); + +struct S3 { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void fn(); + void fnx() noexcept; +}; + +void +S3::f1() noexcept(noexcept(fn())) +{ +} + +void +S3::f2() noexcept(noexcept(fnx())) +{ +} + +struct S4 { + int f1 (int p) noexcept(noexcept(p)) { return p; } + int f2 (int p) noexcept(noexcept(p)); + int f3 (int p = 10) noexcept(noexcept(p)); + int f4 () noexcept(noexcept(S4{})); +}; + +S4 s4; +SA(noexcept(s4.f1(1))); +SA(noexcept(s4.f2(1))); +SA(noexcept(s4.f3())); +SA(noexcept(s4.f4())); + +template +struct S5 { + void f1() noexcept(noexcept(i)) { } + void f2() noexcept(noexcept(fn())) { } + void f3() noexcept(noexcept(fnx())) { } + void f4() noexcept(noexcept(i)); + void f5() noexcept(noexcept(fn())); + void f6() noexcept(noexcept(fnx())); + + int i; + void fny() noexcept(noexcept(fn())); + void fn(); + void fnx() noexcept; +}; + +S5 s5; +SA(noexcept(s5.f1())); +SA(!noexcept(s5.f2())); +SA(noexcept(s5.f3())); +SA(noexcept(s5.f4())); +SA(!noexcept(s5.f5())); +SA(noexcept(s5.f6())); + +template +struct S6 { + void f1() noexcept(noexcept(x)); + T x; +}; + +struct S7 { + template + void f1 () noexcept(noexcept(U(1))) { } + + template + void f2() noexcept(noexcept(N)); + + template + void f3(_Up __p) noexcept(noexcept(__p)); +}; + +void glob(); +void globx() noexcept; +struct S8 { + void f1 () noexcept(noexcept(glob())); + void f2 () noexcept(noexcept(globx())); +}; + +S8 s8; +SA(!noexcept(s8.f1())); +SA(noexcept(s8.f2())); + +struct W { + constexpr operator bool(); +}; + +template +struct S9 { + S9() noexcept(noexcept(w)) { } + S9 &operator=(S9 &&) noexcept(T::X); + W w; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept51.C b/gcc/testsuite/g++.dg/cpp0x/noexcept51.C new file mode 100644 index 00000000000..a81032f28e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept51.C @@ -0,0 +1,14 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +void fn1(void()); +template class A { + void _M_local_data(); + A() noexcept(_M_local_data); +}; + +class B { + void _S_initialize(); + static void _S_initialize_once(); +}; +void B::_S_initialize() { fn1(_S_initialize_once); } diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept52.C b/gcc/testsuite/g++.dg/cpp0x/noexcept52.C new file mode 100644 index 00000000000..12c6d364099 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept52.C @@ -0,0 +1,9 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +template class A { + typedef _Alloc _Alloc_traits; + A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move); + A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move); + void m_fn2(A) {} +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept53.C b/gcc/testsuite/g++.dg/cpp0x/noexcept53.C new file mode 100644 index 00000000000..b3859de9ebc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept53.C @@ -0,0 +1,26 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +struct S { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void fn(); + void fnx() noexcept; +}; + +void +S::f1() noexcept // { dg-error "different exception specifier" } +{ +} + +void +S::f2() // { dg-error "different exception specifier" } +{ +} + +struct S2 { + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" } + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" } + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" } + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" } +}; diff --git a/gcc/testsuite/g++.dg/eh/shadow1.C b/gcc/testsuite/g++.dg/eh/shadow1.C index 0ba6145ef0c..6bccc704d49 100644 --- a/gcc/testsuite/g++.dg/eh/shadow1.C +++ b/gcc/testsuite/g++.dg/eh/shadow1.C @@ -18,7 +18,7 @@ struct D : private B // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 } struct E : public D { - virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } } + virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } } }; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 } // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 } B* foo (D *); -- 2.30.2