From: Marek Polacek <polacek@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
Date: Mon, 10 Jun 2019 12:28:00 -0000 [thread overview]
Message-ID: <20190610122805.GD5989@redhat.com> (raw)
In-Reply-To: <20190604010137.GY5989@redhat.com>
Ping.
On Mon, Jun 03, 2019 at 09:01:37PM -0400, Marek Polacek wrote:
> On Tue, May 28, 2019 at 11:46:53AM -0400, Jason Merrill wrote:
> > > @@ -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);
> >
> > Can we handle this in cp_parser_save_default_args rather than all its
> > callers?
>
> Yes, 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
> > > +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 ();
> > > +}
> >
> > Can we share this code with store_parm_decls instead of having two copies?
>
> Makes sense, the nreverse logic is tricky enough not to duplicate it. I named
> the new function do_push_parm_decls.
>
> > > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > > + /* [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);
> >
> > We might optimize the noexcept(<literal>) case, which should be pretty
> > common.
>
> Yeah, it's worth it. Added for numbers/false/true.
>
> > > +maybe_check_throw_specifier (tree overrider, tree basefn)
> >
> > maybe_check_overriding_exception_spec
>
> Changed.
>
> > > 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;
> >
> > Here you're throwing away the other side, which seems wrong.
>
> 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. 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.
>
> noexcept47.C tests various cases with friend declarations.
>
> Thanks,
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-06-03 Marek Polacek <polacek@redhat.com>
>
> 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, do_push_parm_decls,
> maybe_check_overriding_exception_spec): Declare.
> * decl.c (check_redeclaration_exception_specification): No longer
> static. Handle UNPARSED_NOEXCEPT_SPEC_P.
> (do_push_parm_decls): New function, broken out of...
> (store_parm_decls): ...here.
> * 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.
> * 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/cpp0x/noexcept47.C: New test.
> * g++.dg/cpp0x/noexcept48.C: New test.
> * g++.dg/cpp0x/noexcept49.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 4d79c43c5af..3f1d8a853db 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1187,12 +1187,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<tree, va_gc> *instantiations;
> + BOOL_BITFIELD noexcept_p : 1;
> + union {
> + vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
> + tree GTY((tag ("1"))) decl;
> + } GTY((desc ("%1.noexcept_p"))) u;
> };
>
>
> @@ -1206,6 +1214,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 +6478,9 @@ 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);
> +extern void do_push_parm_decls (tree, tree, tree *);
>
> /* in decl2.c */
> extern void record_mangling (tree, bool);
> @@ -6932,6 +6947,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 bdf397e5ecb..e8fdc08bd4a 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,18 @@ check_redeclaration_exception_specification (tree new_decl,
> && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> return;
>
> + /* 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;
> + }
> + else
> + /* Member functions can't be redeclared. */
> + gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (old_exceptions));
> +
> if (!type_dependent_expression_p (old_decl))
> {
> maybe_instantiate_noexcept (new_decl);
> @@ -15706,6 +15718,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.
> @@ -15716,7 +15761,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;
> @@ -15731,35 +15775,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 1de35da83ec..ea4adbcdb14 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 *);
>
> @@ -20804,7 +20813,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);
>
> @@ -23298,6 +23307,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:
> @@ -23608,6 +23645,67 @@ 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);
> +
> + 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);
> +
> + 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);
> +
> + /* 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)
> {
> @@ -25163,6 +25261,91 @@ 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,
> + /*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:
> @@ -25173,13 +25356,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;
> @@ -25191,6 +25376,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)
> @@ -25261,10 +25466,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;
> @@ -25274,8 +25480,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;
>
> @@ -28403,7 +28613,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
> @@ -28696,6 +28906,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,
> @@ -40506,7 +40721,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);
> @@ -40566,8 +40785,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<tree, va_gc> *classes;
> +
> + /* Functions with noexcept-specifiers that require post-processing. */
> + vec<tree, va_gc> *noexcepts;
> };
>
>
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index cfbd9fd4c88..0324bd404e9 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -25302,8 +25302,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/cp/typeck2.c gcc/cp/typeck2.c
> index e9f759d4213..eec5550bf2d 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2391,6 +2391,15 @@ 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.
> + We can use the newer variant, because the old one will be saved in
> + DEFARG_DECL's noexcept-specifier. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> + return list;
> + else
> + /* Member functions can't be redeclared. */
> + gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (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<typename T>
> +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<int> 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<typename T>
> +struct S6 {
> + void f1() noexcept(noexcept(x));
> + T x;
> +};
> +
> +struct S7 {
> + template<typename U>
> + void f1 () noexcept(noexcept(U(1))) { }
> +
> + template<int N>
> + void f2() noexcept(noexcept(N));
> +
> + template <typename _Up>
> + 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<typename T>
> +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 <typename _Alloc> 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<char>) {}
> +};
> 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 <typename> 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/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
next prev parent reply other threads:[~2019-06-10 12:28 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-12-19 20:27 Marek Polacek
2019-01-04 14:45 ` Marek Polacek
2019-01-07 15:44 ` Jason Merrill
2019-05-10 19:21 ` Marek Polacek
2019-05-17 14:35 ` Marek Polacek
2019-05-24 16:17 ` Marek Polacek
2019-05-28 15:48 ` Jason Merrill
2019-06-04 1:02 ` Marek Polacek
2019-06-10 12:28 ` Marek Polacek [this message]
2019-06-12 3:46 ` Jason Merrill
2019-06-14 20:54 ` Marek Polacek
2019-06-21 20:47 ` Jason Merrill
2019-06-21 21:30 ` Marek Polacek
2019-06-22 0:28 ` Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190610122805.GD5989@redhat.com \
--to=polacek@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).