From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 121852 invoked by alias); 14 Jun 2019 20:54:26 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 121841 invoked by uid 89); 14 Jun 2019 20:54:25 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.8 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=S10, gotten X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 14 Jun 2019 20:54:20 +0000 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 29175B0ABB for ; Fri, 14 Jun 2019 20:54:19 +0000 (UTC) Received: from redhat.com (ovpn-120-65.rdu2.redhat.com [10.10.120.65]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 81DBC544F9; Fri, 14 Jun 2019 20:54:17 +0000 (UTC) Date: Fri, 14 Jun 2019 20:54:00 -0000 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) Message-ID: <20190614205415.GH5989@redhat.com> References: <20181219202731.GL21364@redhat.com> <90c62261-b25e-0611-696d-044d319571cc@redhat.com> <20190510192133.GC20687@redhat.com> <2a193c49-75c7-4407-1f1f-3748a69ab560@redhat.com> <20190604010137.GY5989@redhat.com> <6949597a-e84c-4b87-ec57-f0186b936101@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <6949597a-e84c-4b87-ec57-f0186b936101@redhat.com> User-Agent: Mutt/1.11.4 (2019-03-13) X-SW-Source: 2019-06/txt/msg00861.txt.bz2 On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote: > On 6/3/19 9:01 PM, Marek Polacek wrote: > > > I sort of ended up going down a rathole, but then I realized we don't need to > > delay parsing of noexcept-specifiers of member friend function declarations, > > because they aren't members of the class. > > Where are you getting this from? I'm definitely sympathetic to the idea > that noexcept-specifiers of friend functions shouldn't need to be > complete-class contexts, but 10.3 doesn't make that distinction that I can > see. When I tested my patch I noticed that none of the 3 compilers I tried handled this scenario, so I thought I was missing something. But if the standard really doesn't say that noexcept-specifiers of friend functions don't have to be complete-class contexts, then perhaps it needs to say so. Should I raise this on the reflector? > > This was a huge relief because > > member friend function declarations can be redeclared, so we'd have to make > > sure to check if their noexcept-specifiers match. But member function decls > > can't be redeclared. I updated the comment to better reflect why what I'm > > doing there is correct, along with an assert. > > But then why do you still need this: > > > + /* We can't compare unparsed noexcept-specifiers. Save the decl > > + and check this again after we've parsed the noexcept-specifiers > > + for real. */ > > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions)) > > + { > > + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl); > > + return; > > + } > > ? Eh... I don't. The following version is with the DEFARG_DECL junk removed. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-06-14 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. * g++.dg/cpp0x/noexcept42.C: New test. * g++.dg/cpp0x/noexcept43.C: New test. * g++.dg/cpp0x/noexcept44.C: New test. * 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/eh/shadow1.C: Adjust dg-error. diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def index 03c105b5c4c..475c584fd4c 100644 --- gcc/cp/cp-tree.def +++ 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 gcc/cp/cp-tree.h gcc/cp/cp-tree.h index 1f4e1e15554..107a322cc82 100644 --- gcc/cp/cp-tree.h +++ 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 gcc/cp/decl.c gcc/cp/decl.c index 0a3ef452536..43d7ba6a114 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -15720,6 +15720,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. @@ -15730,7 +15763,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; @@ -15745,35 +15777,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 gcc/cp/except.c gcc/cp/except.c index 892d5201da9..8d7b1e9bac7 100644 --- gcc/cp/except.c +++ 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 gcc/cp/parser.c gcc/cp/parser.c index 8f5ae84670a..0d926cc0bd5 100644 --- gcc/cp/parser.c +++ 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,26 @@ 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. */ + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN) + /* No need to delay parsing for a number literal or true/false. */ + && !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_keyword (parser->lexer, 3, RID_FALSE) + || cp_lexer_nth_token_is_keyword (parser->lexer, 3, + RID_TRUE))) + && 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 +25469,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 +25483,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; @@ -28415,7 +28616,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 @@ -28708,6 +28909,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, @@ -40579,7 +40785,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); @@ -40639,8 +40849,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 gcc/cp/parser.h gcc/cp/parser.h index c03a9d87af5..2890788f489 100644 --- gcc/cp/parser.h +++ 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 gcc/cp/pt.c gcc/cp/pt.c index 2a626526c6f..05bc4a3546a 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -25307,8 +25307,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 gcc/cp/search.c gcc/cp/search.c index dac08d44d76..372c4424747 100644 --- gcc/cp/search.c +++ 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 gcc/cp/tree.c gcc/cp/tree.c index cd021b7f594..81c53b23ebf 100644 --- gcc/cp/tree.c +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C new file mode 100644 index 00000000000..b3859de9ebc --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept42.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 gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C new file mode 100644 index 00000000000..12c6d364099 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept43.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 gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C new file mode 100644 index 00000000000..a81032f28e9 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept44.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 gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C new file mode 100644 index 00000000000..39df4a6571e --- /dev/null +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C new file mode 100644 index 00000000000..da7490d651c --- /dev/null +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C new file mode 100644 index 00000000000..0848e68f9b1 --- /dev/null +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C new file mode 100644 index 00000000000..134212c3613 --- /dev/null +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C new file mode 100644 index 00000000000..6da7ff3361f --- /dev/null +++ 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 gcc/testsuite/g++.dg/cpp0x/noexcept50.C gcc/testsuite/g++.dg/cpp0x/noexcept50.C new file mode 100644 index 00000000000..43b38c2446f --- /dev/null +++ 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 gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C index 0ba6145ef0c..6bccc704d49 100644 --- gcc/testsuite/g++.dg/eh/shadow1.C +++ 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 *);