diff options
author | Jason Merrill <jason@redhat.com> | 2016-11-07 18:09:29 -0500 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2016-11-07 18:09:29 -0500 |
commit | 51dc660315ef83dcb907f3bd3a0a56abb9efed7a (patch) | |
tree | c9f9278ab8d390747f291ff0e7146959604797d2 /gcc | |
parent | 452811eb53e9c0457f99f4f23a4ca10354c088e1 (diff) | |
download | gcc-51dc660315ef83dcb907f3bd3a0a56abb9efed7a.zip gcc-51dc660315ef83dcb907f3bd3a0a56abb9efed7a.tar.gz gcc-51dc660315ef83dcb907f3bd3a0a56abb9efed7a.tar.bz2 |
Implement P0012R1, Make exception specifications part of the type system.
gcc/cp/
* cp-tree.h (enum tsubst_flags): Add tf_fndecl_type.
(flag_noexcept_type, ce_type): New.
* call.c (build_conv): Add ck_fnptr.
(enum conversion_kind): Change ck_tsafe to ck_fnptr.
(convert_like_real): Likewise.
(standard_conversion): Likewise. Allow function pointer
conversions for pointers to member functions.
(reference_compatible_p): Allow function pointer conversions.
(direct_reference_binding): Likewise.
(reference_binding): Reference-compatible is no longer a subset of
reference-related.
(is_subseq): Also strip ck_lvalue after next_conversion.
* class.c (instantiate_type): Check fnptr_conv_p.
(resolve_address_of_overloaded_function): Likewise.
* cvt.c (can_convert_tx_safety): Now static.
(noexcept_conv_p, fnptr_conv_p, strip_fnptr_conv): New.
* decl.c (flag_noexcept_type): Define.
(cxx_init_decl_processing): Set it.
(bad_specifiers): Check it.
(grokdeclarator) [cdk_function]: Add exception-spec to type here.
* lambda.c (maybe_add_lambda_conv_op): Add exception-spec to
returned pointer.
* mangle.c (struct globals): Add need_cxx1z_warning.
(mangle_decl): Check it.
(write_exception_spec): New.
(write_function_type): Call it.
(canonicalize_for_substitution): Handle exception spec.
(write_type): Likewise.
(write_encoding): Set processing_template_decl across mangling of
partially-instantiated type.
* pt.c (determine_specialization): Pass tf_fndecl_type.
(tsubst_decl, fn_type_unification): Likewise.
(tsubst): Strip tf_fndecl_type, pass it to
tsubst_exception_specification.
(convert_nontype_argument_function): Handle function pointer
conversion.
(convert_nontype_argument): Likewise.
(unify, for_each_template_parm_r): Walk into noexcept-specifier.
* rtti.c (ptr_initializer): Encode noexcept.
* tree.c (canonical_eh_spec): New.
(build_exception_variant): Use it.
* typeck.c (composite_pointer_type): Handle fnptr conversion.
(comp_except_specs): Compare canonical EH specs.
(structural_comptypes): Call it.
gcc/c-family/
* c.opt (Wc++1z-compat): New.
* c-cppbuiltin.c (c_cpp_builtins): Add __cpp_noexcept_function_type.
libstdc++-v3/
* include/bits/c++config (_GLIBCXX_NOEXCEPT_PARM)
(_GLIBCXX_NOEXCEPT_QUAL): New.
* include/std/type_traits (is_function): Use them.
* libsubc++/new (launder): Likewise.
* libsupc++/cxxabi.h (__pbase_type_info::__masks): Add
__noexcept_mask.
* libsupc++/pbase_type_info.cc (__do_catch): Handle function
pointer conversion.
libiberty/
* cp-demangle.c (is_fnqual_component_type): New.
(d_encoding, d_print_comp_inner, d_print_mod_list): Use it.
(FNQUAL_COMPONENT_CASE): New.
(d_make_comp, has_return_type, d_print_comp_inner)
(d_print_function_type): Use it.
(next_is_type_qual): New.
(d_cv_qualifiers, d_print_mod): Handle noexcept and throw-spec.
include/
* demangle.h (enum demangle_component_type): Add
DEMANGLE_COMPONENT_NOEXCEPT, DEMANGLE_COMPONENT_THROW_SPEC.
From-SVN: r241944
Diffstat (limited to 'gcc')
30 files changed, 579 insertions, 78 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 39e6de1..5207c34 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2016-11-07 Jason Merrill <jason@redhat.com> + + * c.opt (Wc++1z-compat): New. + * c-cppbuiltin.c (c_cpp_builtins): Add __cpp_noexcept_function_type. + 2016-11-07 Martin Liska <mliska@suse.cz> * c-warn.c (warn_for_unused_label): Save all labels used diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index e40fa6f..55dbf44 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -941,6 +941,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_inline_variables=201606"); cpp_define (pfile, "__cpp_aggregate_bases=201603"); cpp_define (pfile, "__cpp_deduction_guides=201606"); + cpp_define (pfile, "__cpp_noexcept_function_type=201510"); } if (flag_concepts) cpp_define (pfile, "__cpp_concepts=201507"); diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 7d8a726..213353b 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -360,6 +360,13 @@ Wc++14-compat C++ ObjC++ Var(warn_cxx14_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Warn about C++ constructs whose meaning differs between ISO C++ 2011 and ISO C++ 2014. +Wc++1z-compat +C++ ObjC++ Var(warn_cxx1z_compat) Warning LangEnabledBy(C++ ObjC++,Wall) +Warn about C++ constructs whose meaning differs between ISO C++ 2014 and (forthcoming) ISO C++ 201z(7?). + +Wc++17-compat +C++ ObjC++ Warning Alias(Wc++1z-compat) Undocumented + Wcast-qual C ObjC C++ ObjC++ Var(warn_cast_qual) Warning Warn about casts which discard qualifiers. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a65ddec..f325ccc 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,52 @@ 2016-11-07 Jason Merrill <jason@redhat.com> + Implement P0012R1, Make exception specifications part of the type + system. + * cp-tree.h (enum tsubst_flags): Add tf_fndecl_type. + (flag_noexcept_type, ce_type): New. + * call.c (build_conv): Add ck_fnptr. + (enum conversion_kind): Change ck_tsafe to ck_fnptr. + (convert_like_real): Likewise. + (standard_conversion): Likewise. Allow function pointer + conversions for pointers to member functions. + (reference_compatible_p): Allow function pointer conversions. + (direct_reference_binding): Likewise. + (reference_binding): Reference-compatible is no longer a subset of + reference-related. + (is_subseq): Also strip ck_lvalue after next_conversion. + * class.c (instantiate_type): Check fnptr_conv_p. + (resolve_address_of_overloaded_function): Likewise. + * cvt.c (can_convert_tx_safety): Now static. + (noexcept_conv_p, fnptr_conv_p, strip_fnptr_conv): New. + * decl.c (flag_noexcept_type): Define. + (cxx_init_decl_processing): Set it. + (bad_specifiers): Check it. + (grokdeclarator) [cdk_function]: Add exception-spec to type here. + * lambda.c (maybe_add_lambda_conv_op): Add exception-spec to + returned pointer. + * mangle.c (struct globals): Add need_cxx1z_warning. + (mangle_decl): Check it. + (write_exception_spec): New. + (write_function_type): Call it. + (canonicalize_for_substitution): Handle exception spec. + (write_type): Likewise. + (write_encoding): Set processing_template_decl across mangling of + partially-instantiated type. + * pt.c (determine_specialization): Pass tf_fndecl_type. + (tsubst_decl, fn_type_unification): Likewise. + (tsubst): Strip tf_fndecl_type, pass it to + tsubst_exception_specification. + (convert_nontype_argument_function): Handle function pointer + conversion. + (convert_nontype_argument): Likewise. + (unify, for_each_template_parm_r): Walk into noexcept-specifier. + * rtti.c (ptr_initializer): Encode noexcept. + * tree.c (canonical_eh_spec): New. + (build_exception_variant): Use it. + * typeck.c (composite_pointer_type): Handle fnptr conversion. + (comp_except_specs): Compare canonical EH specs. + (structural_comptypes): Call it. + * call.c (standard_conversion): Reorganize pointer conversions. * pt.c (convert_nontype_argument_function): Convert to ref here. (convert_nontype_argument): Not here. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 0466cd1..0dcf322 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -45,7 +45,7 @@ along with GCC; see the file COPYING3. If not see enum conversion_kind { ck_identity, ck_lvalue, - ck_tsafe, + ck_fnptr, ck_qual, ck_std, ck_ptr, @@ -771,6 +771,7 @@ build_conv (conversion_kind code, tree type, conversion *from) break; case ck_qual: + case ck_fnptr: if (rank < cr_exact) rank = cr_exact; break; @@ -1285,18 +1286,6 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, conv = build_conv (ck_ptr, from, conv); conv->base_p = true; } - else if (tx_safe_fn_type_p (from_pointee)) - { - /* A prvalue of type "pointer to transaction_safe function" can be - converted to a prvalue of type "pointer to function". */ - tree unsafe = tx_unsafe_fn_variant (from_pointee); - if (same_type_p (unsafe, to_pointee)) - { - from_pointee = unsafe; - from = build_pointer_type (unsafe); - conv = build_conv (ck_tsafe, from, conv); - } - } if (same_type_p (from, to)) /* OK */; @@ -1310,6 +1299,8 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, else if (expr && string_conv_p (to, expr, 0)) /* converting from string constant to char *. */ conv = build_conv (ck_qual, to, conv); + else if (fnptr_conv_p (to, from)) + conv = build_conv (ck_fnptr, to, conv); /* Allow conversions among compatible ObjC pointer types (base conversions have been already handled above). */ else if (c_dialect_objc () @@ -1332,18 +1323,29 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, tree fbase = class_of_this_parm (fromfn); tree tbase = class_of_this_parm (tofn); - if (!DERIVED_FROM_P (fbase, tbase) - || !same_type_p (static_fn_type (fromfn), - static_fn_type (tofn))) + if (!DERIVED_FROM_P (fbase, tbase)) + return NULL; + + tree fstat = static_fn_type (fromfn); + tree tstat = static_fn_type (tofn); + if (same_type_p (tstat, fstat) + || fnptr_conv_p (tstat, fstat)) + /* OK */; + else return NULL; - from = build_memfn_type (fromfn, - tbase, - cp_type_quals (tbase), - type_memfn_rqual (tofn)); - from = build_ptrmemfunc_type (build_pointer_type (from)); - conv = build_conv (ck_pmem, from, conv); - conv->base_p = true; + if (!same_type_p (fbase, tbase)) + { + from = build_memfn_type (fstat, + tbase, + cp_type_quals (tbase), + type_memfn_rqual (tofn)); + from = build_ptrmemfunc_type (build_pointer_type (from)); + conv = build_conv (ck_pmem, from, conv); + conv->base_p = true; + } + if (fnptr_conv_p (tstat, fstat)) + conv = build_conv (ck_fnptr, to, conv); } else if (tcode == BOOLEAN_TYPE) { @@ -1441,10 +1443,14 @@ reference_compatible_p (tree t1, tree t2) { /* [dcl.init.ref] - "cv1 T1" is reference compatible with "cv2 T2" if T1 is - reference-related to T2 and cv1 is the same cv-qualification as, - or greater cv-qualification than, cv2. */ - return (reference_related_p (t1, t2) + "cv1 T1" is reference compatible with "cv2 T2" if + * T1 is reference-related to T2 or + * T2 is "noexcept function" and T1 is "function", where the + function types are otherwise the same, + and cv1 is the same cv-qualification as, or greater cv-qualification + than, cv2. */ + return ((reference_related_p (t1, t2) + || fnptr_conv_p (t1, t2)) && at_least_as_qualified_p (t1, t2)); } @@ -1478,7 +1484,7 @@ direct_reference_binding (tree type, conversion *conv) either an identity conversion or, if the conversion function returns an entity of a type that is a derived class of the parameter type, a derived-to-base conversion. */ - if (!same_type_ignoring_top_level_qualifiers_p (t, conv->type)) + if (is_properly_derived_from (conv->type, t)) { /* Represent the derived-to-base conversion. */ conv = build_conv (ck_base, t, conv); @@ -1591,7 +1597,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, [8.5.3/5 dcl.init.ref] is changed to also require direct bindings for const and rvalue references to rvalues of compatible class type. We should also do direct bindings for non-class xvalues. */ - if (related_p && gl_kind) + if ((related_p || compatible_p) && gl_kind) { /* [dcl.init.ref] @@ -6978,9 +6984,9 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, case ck_lvalue: return decay_conversion (expr, complain); - case ck_tsafe: + case ck_fnptr: /* ??? Should the address of a transaction-safe pointer point to the TM - clone, and this conversion look up the primary function? */ + clone, and this conversion look up the primary function? */ return build_nop (totype, expr); case ck_qual: @@ -8863,10 +8869,15 @@ is_subseq (conversion *ics1, conversion *ics2) ics2 = next_conversion (ics2); + while (ics2->kind == ck_rvalue + || ics2->kind == ck_lvalue) + ics2 = next_conversion (ics2); + if (ics2->kind == ics1->kind && same_type_p (ics2->type, ics1->type) - && same_type_p (next_conversion (ics2)->type, - next_conversion (ics1)->type)) + && (ics1->kind == ck_identity + || same_type_p (next_conversion (ics2)->type, + next_conversion (ics1)->type))) return true; } } diff --git a/gcc/cp/class.c b/gcc/cp/class.c index c6b4ed6..5460ae5 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -8177,10 +8177,14 @@ resolve_address_of_overloaded_function (tree target_type, if (DECL_ANTICIPATED (fn)) continue; + /* In C++17 we need the noexcept-qualifier to compare types. */ + if (flag_noexcept_type) + maybe_instantiate_noexcept (fn); + /* See if there's a match. */ tree fntype = static_fn_type (fn); if (same_type_p (target_fn_type, fntype) - || can_convert_tx_safety (target_fn_type, fntype)) + || fnptr_conv_p (target_fn_type, fntype)) matches = tree_cons (fn, NULL_TREE, matches); } } @@ -8257,10 +8261,14 @@ resolve_address_of_overloaded_function (tree target_type, require_deduced_type (instantiation); } + /* In C++17 we need the noexcept-qualifier to compare types. */ + if (flag_noexcept_type) + maybe_instantiate_noexcept (instantiation); + /* See if there's a match. */ tree fntype = static_fn_type (instantiation); if (same_type_p (target_fn_type, fntype) - || can_convert_tx_safety (target_fn_type, fntype)) + || fnptr_conv_p (target_fn_type, fntype)) matches = tree_cons (instantiation, fn, matches); } @@ -8424,6 +8432,8 @@ instantiate_type (tree lhstype, tree rhs, tsubst_flags_t complain) tree fntype = non_reference (lhstype); if (same_type_p (fntype, TREE_TYPE (rhs))) return rhs; + if (fnptr_conv_p (fntype, TREE_TYPE (rhs))) + return rhs; if (flag_ms_extensions && TYPE_PTRMEMFUNC_P (fntype) && !TYPE_PTRMEMFUNC_P (TREE_TYPE (rhs))) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d3a5aeb..20b52ad 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4744,6 +4744,8 @@ enum tsubst_flags { for calls in decltype (5.2.2/11). */ tf_partial = 1 << 8, /* Doing initial explicit argument substitution in fn_type_unification. */ + tf_fndecl_type = 1 << 9, /* Substituting the type of a function + declaration. */ /* Convenient substitution flags combinations. */ tf_warning_or_error = tf_warning | tf_error }; @@ -4949,6 +4951,10 @@ extern int at_eof; extern bool defer_mangling_aliases; +/* True if noexcept is part of the type (i.e. in C++17). */ + +extern bool flag_noexcept_type; + /* A list of namespace-scope objects which have constructors or destructors which reside in the global scope. The decl is stored in the TREE_VALUE slot and the initializer is stored in the @@ -5737,7 +5743,8 @@ extern tree type_promotes_to (tree); extern tree perform_qualification_conversions (tree, tree); extern bool tx_safe_fn_type_p (tree); extern tree tx_unsafe_fn_variant (tree); -extern bool can_convert_tx_safety (tree, tree); +extern bool fnptr_conv_p (tree, tree); +extern tree strip_fnptr_conv (tree); /* in name-lookup.c */ extern tree pushdecl (tree); @@ -6577,6 +6584,7 @@ extern tree build_overload (tree, tree); extern tree ovl_scope (tree); extern const char *cxx_printable_name (tree, int); extern const char *cxx_printable_name_translate (tree, int); +extern tree canonical_eh_spec (tree); extern tree build_exception_variant (tree, tree); extern tree bind_template_template_parm (tree, tree); extern tree array_type_nelts_total (tree); @@ -6648,7 +6656,7 @@ extern tree complete_type (tree); extern tree complete_type_or_else (tree, tree); extern tree complete_type_or_maybe_complain (tree, tree, tsubst_flags_t); extern int type_unknown_p (const_tree); -enum { ce_derived, ce_normal, ce_exact }; +enum { ce_derived, ce_type, ce_normal, ce_exact }; extern bool comp_except_specs (const_tree, const_tree, int); extern bool comptypes (tree, tree, int); extern bool same_type_ignoring_top_level_qualifiers_p (tree, tree); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 2f5f15a..400566f 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1932,9 +1932,84 @@ tx_unsafe_fn_variant (tree t) /* Return true iff FROM can convert to TO by a transaction-safety conversion. */ -bool +static bool can_convert_tx_safety (tree to, tree from) { return (flag_tm && tx_safe_fn_type_p (from) && same_type_p (to, tx_unsafe_fn_variant (from))); } + +/* Return true iff FROM can convert to TO by dropping noexcept. */ + +static bool +noexcept_conv_p (tree to, tree from) +{ + if (!flag_noexcept_type) + return false; + + tree t = non_reference (to); + tree f = from; + if (TYPE_PTRMEMFUNC_P (t) + && TYPE_PTRMEMFUNC_P (f)) + { + t = TYPE_PTRMEMFUNC_FN_TYPE (t); + f = TYPE_PTRMEMFUNC_FN_TYPE (f); + } + if (TREE_CODE (t) == POINTER_TYPE + && TREE_CODE (f) == POINTER_TYPE) + { + t = TREE_TYPE (t); + f = TREE_TYPE (f); + } + tree_code code = TREE_CODE (f); + if (TREE_CODE (t) != code) + return false; + if (code != FUNCTION_TYPE && code != METHOD_TYPE) + return false; + if (!type_throw_all_p (t) + || type_throw_all_p (f)) + return false; + tree v = build_exception_variant (f, NULL_TREE); + return same_type_p (t, v); +} + +/* Return true iff FROM can convert to TO by a function pointer conversion. */ + +bool +fnptr_conv_p (tree to, tree from) +{ + tree t = non_reference (to); + tree f = from; + if (TYPE_PTRMEMFUNC_P (t) + && TYPE_PTRMEMFUNC_P (f)) + { + t = TYPE_PTRMEMFUNC_FN_TYPE (t); + f = TYPE_PTRMEMFUNC_FN_TYPE (f); + } + if (TREE_CODE (t) == POINTER_TYPE + && TREE_CODE (f) == POINTER_TYPE) + { + t = TREE_TYPE (t); + f = TREE_TYPE (f); + } + + return (noexcept_conv_p (t, f) + || can_convert_tx_safety (t, f)); +} + +/* Return FN with any NOP_EXPRs that represent function pointer + conversions stripped. */ + +tree +strip_fnptr_conv (tree fn) +{ + while (TREE_CODE (fn) == NOP_EXPR) + { + tree op = TREE_OPERAND (fn, 0); + if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op))) + fn = op; + else + break; + } + return fn; +} diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index ecf4d14..c0321f9 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -73,8 +73,6 @@ static int check_static_variable_definition (tree, tree); static void record_unknown_type (tree, const char *); static tree builtin_function_1 (tree, tree, bool); static int member_function_or_else (tree, tree, enum overload_flags); -static void bad_specifiers (tree, enum bad_spec_place, int, int, int, int, - int); static void check_for_uninitialized_const_var (tree); static tree local_variable_p_walkfn (tree *, int *, void *); static const char *tag_name (enum tag_types); @@ -227,6 +225,9 @@ struct GTY((for_user)) named_label_entry { function, two inside the body of a function in a local class, etc.) */ int function_depth; +/* Whether the exception-specifier is part of a function type (i.e. C++17). */ +bool flag_noexcept_type; + /* States indicating how grokdeclarator() should handle declspecs marked with __attribute__((deprecated)). An object declared as __attribute__((deprecated)) suppresses warnings of uses of other @@ -4044,6 +4045,8 @@ cxx_init_decl_processing (void) std_node = current_namespace; pop_namespace (); + flag_noexcept_type = (cxx_dialect >= cxx1z); + c_common_nodes_and_builtins (); integer_two_node = build_int_cst (NULL_TREE, 2); @@ -7842,6 +7845,7 @@ bad_specifiers (tree object, if (friendp) error ("%q+D declared as a friend", object); if (raises + && !flag_noexcept_type && (TREE_CODE (object) == TYPE_DECL || (!TYPE_PTRFN_P (TREE_TYPE (object)) && !TYPE_REFFN_P (TREE_TYPE (object)) @@ -10477,6 +10481,9 @@ grokdeclarator (const cp_declarator *declarator, The optional attribute-specifier-seq appertains to the function type. */ decl_attributes (&type, attrs, 0); + + if (raises) + type = build_exception_variant (type, raises); } break; diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index d4284bf..c48cd52 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1029,6 +1029,9 @@ maybe_add_lambda_conv_op (tree type) tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop)); stattype = (cp_build_type_attribute_variant (stattype, TYPE_ATTRIBUTES (optype))); + if (flag_noexcept_type + && TYPE_NOTHROW_P (TREE_TYPE (callop))) + stattype = build_exception_variant (stattype, noexcept_true_spec); /* First build up the conversion op. */ diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index f3b2fe3..a354ec5 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -114,6 +114,9 @@ struct GTY(()) globals { /* True if the mangling will be different in a future version of the ABI. */ bool need_abi_warning; + + /* True if the mangling will be different in C++17 mode. */ + bool need_cxx1z_warning; }; static GTY (()) globals G; @@ -344,6 +347,43 @@ dump_substitution_candidates (void) } } +/* <exception-spec> ::= + Do -- non-throwing exception specification + DO <expression> E -- computed (instantiation-dependent) noexcept + Dw <type>* E -- throw (types) */ + +static void +write_exception_spec (tree spec) +{ + + if (!spec || spec == noexcept_false_spec) + /* Nothing. */ + return; + + if (!flag_noexcept_type) + { + G.need_cxx1z_warning = true; + return; + } + + if (nothrow_spec_p (spec)) + write_string ("Do"); + else if (TREE_PURPOSE (spec)) + { + gcc_assert (uses_template_parms (TREE_PURPOSE (spec))); + write_string ("DO"); + write_expression (TREE_PURPOSE (spec)); + write_char ('E'); + } + else + { + write_string ("Dw"); + for (tree t = spec; t; t = TREE_CHAIN (t)) + write_type (TREE_VALUE (t)); + write_char ('E'); + } +} + /* Both decls and types can be substitution candidates, but sometimes they refer to the same thing. For instance, a TYPE_DECL and RECORD_TYPE for the same class refer to the same thing, and should @@ -375,7 +415,15 @@ canonicalize_for_substitution (tree node) cp_type_quals (node)); if (TREE_CODE (node) == FUNCTION_TYPE || TREE_CODE (node) == METHOD_TYPE) - node = build_ref_qualified_type (node, type_memfn_rqual (orig)); + { + node = build_ref_qualified_type (node, type_memfn_rqual (orig)); + tree r = canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (orig)); + if (flag_noexcept_type) + node = build_exception_variant (node, r); + else + /* Set the warning flag if appropriate. */ + write_exception_spec (r); + } } return node; } @@ -777,9 +825,11 @@ write_encoding (const tree decl) { tree fn_type; tree d; + bool tmpl = decl_is_template_id (decl, NULL); - if (decl_is_template_id (decl, NULL)) + if (tmpl) { + ++processing_template_decl; fn_type = get_mostly_instantiated_function_type (decl); /* FN_TYPE will not have parameter types for in-charge or VTT parameters. Therefore, we pass NULL_TREE to @@ -796,6 +846,9 @@ write_encoding (const tree decl) write_bare_function_type (fn_type, mangle_return_type_p (decl), d); + + if (tmpl) + --processing_template_decl; } } @@ -2064,7 +2117,11 @@ write_type (tree type) type = TYPE_MAIN_VARIANT (type); if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) - type = build_ref_qualified_type (type, type_memfn_rqual (type_orig)); + { + type = build_ref_qualified_type (type, type_memfn_rqual (type_orig)); + type = build_exception_variant (type, + TYPE_RAISES_EXCEPTIONS (type_orig)); + } /* According to the C++ ABI, some library classes are passed the same as the scalar type of their single member and use the same @@ -2589,6 +2646,8 @@ write_function_type (const tree type) write_CV_qualifiers_for_type (this_type); } + write_exception_spec (TYPE_RAISES_EXCEPTIONS (type)); + if (tx_safe_fn_type_p (type)) write_string ("Dx"); @@ -3776,6 +3835,12 @@ mangle_decl (const tree decl) } SET_DECL_ASSEMBLER_NAME (decl, id); + if (G.need_cxx1z_warning) + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wc__1z_compat, + "mangled name for %qD will change in C++17 because the " + "exception specification is part of a function type", + decl); + if (id != DECL_NAME (decl) /* Don't do this for a fake symbol we aren't going to emit anyway. */ && TREE_CODE (decl) != TYPE_DECL diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 45965aa..3df71dd 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -2216,7 +2216,7 @@ determine_specialization (tree template_id, continue; // Then, try to form the new function type. - insttype = tsubst (TREE_TYPE (fn), targs, tf_none, NULL_TREE); + insttype = tsubst (TREE_TYPE (fn), targs, tf_fndecl_type, NULL_TREE); if (insttype == error_mark_node) continue; fn_arg_types @@ -5876,7 +5876,7 @@ get_underlying_template (tree tmpl) } /* Subroutine of convert_nontype_argument. Converts EXPR to TYPE, which - must be a function or a pointer-to-function type, as specified + must be a reference-to-function or a pointer-to-function type, as specified in [temp.arg.nontype]: disambiguate EXPR if it is an overload set, and check that the resulting function has external linkage. */ @@ -5892,7 +5892,7 @@ convert_nontype_argument_function (tree type, tree expr, if (fn == error_mark_node) return error_mark_node; - fn_no_ptr = fn; + fn_no_ptr = strip_fnptr_conv (fn); if (TREE_CODE (fn_no_ptr) == ADDR_EXPR) fn_no_ptr = TREE_OPERAND (fn_no_ptr, 0); if (BASELINK_P (fn_no_ptr)) @@ -6672,6 +6672,11 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) && !check_valid_ptrmem_cst_expr (type, expr, complain)) return error_mark_node; + /* Repeated conversion can't deal with a conversion that turns PTRMEM_CST + into a CONSTRUCTOR, so build up a new PTRMEM_CST instead. */ + if (fnptr_conv_p (type, TREE_TYPE (expr))) + expr = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr)); + /* There is no way to disable standard conversions in resolve_address_of_overloaded_function (called by instantiate_type). It is possible that the call succeeded by @@ -8861,6 +8866,13 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) want walk_tree walking into them itself. */ *walk_subtrees = 0; } + + if (flag_noexcept_type) + { + tree spec = TYPE_RAISES_EXCEPTIONS (t); + if (spec) + WALK_SUBTREE (TREE_PURPOSE (spec)); + } break; case TYPEOF_TYPE: @@ -11932,7 +11944,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) member = 0; ctx = DECL_CONTEXT (t); } - type = tsubst (TREE_TYPE (t), args, complain, in_decl); + type = tsubst (TREE_TYPE (t), args, complain|tf_fndecl_type, in_decl); if (type == error_mark_node) RETURN (error_mark_node); @@ -13015,6 +13027,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) } } + bool fndecl_type = (complain & tf_fndecl_type); + complain &= ~tf_fndecl_type; + if (type && code != TYPENAME_TYPE && code != TEMPLATE_TYPE_PARM @@ -13512,8 +13527,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) return error_mark_node; /* Substitute the exception specification. */ - specs = tsubst_exception_specification (t, args, complain, - in_decl, /*defer_ok*/true); + specs = tsubst_exception_specification (t, args, complain, in_decl, + /*defer_ok*/fndecl_type); if (specs == error_mark_node) return error_mark_node; if (specs) @@ -17991,7 +18006,7 @@ fn_type_unification (tree fn, access path at this point. */ push_deferring_access_checks (dk_deferred); fntype = tsubst (TREE_TYPE (fn), explicit_targs, - complain | tf_partial, NULL_TREE); + complain | tf_partial | tf_fndecl_type, NULL_TREE); pop_deferring_access_checks (); input_location = loc; processing_template_decl -= incomplete; @@ -20333,9 +20348,27 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, args[i] = TREE_VALUE (a); nargs = i; - return type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm), - args, nargs, 1, DEDUCE_EXACT, - LOOKUP_NORMAL, NULL, explain_p); + if (type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm), + args, nargs, 1, DEDUCE_EXACT, + LOOKUP_NORMAL, NULL, explain_p)) + return 1; + + if (flag_noexcept_type) + { + tree pspec = TYPE_RAISES_EXCEPTIONS (parm); + tree aspec = canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (arg)); + if (pspec == NULL_TREE) pspec = noexcept_false_spec; + if (aspec == NULL_TREE) aspec = noexcept_false_spec; + if (TREE_PURPOSE (pspec) && TREE_PURPOSE (aspec) + && uses_template_parms (TREE_PURPOSE (pspec))) + RECUR_AND_CHECK_FAILURE (tparms, targs, TREE_PURPOSE (pspec), + TREE_PURPOSE (aspec), + UNIFY_ALLOW_NONE, explain_p); + else if (nothrow_spec_p (pspec) && !nothrow_spec_p (aspec)) + return unify_type_mismatch (explain_p, parm, arg); + } + + return 0; } case OFFSET_TYPE: diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index cfbc3d4..247a98f 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -985,6 +985,14 @@ ptr_initializer (tinfo_s *ti, tree target) flags |= 0x20; to = tx_unsafe_fn_variant (to); } + if (flag_noexcept_type + && (TREE_CODE (to) == FUNCTION_TYPE + || TREE_CODE (to) == METHOD_TYPE) + && TYPE_NOTHROW_P (to)) + { + flags |= 0x40; + to = build_exception_variant (to, NULL_TREE); + } CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, flags)); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 4dc6e22..7872dd2 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2209,6 +2209,27 @@ cxx_printable_name_translate (tree decl, int v) return cxx_printable_name_internal (decl, v, true); } +/* Return the canonical version of exception-specification RAISES for a C++17 + function type, for use in type comparison and building TYPE_CANONICAL. */ + +tree +canonical_eh_spec (tree raises) +{ + if (raises == NULL_TREE) + return raises; + else if (DEFERRED_NOEXCEPT_SPEC_P (raises) + || uses_template_parms (raises) + || uses_template_parms (TREE_PURPOSE (raises))) + /* Keep a dependent or deferred exception specification. */ + return raises; + else if (nothrow_spec_p (raises)) + /* throw() -> noexcept. */ + return noexcept_true_spec; + else + /* For C++17 type matching, anything else -> nothing. */ + return NULL_TREE; +} + /* Build the FUNCTION_TYPE or METHOD_TYPE which may throw exceptions listed in RAISES. */ @@ -2230,6 +2251,25 @@ build_exception_variant (tree type, tree raises) /* Need to build a new variant. */ v = build_variant_type_copy (type); TYPE_RAISES_EXCEPTIONS (v) = raises; + + if (!flag_noexcept_type) + /* The exception-specification is not part of the canonical type. */ + return v; + + /* Canonicalize the exception specification. */ + tree cr = canonical_eh_spec (raises); + + if (TYPE_STRUCTURAL_EQUALITY_P (type)) + /* Propagate structural equality. */ + SET_TYPE_STRUCTURAL_EQUALITY (v); + else if (TYPE_CANONICAL (type) != type || cr != raises) + /* Build the underlying canonical type, since it is different + from TYPE. */ + TYPE_CANONICAL (v) = build_exception_variant (TYPE_CANONICAL (type), cr); + else + /* T is its own canonical type. */ + TYPE_CANONICAL (v) = v; + return v; } diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 4ff2bc2..211696c 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -647,6 +647,14 @@ composite_pointer_type (tree t1, tree t2, tree arg1, tree arg2, return objc_common_type (t1, t2); } + /* if T1 or T2 is "pointer to noexcept function" and the other type is + "pointer to function", where the function types are otherwise the same, + "pointer to function" */ + if (fnptr_conv_p (t1, t2)) + return t1; + if (fnptr_conv_p (t2, t1)) + return t2; + /* [expr.eq] permits the application of a pointer conversion to bring the pointers to a common type. */ if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2) @@ -710,22 +718,6 @@ composite_pointer_type (tree t1, tree t2, tree arg1, tree arg2, return error_mark_node; } } - else if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2) - && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (t1)) - && TREE_CODE (TREE_TYPE (t2)) == TREE_CODE (TREE_TYPE (t1))) - { - /* ...if T1 is "pointer to transaction_safe function" and T2 is "pointer - to function", where the function types are otherwise the same, T2, and - vice versa.... */ - tree f1 = TREE_TYPE (t1); - tree f2 = TREE_TYPE (t2); - bool safe1 = tx_safe_fn_type_p (f1); - bool safe2 = tx_safe_fn_type_p (f2); - if (safe1 && !safe2) - t1 = build_pointer_type (tx_unsafe_fn_variant (f1)); - else if (safe2 && !safe1) - t2 = build_pointer_type (tx_unsafe_fn_variant (f2)); - } return composite_pointer_type_r (t1, t2, operation, complain); } @@ -1020,6 +1012,7 @@ comp_except_types (tree a, tree b, bool exact) /* Return true if TYPE1 and TYPE2 are equivalent exception specifiers. If EXACT is ce_derived, T2 can be stricter than T1 (according to 15.4/5). + If EXACT is ce_type, the C++17 type compatibility rules apply. If EXACT is ce_normal, the compatibility rules in 15.4/3 apply. If EXACT is ce_exact, the specs must be exactly the same. Exception lists are unordered, but we've already filtered out duplicates. Most lists will @@ -1038,8 +1031,13 @@ comp_except_specs (const_tree t1, const_tree t2, int exact) /* First handle noexcept. */ if (exact < ce_exact) { + if (exact == ce_type + && (canonical_eh_spec (CONST_CAST_TREE (t1)) + == canonical_eh_spec (CONST_CAST_TREE (t2)))) + return true; + /* noexcept(false) is compatible with no exception-specification, - and stricter than any spec. */ + and less strict than any spec. */ if (t1 == noexcept_false_spec) return t2 == NULL_TREE || exact == ce_derived; /* Even a derived noexcept(false) is compatible with no @@ -1222,10 +1220,17 @@ structural_comptypes (tree t1, tree t2, int strict) return false; /* Need to check this before TYPE_MAIN_VARIANT. FIXME function qualifiers should really change the main variant. */ - if ((TREE_CODE (t1) == FUNCTION_TYPE - || TREE_CODE (t1) == METHOD_TYPE) - && type_memfn_rqual (t1) != type_memfn_rqual (t2)) - return false; + if (TREE_CODE (t1) == FUNCTION_TYPE + || TREE_CODE (t1) == METHOD_TYPE) + { + if (type_memfn_rqual (t1) != type_memfn_rqual (t2)) + return false; + if (flag_noexcept_type + && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (t1), + TYPE_RAISES_EXCEPTIONS (t2), + ce_type)) + return false; + } /* Allow for two different type nodes which have essentially the same definition. Note that we already checked for equality of the type diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index e0afc269..17c5c22 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -5574,6 +5574,11 @@ enabled by @option{-Wall}. Warn about C++ constructs whose meaning differs between ISO C++ 2011 and ISO C++ 2014. This warning is enabled by @option{-Wall}. +@item -Wc++1z-compat @r{(C++ and Objective-C++ only)} +@opindex Wc++1z-compat +Warn about C++ constructs whose meaning differs between ISO C++ 2014 +and the forthoming ISO C++ 2017(?). This warning is enabled by @option{-Wall}. + @item -Wcast-qual @opindex Wcast-qual @opindex Wno-cast-qual diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C index 6ac66e0..f4658a9 100644 --- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C +++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C @@ -380,6 +380,12 @@ # error "__cpp_capture_star_this != 201603" #endif +#ifndef __cpp_noexcept_function_type +# error "__cpp_noexcept_function_type" +#elif __cpp_noexcept_function_type != 201510 +# error "__cpp_noexcept_function_type != 201510" +#endif + #ifdef __has_cpp_attribute # if ! __has_cpp_attribute(maybe_unused) diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C new file mode 100644 index 0000000..62e1322 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C @@ -0,0 +1,8 @@ +// Testcase from P0012r1 +// { dg-options -std=c++1z } + +void (*p)() throw(int); +void (**pp)() noexcept = &p; // { dg-error "" } cannot convert to pointer to noexcept function + +struct S { typedef void (*p)(); operator p(); }; +void (*q)() noexcept = S(); // { dg-error "" } cannot convert to pointer to noexcept function diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C new file mode 100644 index 0000000..bcd4d8d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C @@ -0,0 +1,3 @@ +// { dg-options "-Wall -std=c++14" } + +void f(int(*)() noexcept) { } // { dg-warning "mangled" } diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C new file mode 100644 index 0000000..39820af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C @@ -0,0 +1,23 @@ +// { dg-options -std=c++1z } + +template <class R, class... A, bool B> +void f(R (*)(A...) noexcept(B)) { } + +template <class R, class... A, bool B> +void f2(R (*)(A...) noexcept(B)) { } + +void g(int); +void h(int) noexcept; + +int main() +{ + f(g); + f2(h); +} + +// { dg-final { scan-assembler "_Z1fIvJiELb0EEvPDOT1_EFT_DpT0_E" } } +// { dg-final { scan-assembler "_Z2f2IvJiELb1EEvPDOT1_EFT_DpT0_E" } } + +void f3(void (*)() noexcept) { } + +// { dg-final { scan-assembler "_Z2f3PDoFvvE" } } diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C new file mode 100644 index 0000000..747bb19 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C @@ -0,0 +1,10 @@ +// Test for function pointer conversion on template arguments. +// { dg-options -std=c++1z } + +template <void (*P)()> struct A { }; + +void f() noexcept { }; +constexpr void (*p)() noexcept = f; + +A<f> a; +A<p> b; diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C new file mode 100644 index 0000000..9303da8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C @@ -0,0 +1,13 @@ +// Test for overload resolution. +// { dg-options -std=c++1z } + +void f(void (*)() noexcept) = delete; +void f(void (*)()) { } +void g() {} +void h() noexcept {} + +int main() +{ + f(g); + f(h); // { dg-error "deleted" } +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C new file mode 100644 index 0000000..621da93 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C @@ -0,0 +1,16 @@ +// Test for deduction. +// { dg-options -std=c++1z } + +template <class R, class... A> +void f(R (*)(A...)); +void g(int) noexcept; + +template <class R, class... A> +void h(R (*)(A...) noexcept); +void i(int); + +int main() +{ + f(g); + h(i); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C new file mode 100644 index 0000000..9e5d202 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C @@ -0,0 +1,25 @@ +// Test for composite pointer type. +// { dg-options -std=c++1z } + +typedef void (*P)(); +typedef void (*NP)() noexcept; + +void f(); +void g() noexcept; + +bool b; + +template <class T, class U> struct Same; +template <class T> struct Same<T,T> { }; + +Same<decltype(b ? &f : &g),P> s; + +int main() +{ + P p = 0; + NP np = 0; + + p == np; + p != np; + p < np; +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C new file mode 100644 index 0000000..5068457 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C @@ -0,0 +1,13 @@ +// Test for lambda conversion. +// { dg-options -std=c++1z } + +void f() +{ + auto l = []() noexcept { return 0; }; + int (*p)() noexcept = l; + int (*q)() = l; + + auto l2 = []{ return 0; }; + p = l2; // { dg-error "" } + q = l2; +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C new file mode 100644 index 0000000..1f78114 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C @@ -0,0 +1,14 @@ +// Test for static_cast. +// { dg-options -std=c++1z } + +void f() +{ + typedef void (*P)(); + typedef void (*NP)() noexcept; + + P p; + NP np; + + static_cast<P>(np); + static_cast<NP>(p); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C new file mode 100644 index 0000000..0182e3a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C @@ -0,0 +1,22 @@ +// Test for exception handling. +// { dg-options -std=c++1z } +// { dg-do run } + +void f() {} +void g() noexcept {} + +int main() +{ + try { throw g; } + catch (void (*)()) { } + + try { throw g; } + catch (void (*)() noexcept) { } + + try { throw f; } + catch (void (*)()) { } + + try { throw f; } + catch (void (*)() noexcept) { __builtin_abort(); } + catch (...) { } +} diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C new file mode 100644 index 0000000..4547c4c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C @@ -0,0 +1,18 @@ +// Test for PMF template args. +// { dg-options -std=c++1z } +// { dg-do run } + +struct A +{ + void f() noexcept; + void g(); +}; + +template <void (A::*)()> struct B { }; +template <void (A::*)() noexcept> struct C { }; + +B<&A::f> b1; +B<&A::g> b2; + +C<&A::f> c1; +C<&A::g> c2; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/eh/spec2.C b/gcc/testsuite/g++.dg/eh/spec2.C index 64807dd..8107f01 100644 --- a/gcc/testsuite/g++.dg/eh/spec2.C +++ b/gcc/testsuite/g++.dg/eh/spec2.C @@ -2,9 +2,9 @@ struct S { void f (void); }; -typedef void f1 (void) throw (int); // { dg-error "exception" } -typedef void (*f2) (void) throw (int); // { dg-error "exception" } -typedef void (S::*f3) (void) throw (int); // { dg-error "exception" } +typedef void f1 (void) throw (int); // { dg-error "exception" "" { target c++14_down } } +typedef void (*f2) (void) throw (int); // { dg-error "exception" "" { target c++14_down } } +typedef void (S::*f3) (void) throw (int); // { dg-error "exception" "" { target c++14_down } } void (*f4) (void) throw (int); void (S::*f5) (void) throw (int); diff --git a/gcc/testsuite/g++.old-deja/g++.eh/spec7.C b/gcc/testsuite/g++.old-deja/g++.eh/spec7.C index 2ef88a2..2a4a989 100644 --- a/gcc/testsuite/g++.old-deja/g++.eh/spec7.C +++ b/gcc/testsuite/g++.old-deja/g++.eh/spec7.C @@ -10,7 +10,7 @@ struct A static void (A::*pmf)() throw (); }; -void (A::* A::pmf)() = &A::g; +void (A::* A::pmf)() throw() = &A::g; int main() { |