2019-06-22 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 (UNPARSED_NOEXCEPT_SPEC_P): New macro.
+ (tree_default_arg): Use tree_base instead of tree_common.
+ (do_push_parm_decls, maybe_check_overriding_exception_spec): Declare.
+ * decl.c (do_push_parm_decls): New function, broken out of...
+ (store_parm_decls): ...here. Call it.
+ * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
+ * parser.c (cp_parser_noexcept_specification_opt,
+ cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
+ Forward-declare.
+ (unparsed_noexcepts): New macro.
+ (push_unparsed_function_queues): Update initializer.
+ (cp_parser_direct_declarator): Pass FRIEND_P to
+ cp_parser_exception_specification_opt.
+ (inject_parm_decls): New.
+ (pop_injected_parms): New.
+ (cp_parser_class_specifier_1): Implement delayed parsing of
+ noexcept-specifiers.
+ (cp_parser_save_noexcept): New.
+ (cp_parser_late_noexcept_specifier): New.
+ (noexcept_override_late_checks): New.
+ (cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call
+ cp_parser_save_noexcept instead of the normal processing if needed.
+ (cp_parser_exception_specification_opt): Add FRIEND_P parameter and
+ pass it to cp_parser_noexcept_specification_opt.
+ (cp_parser_save_member_function_body): Fix comment.
+ (cp_parser_save_default_args): Maybe save the noexcept-specifier to
+ post process.
+ (cp_parser_transaction): Update call to
+ cp_parser_noexcept_specification_opt.
+ (cp_parser_transaction_expression): Likewise.
+ * parser.h (cp_unparsed_functions_entry): Add new field to carry
+ a noexcept-specifier.
+ * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
+ * search.c (maybe_check_overriding_exception_spec): New function, broken
+ out of...
+ (check_final_overrider): ...here. Call
+ maybe_check_overriding_exception_spec.
+ * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
+ (cp_tree_equal): Handle DEFAULT_ARG.
+
PR c++/90881 - bogus -Wunused-value in unevaluated context.
* cvt.c (convert_to_void): Don't emit unused warnings in
an unevaluated context.
/* 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
(((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<tree, va_gc> *instantiations;
};
#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;
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);
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. */
&& !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.
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;
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;
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || UNPARSED_NOEXCEPT_SPEC_P (spec)
|| processing_template_decl);
return false;
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 *);
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);
}
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 *);
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);
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:
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)
{
/* 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:
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;
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ const bool literal_p
+ = ((cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
+ || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD))
+ && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_PAREN));
+
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+ /* No need to delay parsing for a number literal or true/false. */
+ && !literal_p
+ && at_class_scope_p ()
+ /* Don't delay parsing for friend member functions. */
+ && !friend_p
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
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;
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;
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
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,
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);
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)
/* 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;
};
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;
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. */
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;
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. */
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. */
case IDENTIFIER_NODE:
case SSA_NAME:
case USING_DECL:
+ case DEFAULT_ARG:
return false;
case BASELINK:
2019-06-22 Marek Polacek <polacek@redhat.com>
+ PR c++/86476 - noexcept-specifier is a complete-class context.
+ PR c++/52869
+ * g++.dg/cpp0x/noexcept45.C: New test.
+ * g++.dg/cpp0x/noexcept46.C: New test.
+ * g++.dg/cpp0x/noexcept47.C: New test.
+ * g++.dg/cpp0x/noexcept48.C: New test.
+ * g++.dg/cpp0x/noexcept49.C: New test.
+ * g++.dg/cpp0x/noexcept50.C: New test.
+ * g++.dg/cpp0x/noexcept51.C: New test.
+ * g++.dg/cpp0x/noexcept52.C: New test.
+ * g++.dg/cpp0x/noexcept53.C: New test.
+ * g++.dg/eh/shadow1.C: Adjust dg-error.
+
PR c++/90881 - bogus -Wunused-value in unevaluated context.
* g++.dg/cpp0x/Wunused-value1.C: New test.
--- /dev/null
+// 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);
+};
--- /dev/null
+// 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);
+};
--- /dev/null
+// 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" }
+};
--- /dev/null
+// 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));
+};
--- /dev/null
+// 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()));
--- /dev/null
+// 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;
+};
--- /dev/null
+// 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); }
--- /dev/null
+// 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>) {}
+};
--- /dev/null
+// 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" }
+};
// { 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 *);