From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 64253 invoked by alias); 24 May 2019 16:17:24 -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 64244 invoked by uid 89); 24 May 2019 16:17:24 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.4 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=overrider, pushdecl, field_decl, instantiations 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, 24 May 2019 16:17:19 +0000 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 69DC5C04FFF9 for ; Fri, 24 May 2019 16:17:16 +0000 (UTC) Received: from redhat.com (unknown [10.20.4.51]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D534619748; Fri, 24 May 2019 16:17:15 +0000 (UTC) Date: Fri, 24 May 2019 16:17: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: <20190524161714.GC5989@redhat.com> References: <20181219202731.GL21364@redhat.com> <90c62261-b25e-0611-696d-044d319571cc@redhat.com> <20190510192133.GC20687@redhat.com> <20190517143529.GB21145@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20190517143529.GB21145@redhat.com> User-Agent: Mutt/1.11.4 (2019-03-13) X-SW-Source: 2019-05/txt/msg01710.txt.bz2 Ping. On Fri, May 17, 2019 at 10:35:29AM -0400, Marek Polacek wrote: > Ping. > > On Fri, May 10, 2019 at 03:21:33PM -0400, Marek Polacek wrote: > > Coming back to this. I didn't think this was suitable for GCC 9. > > > > On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote: > > > On 12/19/18 3:27 PM, Marek Polacek wrote: > > > > Prompted by Jon's observation in 52869, I noticed that we don't treat > > > > a noexcept-specifier as a complete-class context of a class ([class.mem]/6). > > > > As with member function bodies, default arguments, and NSDMIs, names used in > > > > a noexcept-specifier of a member-function can be declared later in the class > > > > body, so we need to wait and parse them at the end of the class. > > > > For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG). > > > > > > Or DEFERRED_PARSE, yes. > > > > I didn't change the name but I'm happy to do it as a follow up. > > > > > > + /* We can't compare unparsed noexcept-specifiers. Save the old decl > > > > + and check this again after we've parsed the noexcept-specifiers > > > > + for real. */ > > > > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions)) > > > > + { > > > > + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)), > > > > + copy_decl (old_decl)); > > > > + return; > > > > + } > > > > > > Why copy_decl? > > > > This is so that we don't lose the diagnostic in noexcept46.C. If I don't use > > copy_decl then the tree is shared and subsequent changes to it make us not > > detect discrepancies like noexcept(false) vs. noexcept(true) on the same decl. > > > > > It seems wasteful to allocate a vec to hold this single decl; let's make the > > > last field of tree_default_arg a union instead. And add a new macro for the > > > single decl case. > > > > Done. But that required also adding GTY markers *and* a new BOOL_BITFIELD for > > the sake of GTY((desc)). > > > > > I notice that default_arg currently uses tree_common for some reason, and we > > > ought to be able to save two words by switching to tree_base > > > > Done. > > > > > > @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec) > > > > || TREE_VALUE (spec) > > > > || spec == noexcept_false_spec > > > > || TREE_PURPOSE (spec) == error_mark_node > > > > + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG > > > > > > Maybe use UNPARSED_NOEXCEPT_SPEC_P here? > > > > Done. > > > > > > +/* 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 > > > > +maybe_begin_member_function_processing (tree decl) > > > > > > This name is pretty misleading. How about inject_parm_decls, to go with > > > inject_this_parameter? > > > > Done. > > > > > > +/* Undo the effects of maybe_begin_member_function_processing. */ > > > > + > > > > +static void > > > > +maybe_end_member_function_processing (void) > > > > > > And then perhaps pop_injected_parms. > > > > Done. > > > > > > +/* Check throw specifier of OVERRIDER is at least as strict as > > > > + the one of BASEFN. */ > > > > + > > > > +bool > > > > +maybe_check_throw_specifier (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 throw specifier for %q+#F", overrider); > > > > > > Since we're touching this diagnostic, let's correct it now to "exception > > > specification". And add "on overriding virtual function". > > > > Ok, changed to the more up-to-date term. > > > > Two further changes were required since my changes to detecting 'this' for > > static member functions: > > 1) use THIS_FORBIDDEN if needed when parsing the delayed noexcept, > > 2) careful about friend member functions -- its DECL_CONTEXT is not the > > containing class, need to use DECL_FRIEND_CONTEXT. > > > > Both of these points are tested in g++.dg/cpp0x/this1.C. > > > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > > > 2019-05-10 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 (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P): > > New macros. > > (tree_default_arg): Add a tree field, make the last two fields into a > > union. Add GTY markers. > > (check_redeclaration_exception_specification): Declare. > > (maybe_check_throw_specifier): Declare. > > * decl.c (check_redeclaration_exception_specification): No longer > > static. Handle UNPARSED_NOEXCEPT_SPEC_P. > > * 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_init_declarator): Maybe save the noexcept-specifier to > > post process. > > (inject_parm_decls): New. > > (pop_injected_parms): New. > > (cp_parser_class_specifier_1): Implement delayed parsing of > > noexcept-specifiers. > > (cp_parser_member_declaration): Maybe save the noexcept-specifier to > > post process. > > (cp_parser_save_noexcept): New. > > (cp_parser_late_noexcept_specifier): New. > > (noexcept_override_late_checks): New. > > (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept > > instead of the normal processing if needed. > > (cp_parser_save_member_function_body): Maybe save the > > noexcept-specifier to post process. > > * 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_throw_specifier): New function, broken out > > of... > > (check_final_overrider): ...here. Call maybe_check_throw_specifier. > > * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P. > > (cp_tree_equal): Handle DEFAULT_ARG. > > * typeck2.c (merge_exception_specifiers): If an unparsed noexcept > > expression has been passed, return it instead of merging it. > > > > * g++.dg/cpp0x/noexcept41.C: New test. > > * 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/eh/shadow1.C: Adjust dg-error. > > > > diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def > > index 03c105b5c4c..33eb5d25efe 100644 > > --- gcc/cp/cp-tree.def > > +++ gcc/cp/cp-tree.def > > @@ -209,7 +209,9 @@ 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. For a noexcept-specifier, we use a tree > > + holding a function declaration used for late checking. */ > > 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 f253857b02a..ef14a011293 100644 > > --- gcc/cp/cp-tree.h > > +++ gcc/cp/cp-tree.h > > @@ -1178,12 +1178,20 @@ enum cp_identifier_kind { > > #define DEFARG_TOKENS(NODE) \ > > (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens) > > #define DEFARG_INSTANTIATIONS(NODE) \ > > - (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations) > > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations) > > +#define DEFARG_DECL(NODE) \ > > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl) > > +#define DEFARG_NOEXCEPT_P(NODE) \ > > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p) > > > > struct GTY (()) tree_default_arg { > > - struct tree_common common; > > + struct tree_base base; > > struct cp_token_cache *tokens; > > - vec *instantiations; > > + BOOL_BITFIELD noexcept_p : 1; > > + union { > > + vec* GTY((tag ("0"))) instantiations; > > + tree GTY((tag ("1"))) decl; > > + } GTY((desc ("%1.noexcept_p"))) u; > > }; > > > > > > @@ -1197,6 +1205,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; > > @@ -6464,6 +6475,8 @@ 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 check_redeclaration_exception_specification > > + (tree, tree); > > > > /* in decl2.c */ > > extern void record_mangling (tree, bool); > > @@ -6929,6 +6942,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_throw_specifier (tree, tree); > > > > /* The representation of a deferred access check. */ > > > > diff --git gcc/cp/decl.c gcc/cp/decl.c > > index 36014dc628e..a2effa13623 100644 > > --- gcc/cp/decl.c > > +++ gcc/cp/decl.c > > @@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl) > > function templates. If their exception specifications do not > > match, issue a diagnostic. */ > > > > -static void > > +void > > check_redeclaration_exception_specification (tree new_decl, > > tree old_decl) > > { > > @@ -1151,6 +1151,15 @@ check_redeclaration_exception_specification (tree new_decl, > > && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions)) > > return; > > > > + /* We can't compare unparsed noexcept-specifiers. Save the old 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; > > + } > > + > > if (!type_dependent_expression_p (old_decl)) > > { > > maybe_instantiate_noexcept (new_decl); > > diff --git gcc/cp/except.c gcc/cp/except.c > > index afc261073d7..208c9c1461d 100644 > > --- gcc/cp/except.c > > +++ gcc/cp/except.c > > @@ -1248,6 +1248,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 12beadf5096..41197ab3486 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); > > +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); > > } > > > > @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser, > > /*asmspec=*/NULL_TREE, > > attr_chainon (attributes, prefix_attributes)); > > if (decl && TREE_CODE (decl) == FUNCTION_DECL) > > - cp_parser_save_default_args (parser, decl); > > + { > > + cp_parser_save_default_args (parser, decl); > > + /* 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); > > + } > > cp_finalize_omp_declare_simd (parser, decl); > > cp_finalize_oacc_routine (parser, decl, false); > > } > > @@ -23334,6 +23349,45 @@ 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); > > + args = nreverse (args); > > + > > + tree next; > > + for (tree parm = args; parm; parm = next) > > + { > > + next = DECL_CHAIN (parm); > > + if (TREE_CODE (parm) == PARM_DECL) > > + pushdecl (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 (); > > +} > > + > > +/* Undo the effects of inject_parm_decls. */ > > + > > +static void > > +pop_injected_parms (void) > > +{ > > + pop_bindings_and_leave_scope (); > > +} > > + > > /* Parse a class-specifier. > > > > class-specifier: > > @@ -23644,6 +23698,66 @@ 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_FRIEND_P (decl) ? DECL_FRIEND_CONTEXT (decl) > > + : DECL_CONTEXT (decl)); > > + if (class_type != ctx) > > + { > > + if (pushed_scope) > > + pop_scope (pushed_scope); > > + class_type = ctx; > > + pushed_scope = push_scope (class_type); > > + } > > + > > + /* 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); > > + > > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); > > + spec = TREE_PURPOSE (spec); > > + tree old_decl = DEFARG_DECL (spec); > > + > > + /* '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); > > + > > + /* Restore the state of local_variables_forbidden_p. */ > > + parser->local_variables_forbidden_p = local_variables_forbidden_p; > > + if (spec != error_mark_node) > > + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec); > > + > > + /* If we've stashed an old declaration, it means we need to > > + perform late redeclaration checking. */ > > + if (old_decl) > > + check_redeclaration_exception_specification (decl, old_decl); > > + > > + /* 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) > > { > > @@ -24817,6 +24931,12 @@ cp_parser_member_declaration (cp_parser* parser) > > else > > decl = finish_fully_implicit_template (parser, decl); > > } > > + if (decl && TREE_CODE (decl) == FUNCTION_DECL) > > + { > > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); > > + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) > > + vec_safe_push (unparsed_noexcepts, decl); > > + } > > } > > > > cp_finalize_omp_declare_simd (parser, decl); > > @@ -25199,6 +25319,90 @@ 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); > > + DEFARG_DECL (expr) = NULL_TREE; > > + DEFARG_NOEXCEPT_P (expr) = true; > > + 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, > > + NULL, > > + /*return_cond=*/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_throw_specifier (fndecl, fn); > > + } > > +} > > + > > /* Parse an (optional) noexcept-specification. > > > > noexcept-specification: > > @@ -25227,6 +25431,18 @@ 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_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN > > + && at_class_scope_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) > > @@ -28427,7 +28643,12 @@ cp_parser_save_member_function_body (cp_parser* parser, > > return error_mark_node; > > } > > > > - /* Remember it, if there default args to post process. */ > > + /* Remember if there is a noexcept-specifier to post process. */ > > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); > > + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) > > + vec_safe_push (unparsed_noexcepts, fn); > > + > > + /* 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 > > 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 d6976e08690..c00d14fd954 100644 > > --- gcc/cp/pt.c > > +++ gcc/cp/pt.c > > @@ -25308,8 +25308,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 4c3fffda717..5a3a0cf2824 100644 > > --- gcc/cp/search.c > > +++ gcc/cp/search.c > > @@ -1863,6 +1863,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_throw_specifier (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. */ > > > > @@ -1873,7 +1906,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; > > > > @@ -1957,21 +1989,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_throw_specifier (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 718eed349c6..bc0080d6720 100644 > > --- gcc/cp/tree.c > > +++ gcc/cp/tree.c > > @@ -2550,6 +2550,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. */ > > @@ -3662,6 +3663,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/cp/typeck2.c gcc/cp/typeck2.c > > index df002a1664c..8cbc48fb44f 100644 > > --- gcc/cp/typeck2.c > > +++ gcc/cp/typeck2.c > > @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add) > > if (list == error_mark_node || add == error_mark_node) > > return error_mark_node; > > > > + /* We don't want to lose the unparsed operand lest we miss diagnostics. */ > > + if (UNPARSED_NOEXCEPT_SPEC_P (list)) > > + return list; > > + else if (UNPARSED_NOEXCEPT_SPEC_P (add)) > > + return add; > > + > > /* No exception-specifier or noexcept(false) are less strict than > > anything else. Prefer the newer variant (LIST). */ > > if (!list || list == noexcept_false_spec) > > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C > > new file mode 100644 > > index 00000000000..43b38c2446f > > --- /dev/null > > +++ gcc/testsuite/g++.dg/cpp0x/noexcept41.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/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/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 *); > > Marek Marek