From patchwork Tue Apr 29 18:03:40 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 343961 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id EC357140102 for ; Wed, 30 Apr 2014 04:04:00 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:date:from:mime-version:to:subject:content-type; q= dns; s=default; b=YwTsWNSfj3ZqD1XnYljpg8gML2HOXhFuKhtgXO9lMFrQki AK/1NLd0ciIotPnq6aeaH6z/bgsCJIwfPzvZFQVoyrc4jjlio3Oimg6AvPn774Vv TGFiH929S/CCVGUqwwvk6W0OKV1X2wIXioq0TA/xuqTVxPEUD33AjVQUkiK2s= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:date:from:mime-version:to:subject:content-type; s= default; bh=j4xvJKp5tRimgfSH42HmJ0EVFmA=; b=ya4lmJYvvb/7LCuMo/AQ XEVZtBc9FzbZdKJTsZ+VgTJt0u4oqASr9A0eHITiMd82wGdGD84pAici6hcPSvr6 2XAOHSrmEgzbNuWwbTSm0PqfhUTKYYUCWpOsl3vJw5D8WjHL8JNF3PN0yZfcE8wc XRRPeVgQULFIBNz+3Fp5oW4= Received: (qmail 14257 invoked by alias); 29 Apr 2014 18:03:48 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 14245 invoked by uid 89); 29 Apr 2014 18:03:48 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.3 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 29 Apr 2014 18:03:43 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s3TI3g5E004996 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 29 Apr 2014 14:03:42 -0400 Received: from [10.10.116.23] ([10.10.116.23]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id s3TI3fj9024557 for ; Tue, 29 Apr 2014 14:03:41 -0400 Message-ID: <535FE97C.2060305@redhat.com> Date: Tue, 29 Apr 2014 14:03:40 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.4.0 MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for DR 1351/1397 (implicit EH spec and NSDMI) DR 1351 says that when calculating the implicit exception-specification for a defaulted default constructor, we need to consider whether an NSDMI could throw. Previously we have represented an uncalculated implicit eh-spec with a list of the subobject constructors that will be called, but that doesn't really extend to handling NSDMIs, so this patch replaces that representation with a simple token that just means "figure it out later". Tested x86_64-pc-linux-gnu, applying to trunk. commit 73c6946ee42b364de9587ebd2e4137e064a815f3 Author: Jason Merrill Date: Tue Feb 11 23:07:29 2014 -0800 DR 1351 Represent the unevaluated exception specification of an implicitly declared or deleted function with a simple placeholder, not a list of functions. * cp-tree.h (UNEVALUATED_NOEXCEPT_SPEC_P): New. * except.c (unevaluated_noexcept_spec): New. * class.c (deduce_noexcept_on_destructor): Use it. * decl.c (check_redeclaration_exception_specification): Call maybe_instantiate_noexcept. (duplicate_decls): Call it before merge_types. (start_preparsed_function): Call maybe_instantiate_noexcept. * decl2.c (mark_used): Call maybe_instantiate_noexcept earlier. * init.c (get_nsdmi): Factor out of perform_member_init. * method.c (process_subob_fn): Call maybe_instantiate_noexcept. (walk_field_subobs): Consider NSDMI for EH spec. (get_defaulted_eh_spec): New. (implicitly_declare_fn): Use unevaluated_noexcept_spec. (defaulted_late_check): Defer EH checking in non-template classes. (after_nsdmi_defaulted_late_checks): New. * parser.c (cp_parser_class_specifier_1): Use it. (unparsed_classes): New macro. * parser.h (cp_unparsed_functions_entry_d): Add classes field. * pt.c (maybe_instantiate_noexcept): Use get_defaulted_eh_spec. Remove list-of-functions handling. * typeck2.c (merge_exception_specifiers): Remove list-of-functions handling and FN parameter. * typeck.c (merge_types): Adjust. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 334bfd5..5cac488 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -4725,11 +4725,7 @@ deduce_noexcept_on_destructor (tree dtor) { if (!TYPE_RAISES_EXCEPTIONS (TREE_TYPE (dtor))) { - tree ctx = DECL_CONTEXT (dtor); - tree implicit_fn = implicitly_declare_fn (sfk_destructor, ctx, - /*const_p=*/false, - NULL, NULL); - tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn)); + tree eh_spec = unevaluated_noexcept_spec (); TREE_TYPE (dtor) = build_exception_variant (TREE_TYPE (dtor), eh_spec); } } diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 057e7ea..b4a72d6 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -212,9 +212,12 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1) parsing had occurred. */ DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0) -/* An uninstantiated noexcept-specification. DEFERRED_NOEXCEPT_PATTERN is - the pattern from the template, and DEFERRED_NOEXCEPT_ARGS are the - template arguments to substitute into the pattern when needed. */ +/* An uninstantiated/unevaluated noexcept-specification. For the + uninstantiated case, DEFERRED_NOEXCEPT_PATTERN is the pattern from the + template, and DEFERRED_NOEXCEPT_ARGS are the template arguments to + substitute into the pattern when needed. For the unevaluated case, + those slots are NULL_TREE and we use get_defaulted_eh_spec to find + the exception-specification. */ DEFTREECODE (DEFERRED_NOEXCEPT, "deferred_noexcept", tcc_exceptional, 0) /* A template-id, like foo. The first operand is the template. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f459e55..55ecc4e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -579,8 +579,10 @@ struct GTY (()) tree_default_arg { (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->args) #define DEFERRED_NOEXCEPT_SPEC_P(NODE) \ ((NODE) && (TREE_PURPOSE (NODE)) \ - && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT \ - || is_overloaded_fn (TREE_PURPOSE (NODE)))) + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT)) +#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \ + (DEFERRED_NOEXCEPT_SPEC_P (NODE) \ + && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE) struct GTY (()) tree_deferred_noexcept { struct tree_base base; @@ -4359,8 +4361,6 @@ extern int comparing_specializations; sizeof can be nested. */ extern int cp_unevaluated_operand; -extern tree cp_convert_range_for (tree, tree, tree, bool); -extern bool parsing_nsdmi (void); /* in pt.c */ @@ -5420,6 +5420,7 @@ extern tree get_type_value (tree); extern tree build_zero_init (tree, tree, bool); extern tree build_value_init (tree, tsubst_flags_t); extern tree build_value_init_noctor (tree, tsubst_flags_t); +extern tree get_nsdmi (tree, bool); extern tree build_offset_ref (tree, tree, bool, tsubst_flags_t); extern tree throw_bad_array_new_length (void); @@ -5468,6 +5469,9 @@ extern tree make_thunk (tree, bool, tree, tree); extern void finish_thunk (tree); extern void use_thunk (tree, bool); extern bool trivial_fn_p (tree); +extern tree get_defaulted_eh_spec (tree); +extern tree unevaluated_noexcept_spec (void); +extern void after_nsdmi_defaulted_late_checks (tree); extern bool maybe_explain_implicit_delete (tree); extern void explain_implicit_non_constexpr (tree); extern void deduce_inheriting_ctor (tree); @@ -5489,6 +5493,11 @@ extern tree implicitly_declare_fn (special_function_kind, tree, /* In optimize.c */ extern bool maybe_clone_body (tree); +/* In parser.c */ +extern tree cp_convert_range_for (tree, tree, tree, bool); +extern bool parsing_nsdmi (void); +extern void inject_this_parameter (tree, cp_cv_quals); + /* in pt.c */ extern bool check_template_shadow (tree); extern tree get_innermost_template_args (tree, int); @@ -6162,7 +6171,7 @@ extern tree build_x_arrow (location_t, tree, extern tree build_m_component_ref (tree, tree, tsubst_flags_t); extern tree build_functional_cast (tree, tree, tsubst_flags_t); extern tree add_exception_specifier (tree, tree, int); -extern tree merge_exception_specifiers (tree, tree, tree); +extern tree merge_exception_specifiers (tree, tree); /* in mangle.c */ extern void init_mangle (void); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 7ce55c8..202db35 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1167,15 +1167,18 @@ static void check_redeclaration_exception_specification (tree new_decl, tree old_decl) { - tree new_type; - tree old_type; - tree new_exceptions; - tree old_exceptions; + tree new_exceptions = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (new_decl)); + tree old_exceptions = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl)); - new_type = TREE_TYPE (new_decl); - new_exceptions = TYPE_RAISES_EXCEPTIONS (new_type); - old_type = TREE_TYPE (old_decl); - old_exceptions = TYPE_RAISES_EXCEPTIONS (old_type); + /* Two default specs are equivalent, don't force evaluation. */ + if (UNEVALUATED_NOEXCEPT_SPEC_P (new_exceptions) + && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions)) + return; + + maybe_instantiate_noexcept (new_decl); + maybe_instantiate_noexcept (old_decl); + new_exceptions = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (new_decl)); + old_exceptions = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl)); /* [except.spec] @@ -1915,13 +1918,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) if (types_match) { + if (TREE_CODE (newdecl) == FUNCTION_DECL) + check_redeclaration_exception_specification (newdecl, olddecl); + /* Automatically handles default parameters. */ tree oldtype = TREE_TYPE (olddecl); tree newtype; - if (TREE_CODE (newdecl) == FUNCTION_DECL) - maybe_instantiate_noexcept (olddecl); - /* For typedefs use the old type, as the new type's DECL_NAME points at newdecl, which will be ggc_freed. */ if (TREE_CODE (newdecl) == TYPE_DECL) @@ -1952,10 +1955,6 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) } } - /* Do this after calling `merge_types' so that default - parameters don't confuse us. */ - else if (TREE_CODE (newdecl) == FUNCTION_DECL) - check_redeclaration_exception_specification (newdecl, olddecl); TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype; if (TREE_CODE (newdecl) == FUNCTION_DECL) @@ -13435,6 +13434,9 @@ start_preparsed_function (tree decl1, tree attrs, int flags) if (!DECL_CLONED_FUNCTION_P (decl1)) determine_visibility (decl1); + if (!processing_template_decl) + maybe_instantiate_noexcept (decl1); + begin_scope (sk_function_parms, decl1); ++function_depth; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 8a7a836..918ea2f 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -4806,6 +4806,9 @@ mark_used (tree decl, tsubst_flags_t complain) if (TREE_CODE (decl) == CONST_DECL) used_types_insert (DECL_CONTEXT (decl)); + if (TREE_CODE (decl) == FUNCTION_DECL) + maybe_instantiate_noexcept (decl); + if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DELETED_FN (decl)) { @@ -4860,9 +4863,6 @@ mark_used (tree decl, tsubst_flags_t complain) return true; } - if (TREE_CODE (decl) == FUNCTION_DECL) - maybe_instantiate_noexcept (decl); - /* Normally, we can wait until instantiation-time to synthesize DECL. However, if DECL is a static data member initialized with a constant or a constexpr function, we need it right now because a reference to diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 221971a..ead889c 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -1342,6 +1342,18 @@ build_noexcept_spec (tree expr, int complain) } } +/* Returns a noexcept-specifier to be evaluated later, for an + implicitly-declared or explicitly defaulted special member function. */ + +tree +unevaluated_noexcept_spec (void) +{ + static tree spec; + if (spec == NULL_TREE) + spec = build_noexcept_spec (make_node (DEFERRED_NOEXCEPT), tf_none); + return spec; +} + /* Returns a TRY_CATCH_EXPR that will put TRY_LIST and CATCH_LIST in the TRY and CATCH locations. CATCH_LIST must be a STATEMENT_LIST */ diff --git a/gcc/cp/init.c b/gcc/cp/init.c index fdc1011..8b9405c 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -521,6 +521,45 @@ perform_target_ctor (tree init) } } +/* Return the non-static data initializer for FIELD_DECL MEMBER. */ + +tree +get_nsdmi (tree member, bool in_ctor) +{ + tree init; + tree save_ccp = current_class_ptr; + tree save_ccr = current_class_ref; + if (!in_ctor) + inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED); + if (DECL_LANG_SPECIFIC (member) && DECL_TEMPLATE_INFO (member)) + /* Do deferred instantiation of the NSDMI. */ + init = (tsubst_copy_and_build + (DECL_INITIAL (DECL_TI_TEMPLATE (member)), + DECL_TI_ARGS (member), + tf_warning_or_error, member, /*function_p=*/false, + /*integral_constant_expression_p=*/false)); + else + { + init = DECL_INITIAL (member); + if (init && TREE_CODE (init) == DEFAULT_ARG) + { + error ("constructor required before non-static data member " + "for %qD has been parsed", member); + DECL_INITIAL (member) = error_mark_node; + init = NULL_TREE; + } + /* Strip redundant TARGET_EXPR so we don't need to remap it, and + so the aggregate init code below will see a CONSTRUCTOR. */ + if (init && TREE_CODE (init) == TARGET_EXPR + && !VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (init)))) + init = TARGET_EXPR_INITIAL (init); + init = break_out_target_exprs (init); + } + current_class_ptr = save_ccp; + current_class_ref = save_ccr; + return init; +} + /* Initialize MEMBER, a FIELD_DECL, with INIT, a TREE_LIST of arguments. If TREE_LIST is void_type_node, an empty initializer list was given; if NULL_TREE no initializer was given. */ @@ -534,31 +573,7 @@ perform_member_init (tree member, tree init) /* Use the non-static data member initializer if there was no mem-initializer for this field. */ if (init == NULL_TREE) - { - if (DECL_LANG_SPECIFIC (member) && DECL_TEMPLATE_INFO (member)) - /* Do deferred instantiation of the NSDMI. */ - init = (tsubst_copy_and_build - (DECL_INITIAL (DECL_TI_TEMPLATE (member)), - DECL_TI_ARGS (member), - tf_warning_or_error, member, /*function_p=*/false, - /*integral_constant_expression_p=*/false)); - else - { - init = DECL_INITIAL (member); - if (init && TREE_CODE (init) == DEFAULT_ARG) - { - error ("constructor required before non-static data member " - "for %qD has been parsed", member); - init = NULL_TREE; - } - /* Strip redundant TARGET_EXPR so we don't need to remap it, and - so the aggregate init code below will see a CONSTRUCTOR. */ - if (init && TREE_CODE (init) == TARGET_EXPR - && !VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (init)))) - init = TARGET_EXPR_INITIAL (init); - init = break_out_target_exprs (init); - } - } + init = get_nsdmi (member, /*ctor*/true); if (init == error_mark_node) return; diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 11bff7f..f8fc01f 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1003,8 +1003,9 @@ process_subob_fn (tree fn, tree *spec_p, bool *trivial_p, if (spec_p) { + maybe_instantiate_noexcept (fn); tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); - *spec_p = merge_exception_specifiers (*spec_p, raises, fn); + *spec_p = merge_exception_specifiers (*spec_p, raises); } if (!trivial_fn_p (fn)) @@ -1090,17 +1091,14 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, inform (0, "initializer for %q+#D is invalid", field); if (trivial_p) *trivial_p = false; -#if 0 /* Core 1351: If the field has an NSDMI that could throw, the - default constructor is noexcept(false). FIXME this is - broken by deferred parsing and 1360 saying we can't lazily - declare a non-trivial default constructor. Also this - needs to do deferred instantiation. Disable until the - conflict between 1351 and 1360 is resolved. */ - if (spec_p && !expr_noexcept_p (DECL_INITIAL (field), complain)) - *spec_p = noexcept_false_spec; -#endif - + default constructor is noexcept(false). */ + if (spec_p) + { + tree nsdmi = get_nsdmi (field, /*ctor*/false); + if (!expr_noexcept_p (nsdmi, complain)) + *spec_p = noexcept_false_spec; + } /* Don't do the normal processing. */ continue; } @@ -1438,6 +1436,26 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, --c_inhibit_evaluation_warnings; } +/* DECL is a defaulted function whose exception specification is now + needed. Return what it should be. */ + +tree +get_defaulted_eh_spec (tree decl) +{ + if (DECL_CLONED_FUNCTION_P (decl)) + decl = DECL_CLONED_FUNCTION (decl); + special_function_kind sfk = special_function_p (decl); + tree ctype = DECL_CONTEXT (decl); + tree parms = FUNCTION_FIRST_USER_PARMTYPE (decl); + tree parm_type = TREE_VALUE (parms); + bool const_p = CP_TYPE_CONST_P (non_reference (parm_type)); + tree spec = empty_except_spec; + synthesized_method_walk (ctype, sfk, const_p, &spec, NULL, NULL, + NULL, false, DECL_INHERITED_CTOR_BASE (decl), + parms); + return spec; +} + /* DECL is a deleted function. If it's implicitly deleted, explain why and return true; else return false. */ @@ -1675,6 +1693,13 @@ implicitly_declare_fn (special_function_kind kind, tree type, deleted_p = DECL_DELETED_FN (inherited_ctor); constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor); } + else if (cxx_dialect >= cxx11) + { + raises = unevaluated_noexcept_spec (); + synthesized_method_walk (type, kind, const_p, NULL, &trivial_p, + &deleted_p, &constexpr_p, false, + inherited_base, inherited_parms); + } else synthesized_method_walk (type, kind, const_p, &raises, &trivial_p, &deleted_p, &constexpr_p, false, @@ -1826,25 +1851,33 @@ defaulted_late_check (tree fn) is explicitly defaulted on its first declaration, (...) it is implicitly considered to have the same exception-specification as if it had been implicitly declared. */ - if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))) + tree fn_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); + if (!fn_spec) { - maybe_instantiate_noexcept (fn); - if (!comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)), - eh_spec, ce_normal)) + 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; - eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); - } + DECL_DELETED_FN (fn) = true; else error ("function %q+D defaulted on its redeclaration " "with an exception-specification that differs from " - "the implicit declaration %q#D", fn, implicit_fn); + "the implicit exception-specification %qX", fn, eh_spec); } } - if (DECL_DEFAULTED_IN_CLASS_P (fn)) - TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec); if (DECL_DEFAULTED_IN_CLASS_P (fn) && DECL_DECLARED_CONSTEXPR_P (implicit_fn)) @@ -1874,6 +1907,30 @@ defaulted_late_check (tree fn) DECL_DELETED_FN (fn) = 1; } +/* 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_METHODS (t); fn; fn = DECL_CHAIN (fn)) + if (!DECL_ARTIFICIAL (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 (!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 errors if defaulting FN is ill-formed. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 962cace..5542dcd 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -1848,11 +1848,13 @@ cp_parser_context_new (cp_parser_context* next) parser->unparsed_queues->last ().funs_with_definitions #define unparsed_nsdmis \ parser->unparsed_queues->last ().nsdmis +#define unparsed_classes \ + parser->unparsed_queues->last ().classes static void push_unparsed_function_queues (cp_parser *parser) { - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL}; + cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL}; vec_safe_push (parser->unparsed_queues, e); } @@ -17834,7 +17836,7 @@ cp_parser_virt_specifier_seq_opt (cp_parser* parser) /* Used by handling of trailing-return-types and NSDMI, in which 'this' is in scope even though it isn't real. */ -static void +void inject_this_parameter (tree ctype, cp_cv_quals quals) { tree this_parm; @@ -19505,6 +19507,13 @@ cp_parser_class_specifier_1 (cp_parser* parser) current_class_ref = save_ccr; if (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); + /* Now parse the body of the functions. */ if (flag_openmp) { @@ -19521,6 +19530,8 @@ cp_parser_class_specifier_1 (cp_parser* parser) cp_parser_late_parsing_for_member (parser, decl); vec_safe_truncate (unparsed_funs_with_definitions, 0); } + else + vec_safe_push (unparsed_classes, type); /* Put back any saved access checks. */ pop_deferring_access_checks (); diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 758c6df..96a8453 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -149,7 +149,7 @@ typedef struct GTY(()) cp_default_arg_entry_d { } cp_default_arg_entry; -/* An entry in a stack for member functions of local classes. */ +/* An entry in a stack for member functions defined within their classes. */ typedef struct GTY(()) cp_unparsed_functions_entry_d { /* Functions with default arguments that require post-processing. @@ -163,6 +163,10 @@ typedef struct GTY(()) cp_unparsed_functions_entry_d { /* Non-static data members with initializers that require post-processing. FIELD_DECLs appear in this list in declaration order. */ vec *nsdmis; + + /* Nested classes go in this vector, so that we can do some final + processing after parsing any NSDMIs. */ + vec *classes; } cp_unparsed_functions_entry; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index c74e7ae..48cc2a9 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -19470,14 +19470,16 @@ maybe_instantiate_noexcept (tree fn) fntype = TREE_TYPE (fn); spec = TYPE_RAISES_EXCEPTIONS (fntype); - if (!DEFERRED_NOEXCEPT_SPEC_P (spec)) + if (!spec || !TREE_PURPOSE (spec)) return; noex = TREE_PURPOSE (spec); if (TREE_CODE (noex) == DEFERRED_NOEXCEPT) { - if (push_tinst_level (fn)) + if (DEFERRED_NOEXCEPT_PATTERN (noex) == NULL_TREE) + spec = get_defaulted_eh_spec (fn); + else if (push_tinst_level (fn)) { push_access_scope (fn); push_deferring_access_checks (dk_no_deferred); @@ -19496,24 +19498,9 @@ maybe_instantiate_noexcept (tree fn) } else spec = noexcept_false_spec; - } - else - { - /* This is an implicitly declared function, so NOEX is a list of - other functions to evaluate and merge. */ - tree elt; - spec = noexcept_true_spec; - for (elt = noex; elt; elt = OVL_NEXT (elt)) - { - tree fn = OVL_CURRENT (elt); - tree subspec; - maybe_instantiate_noexcept (fn); - subspec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); - spec = merge_exception_specifiers (spec, subspec, NULL_TREE); - } - } - TREE_TYPE (fn) = build_exception_variant (fntype, spec); + TREE_TYPE (fn) = build_exception_variant (fntype, spec); + } FOR_EACH_CLONE (clone, fn) { @@ -19664,9 +19651,6 @@ instantiate_decl (tree d, int defer_ok, SET_DECL_IMPLICIT_INSTANTIATION (d); } - if (TREE_CODE (d) == FUNCTION_DECL) - maybe_instantiate_noexcept (d); - /* Defer all other templates, unless we have been explicitly forbidden from doing so. */ if (/* If there is no definition, we cannot instantiate the diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 9a80727..ae7fa77 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -840,8 +840,7 @@ merge_types (tree t1, tree t2) type_memfn_quals (t1), type_memfn_rqual (t1)); raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1), - TYPE_RAISES_EXCEPTIONS (t2), - NULL_TREE); + TYPE_RAISES_EXCEPTIONS (t2)); t1 = build_exception_variant (rval, raises); break; } @@ -852,8 +851,7 @@ merge_types (tree t1, tree t2) is just the main variant of this. */ tree basetype = class_of_this_parm (t2); tree raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1), - TYPE_RAISES_EXCEPTIONS (t2), - NULL_TREE); + TYPE_RAISES_EXCEPTIONS (t2)); cp_ref_qualifier rqual = type_memfn_rqual (t1); tree t3; diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 68e518a..5bbc2efd 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1989,10 +1989,10 @@ nothrow_spec_p_uninst (const_tree spec) } /* Combine the two exceptions specifier lists LIST and ADD, and return - their union. If FN is non-null, it's the source of ADD. */ + their union. */ tree -merge_exception_specifiers (tree list, tree add, tree fn) +merge_exception_specifiers (tree list, tree add) { tree noex, orig_list; @@ -2008,22 +2008,18 @@ merge_exception_specifiers (tree list, tree add, tree fn) if (nothrow_spec_p_uninst (add)) return list; + /* Two implicit noexcept specs (e.g. on a destructor) are equivalent. */ + if (UNEVALUATED_NOEXCEPT_SPEC_P (add) + && UNEVALUATED_NOEXCEPT_SPEC_P (list)) + return list; + /* We should have instantiated other deferred noexcept specs by now. */ + gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (add)); + + if (nothrow_spec_p_uninst (list)) + return add; noex = TREE_PURPOSE (list); - if (DEFERRED_NOEXCEPT_SPEC_P (add)) - { - /* If ADD is a deferred noexcept, we must have been called from - process_subob_fn. For implicitly declared functions, we build up - a list of functions to consider at instantiation time. */ - if (noex && operand_equal_p (noex, boolean_true_node, 0)) - noex = NULL_TREE; - gcc_assert (fn && (!noex || is_overloaded_fn (noex))); - noex = build_overload (fn, noex); - } - else if (nothrow_spec_p_uninst (list)) - return add; - else - gcc_checking_assert (!TREE_PURPOSE (add) - || cp_tree_equal (noex, TREE_PURPOSE (add))); + gcc_checking_assert (!TREE_PURPOSE (add) + || cp_tree_equal (noex, TREE_PURPOSE (add))); /* Combine the dynamic-exception-specifiers, if any. */ orig_list = list; diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-defer6.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-defer6.C index 033c142..0f06343 100644 --- a/gcc/testsuite/g++.dg/cpp0x/nsdmi-defer6.C +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-defer6.C @@ -1,8 +1,8 @@ // { dg-do compile { target c++11 } } -struct A // { dg-error "non-static data member" } +struct A { - int i = (A(), 42); // { dg-message "required here" } + int i = (A(), 42); // { dg-error "constructor required" } }; A a; diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-dr1397.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-dr1397.C new file mode 100644 index 0000000..061af8b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-dr1397.C @@ -0,0 +1,7 @@ +// DR 1397 +// { dg-require-effective-target c++11 } + +struct A +{ + int i = sizeof(A{}); // { dg-error "" } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-eh1.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-eh1.C index edcf588..9bc632c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/nsdmi-eh1.C +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-eh1.C @@ -1,5 +1,5 @@ // Core issue 1351 -// { dg-do run { xfail *-*-* } } +// { dg-do run } // { dg-require-effective-target c++11 } bool fail;