Commit 0998d2fd by Jason Merrill Committed by Jason Merrill

Implement P1286R2, Contra CWG1778

The C++11 requirement that an explicit exception-specification on a
defaulted function match the implicit one was found to be problematic for
std::atomic.  This paper, adopted in February, simply removes that
requirement: if an explicitly defaulted function has a different
exception-specification, that now works just like a user-written function:
either it isn't noexcept when it could be, or it is noexcept and will call
terminate if an exception is thrown.

	* method.c (defaulted_late_check): Don't check explicit
	exception-specification on defaulted function.
	(after_nsdmi_defaulted_late_checks): Remove.
	* parser.h (struct cp_unparsed_functions_entry): Remove classes.
	* parser.c (unparsed_classes): Remove.
	(push_unparsed_function_queues, cp_parser_class_specifier_1):
	Adjust.

From-SVN: r277351
parent cbb28ef1
2019-10-23 Jason Merrill <jason@redhat.com>
Implement P1286R2, Contra CWG1778
* method.c (defaulted_late_check): Don't check explicit
exception-specification on defaulted function.
(after_nsdmi_defaulted_late_checks): Remove.
* parser.h (struct cp_unparsed_functions_entry): Remove classes.
* parser.c (unparsed_classes): Remove.
(push_unparsed_function_queues, cp_parser_class_specifier_1):
Adjust.
2019-10-23 Jakub Jelinek <jakub@redhat.com> 2019-10-23 Jakub Jelinek <jakub@redhat.com>
* constexpr.c (cxx_eval_constant_expression) <case CLEANUP_STMT>: * constexpr.c (cxx_eval_constant_expression) <case CLEANUP_STMT>:
......
...@@ -2204,40 +2204,12 @@ defaulted_late_check (tree fn) ...@@ -2204,40 +2204,12 @@ defaulted_late_check (tree fn)
return; return;
} }
/* 8.4.2/2: An explicitly-defaulted function (...) may have an explicit /* If a function is explicitly defaulted on its first declaration without an
exception-specification only if it is compatible (15.4) with the exception-specification, it is implicitly considered to have the same
exception-specification on the implicit declaration. If a function exception-specification as if it had been implicitly declared. */
is explicitly defaulted on its first declaration, (...) it is if (!TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))
implicitly considered to have the same exception-specification as if && DECL_DEFAULTED_IN_CLASS_P (fn))
it had been implicitly declared. */ TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
maybe_instantiate_noexcept (fn);
tree fn_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
if (!fn_spec)
{
if (DECL_DEFAULTED_IN_CLASS_P (fn))
TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
}
else if (UNEVALUATED_NOEXCEPT_SPEC_P (fn_spec))
/* Equivalent to the implicit spec. */;
else if (DECL_DEFAULTED_IN_CLASS_P (fn)
&& !CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
/* We can't compare an explicit exception-specification on a
constructor defaulted in the class body to the implicit
exception-specification until after we've parsed any NSDMI; see
after_nsdmi_defaulted_late_checks. */;
else
{
tree eh_spec = get_defaulted_eh_spec (fn);
if (!comp_except_specs (fn_spec, eh_spec, ce_normal))
{
if (DECL_DEFAULTED_IN_CLASS_P (fn))
DECL_DELETED_FN (fn) = true;
else
error ("function %q+D defaulted on its redeclaration "
"with an exception-specification that differs from "
"the implicit exception-specification %qX", fn, eh_spec);
}
}
if (DECL_DEFAULTED_IN_CLASS_P (fn) if (DECL_DEFAULTED_IN_CLASS_P (fn)
&& DECL_DECLARED_CONSTEXPR_P (implicit_fn)) && DECL_DECLARED_CONSTEXPR_P (implicit_fn))
...@@ -2264,35 +2236,6 @@ defaulted_late_check (tree fn) ...@@ -2264,35 +2236,6 @@ defaulted_late_check (tree fn)
} }
} }
/* OK, we've parsed the NSDMI for class T, now we can check any explicit
exception-specifications on functions defaulted in the class body. */
void
after_nsdmi_defaulted_late_checks (tree t)
{
if (uses_template_parms (t))
return;
if (t == error_mark_node)
return;
for (tree fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn))
if (!DECL_ARTIFICIAL (fn)
&& DECL_DECLARES_FUNCTION_P (fn)
&& DECL_DEFAULTED_IN_CLASS_P (fn))
{
tree fn_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
if (UNEVALUATED_NOEXCEPT_SPEC_P (fn_spec))
continue;
tree eh_spec = get_defaulted_eh_spec (fn);
if (eh_spec == error_mark_node)
continue;
if (!comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
eh_spec, ce_normal))
DECL_DELETED_FN (fn) = true;
}
}
/* Returns true iff FN can be explicitly defaulted, and gives any /* Returns true iff FN can be explicitly defaulted, and gives any
errors if defaulting FN is ill-formed. */ errors if defaulting FN is ill-formed. */
......
...@@ -2005,16 +2005,13 @@ cp_parser_context_new (cp_parser_context* next) ...@@ -2005,16 +2005,13 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().funs_with_definitions parser->unparsed_queues->last ().funs_with_definitions
#define unparsed_nsdmis \ #define unparsed_nsdmis \
parser->unparsed_queues->last ().nsdmis parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
#define unparsed_noexcepts \ #define unparsed_noexcepts \
parser->unparsed_queues->last ().noexcepts parser->unparsed_queues->last ().noexcepts
static void static void
push_unparsed_function_queues (cp_parser *parser) 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); vec_safe_push (parser->unparsed_queues, e);
} }
...@@ -23754,7 +23751,6 @@ cp_parser_class_specifier_1 (cp_parser* parser) ...@@ -23754,7 +23751,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
error recovery (c++/71169, c++/71832). */ error recovery (c++/71169, c++/71832). */
vec_safe_truncate (unparsed_funs_with_default_args, 0); vec_safe_truncate (unparsed_funs_with_default_args, 0);
vec_safe_truncate (unparsed_nsdmis, 0); vec_safe_truncate (unparsed_nsdmis, 0);
vec_safe_truncate (unparsed_classes, 0);
vec_safe_truncate (unparsed_funs_with_definitions, 0); vec_safe_truncate (unparsed_funs_with_definitions, 0);
} }
...@@ -23809,12 +23805,6 @@ cp_parser_class_specifier_1 (cp_parser* parser) ...@@ -23809,12 +23805,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
if (pushed_scope) if (pushed_scope)
pop_scope (pushed_scope); pop_scope (pushed_scope);
/* Now do some post-NSDMI bookkeeping. */
FOR_EACH_VEC_SAFE_ELT (unparsed_classes, ix, class_type)
after_nsdmi_defaulted_late_checks (class_type);
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
/* If there are noexcept-specifiers that have not yet been processed, /* If there are noexcept-specifiers that have not yet been processed,
take care of them now. */ take care of them now. */
class_type = NULL_TREE; class_type = NULL_TREE;
...@@ -23885,8 +23875,6 @@ cp_parser_class_specifier_1 (cp_parser* parser) ...@@ -23885,8 +23875,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
cp_parser_late_parsing_for_member (parser, decl); cp_parser_late_parsing_for_member (parser, decl);
vec_safe_truncate (unparsed_funs_with_definitions, 0); vec_safe_truncate (unparsed_funs_with_definitions, 0);
} }
else
vec_safe_push (unparsed_classes, type);
/* Put back any saved access checks. */ /* Put back any saved access checks. */
pop_deferring_access_checks (); pop_deferring_access_checks ();
...@@ -163,10 +163,6 @@ struct GTY(()) cp_unparsed_functions_entry { ...@@ -163,10 +163,6 @@ struct GTY(()) cp_unparsed_functions_entry {
FIELD_DECLs appear in this list in declaration order. */ FIELD_DECLs appear in this list in declaration order. */
vec<tree, va_gc> *nsdmis; vec<tree, va_gc> *nsdmis;
/* 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. */ /* Functions with noexcept-specifiers that require post-processing. */
vec<tree, va_gc> *noexcepts; vec<tree, va_gc> *noexcepts;
}; };
......
// P1286R2: Contra CWG1778
// { dg-do compile { target c++11 } }
struct T { T(); T(T &&) noexcept(false); };
struct U { T t; U(); U(U &&) noexcept = default; };
U u1;
U u2 = static_cast<U&&>(u1); // OK, calls std::terminate if T::T(T&&) throws
...@@ -10,10 +10,10 @@ A a; ...@@ -10,10 +10,10 @@ A a;
struct B struct B
{ {
B() throw (int) = default; // { dg-message "exception-specification" "" { target { ! c++17 } } } B() throw (int) = default;
}; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 } }; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
// { dg-warning "deprecated" "" { target { ! c++17 } } .-2 } // { dg-warning "deprecated" "" { target { ! c++17 } } .-2 }
B b; // { dg-error "deleted" "" { target { ! c++17 } } } B b;
struct C struct C
{ {
......
...@@ -17,8 +17,8 @@ struct A ...@@ -17,8 +17,8 @@ struct A
T t; T t;
}; };
A::A() noexcept = default; // { dg-error "defaulted" } A::A() noexcept = default;
A::~A() noexcept = default; // { dg-error "defaulted" } A::~A() noexcept = default;
struct U struct U
{ {
...@@ -51,10 +51,10 @@ V v; ...@@ -51,10 +51,10 @@ V v;
struct C struct C
{ {
C() noexcept = default; // { dg-message "exception-specification" } C() noexcept = default;
~C() noexcept = default; // { dg-message "exception-specification" } ~C() noexcept = default;
V v; V v;
}; };
C c; // { dg-error "deleted" } C c;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment