From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13987 invoked by alias); 19 Dec 2018 20:27:46 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 13969 invoked by uid 89); 19 Dec 2018 20:27:45 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_LAZY_DOMAIN_SECURITY,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=anticipated, bodies, stress, tracked 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; Wed, 19 Dec 2018 20:27:37 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0BBCD8666F for ; Wed, 19 Dec 2018 20:27:36 +0000 (UTC) Received: from redhat.com (ovpn-123-255.rdu2.redhat.com [10.10.123.255]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B35B4D5; Wed, 19 Dec 2018 20:27:32 +0000 (UTC) Date: Wed, 19 Dec 2018 20:27:00 -0000 From: Marek Polacek To: GCC Patches , Jason Merrill Subject: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) Message-ID: <20181219202731.GL21364@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.10.1 (2018-07-13) X-SW-Source: 2018-12/txt/msg01415.txt.bz2 Prompted by Jon's observation in 52869, I noticed that we don't treat a noexcept-specifier as a complete-class context of a class ([class.mem]/6). As with member function bodies, default arguments, and NSDMIs, names used in a noexcept-specifier of a member-function can be declared later in the class body, so we need to wait and parse them at the end of the class. For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG). This wasn't as easy as I'd anticipated, because I needed to make sure to * handle well accessing function parameters in the noexcept-specifier, hence the maybe_{begin,end}_member_function_processing business, * not regress diagnostic. See e.g. noexcept38.C for detecting "looser throw specifier", or noexcept39.C, friend decls and redeclaration. This is handled by functions like noexcept_override_late_checks and check_redeclaration_exception_specification. I hope that's it. Compiling libstdc++ was a fairly good stress test, and I've added a bunch of reduced testcases I've collected along the way. I also noticed we're not properly detecting using 'this' in static member functions; tracked in 88548. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2018-12-19 Marek Polacek 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. (check_redeclaration_exception_specification): Declare. (maybe_check_throw_specifier): Declare. * decl.c (check_redeclaration_exception_specification): No longer static. Handle UNPARSED_NOEXCEPT_SPEC_P. * except.c (nothrow_spec_p): Accept DEFAULT_ARG in 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_init_declarator): Maybe save the noexcept-specifier to post process. (maybe_begin_member_function_processing): New. (maybe_end_member_function_processing): New. (cp_parser_class_specifier_1): Implement delayed parsing of noexcept-specifiers. (cp_parser_member_declaration): Maybe save the noexcept-specifier to post process. (cp_parser_save_noexcept): New. (cp_parser_late_noexcept_specifier): New. (noexcept_override_late_checks): New. (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept instead of the normal processing if needed. (cp_parser_save_member_function_body): Maybe save the noexcept-specifier to post process. * 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_throw_specifier): New function, broken out of... (check_final_overrider): ...here. Call maybe_check_throw_specifier. * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P. (cp_tree_equal): Handle DEFAULT_ARG. * typeck2.c (merge_exception_specifiers): If an unparsed noexcept expression has been passed, return it instead of merging it. * g++.dg/cpp0x/noexcept34.C: New test. * g++.dg/cpp0x/noexcept35.C: New test. * g++.dg/cpp0x/noexcept36.C: New test. * g++.dg/cpp0x/noexcept37.C: New test. * g++.dg/cpp0x/noexcept38.C: New test. * g++.dg/cpp0x/noexcept39.C: New test. diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def index 43d90eb1efb..aa8b752d8f4 100644 --- gcc/cp/cp-tree.def +++ gcc/cp/cp-tree.def @@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1) /* An un-parsed default argument. Holds a vector of input tokens and a vector of places where the argument was instantiated before - parsing had occurred. */ + parsing had occurred. This is also used for delayed NSDMIs and + noexcept-specifier parsing. For a noexcept-specifier, the vector + holds a function declaration used for late checking. */ DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0) /* An uninstantiated/unevaluated noexcept-specification. For the diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index 1d806b782bd..bd3cd200fcb 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -1193,6 +1193,9 @@ struct GTY (()) tree_default_arg { #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \ (DEFERRED_NOEXCEPT_SPEC_P (NODE) \ && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE) +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \ + ((NODE) && (TREE_PURPOSE (NODE)) \ + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG)) struct GTY (()) tree_deferred_noexcept { struct tree_base base; @@ -6430,6 +6433,8 @@ extern bool check_array_designated_initializer (constructor_elt *, unsigned HOST_WIDE_INT); extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t); extern tree build_explicit_specifier (tree, tsubst_flags_t); +extern void check_redeclaration_exception_specification + (tree, tree); /* in decl2.c */ extern void record_mangling (tree, bool); @@ -6894,6 +6899,7 @@ extern tree copied_binfo (tree, tree); extern tree original_binfo (tree, tree); extern int shared_member_p (tree); extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ()); +extern bool maybe_check_throw_specifier (tree, tree); /* The representation of a deferred access check. */ diff --git gcc/cp/decl.c gcc/cp/decl.c index d6028e3608c..74444db8623 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -1140,7 +1140,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl) function templates. If their exception specifications do not match, issue a diagnostic. */ -static void +void check_redeclaration_exception_specification (tree new_decl, tree old_decl) { @@ -1152,6 +1152,16 @@ check_redeclaration_exception_specification (tree new_decl, && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions)) return; + /* We can't compare unparsed noexcept-specifiers. Save the old decl + and check this again after we've parsed the noexcept-specifiers + for real. */ + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions)) + { + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)), + copy_decl (old_decl)); + return; + } + if (!type_dependent_expression_p (old_decl)) { maybe_instantiate_noexcept (new_decl); diff --git gcc/cp/except.c gcc/cp/except.c index b04eb0c5332..92fc39d3968 100644 --- gcc/cp/except.c +++ gcc/cp/except.c @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec) || TREE_VALUE (spec) || spec == noexcept_false_spec || TREE_PURPOSE (spec) == error_mark_node + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG || processing_template_decl); return false; diff --git gcc/cp/parser.c gcc/cp/parser.c index b57e35d04c5..267b3518156 100644 --- gcc/cp/parser.c +++ gcc/cp/parser.c @@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging static cp_token_cache *cp_token_cache_new (cp_token *, cp_token *); +static tree cp_parser_noexcept_specification_opt + (cp_parser *, bool, bool *, bool); +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 *); @@ -1968,11 +1974,14 @@ cp_parser_context_new (cp_parser_context* next) parser->unparsed_queues->last ().nsdmis #define unparsed_classes \ parser->unparsed_queues->last ().classes +#define unparsed_noexcepts \ + parser->unparsed_queues->last ().noexcepts static void push_unparsed_function_queues (cp_parser *parser) { - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL}; + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL, + NULL }; vec_safe_push (parser->unparsed_queues, e); } @@ -20315,7 +20324,13 @@ cp_parser_init_declarator (cp_parser* parser, /*asmspec=*/NULL_TREE, attr_chainon (attributes, prefix_attributes)); if (decl && TREE_CODE (decl) == FUNCTION_DECL) - cp_parser_save_default_args (parser, decl); + { + cp_parser_save_default_args (parser, decl); + /* Remember if there is a noexcept-specifier to post process. */ + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) + vec_safe_push (unparsed_noexcepts, decl); + } cp_finalize_omp_declare_simd (parser, decl); cp_finalize_oacc_routine (parser, decl, false); } @@ -23058,6 +23073,45 @@ cp_parser_class_name (cp_parser *parser, return decl; } +/* Make sure that any member-function parameters are in scope. + For instance, a function's noexcept-specifier can use the function's + parameters: + + struct S { + void fn (int p) noexcept(noexcept(p)); + }; + + so we need to make sure name lookup can find them. This is used + when we delay parsing of the noexcept-specifier. */ + +static void +maybe_begin_member_function_processing (tree decl) +{ + begin_scope (sk_function_parms, decl); + tree args = DECL_ARGUMENTS (decl); + args = nreverse (args); + + tree next; + for (tree parm = args; parm; parm = next) + { + next = DECL_CHAIN (parm); + if (TREE_CODE (parm) == PARM_DECL) + pushdecl (parm); + } + /* Get the decls in their original chain order and record in the + function. This is all and only the PARM_DECLs that were + pushed into scope by the loop above. */ + DECL_ARGUMENTS (decl) = get_local_decls (); +} + +/* Undo the effects of maybe_begin_member_function_processing. */ + +static void +maybe_end_member_function_processing (void) +{ + pop_bindings_and_leave_scope (); +} + /* Parse a class-specifier. class-specifier: @@ -23368,6 +23422,56 @@ cp_parser_class_specifier_1 (cp_parser* parser) vec_safe_truncate (unparsed_classes, 0); after_nsdmi_defaulted_late_checks (type); + /* If there are noexcept-specifiers that have not yet been processed, + take care of them now. */ + class_type = NULL_TREE; + pushed_scope = NULL_TREE; + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl) + { + if (class_type != DECL_CONTEXT (decl)) + { + if (pushed_scope) + pop_scope (pushed_scope); + class_type = DECL_CONTEXT (decl); + pushed_scope = push_scope (class_type); + } + + /* 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. */ + maybe_begin_member_function_processing (decl); + + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + spec = TREE_PURPOSE (spec); + vec *p = DEFARG_INSTANTIATIONS (spec); + tree old_decl = (p ? (*p)[0] : NULL_TREE); + + /* Now we can parse the noexcept-specifier. */ + spec = cp_parser_late_noexcept_specifier (parser, spec); + if (spec != error_mark_node) + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec); + + /* If we've stashed an old declaration, it means we need to + perform late redeclaration checking. */ + if (old_decl) + check_redeclaration_exception_specification (decl, old_decl); + + /* 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. */ + maybe_end_member_function_processing (); + + /* 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) { @@ -24537,6 +24641,12 @@ cp_parser_member_declaration (cp_parser* parser) else decl = finish_fully_implicit_template (parser, decl); } + if (decl && TREE_CODE (decl) == FUNCTION_DECL) + { + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) + vec_safe_push (unparsed_noexcepts, decl); + } } cp_finalize_omp_declare_simd (parser, decl); @@ -24919,6 +25029,89 @@ cp_parser_base_specifier (cp_parser* parser) /* Exception handling [gram.exception] */ +/* Save the tokens that make up the noexcept-specifier for a member-function. + Returns a DEFAULT_ARG. */ + +static tree +cp_parser_save_noexcept (cp_parser *parser) +{ + cp_token *first = parser->lexer->next_token; + /* We want everything up to, including, the final ')'. */ + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0); + cp_token *last = parser->lexer->next_token; + + /* As with default arguments and NSDMIs, make us of DEFAULT_ARG + to carry the information we will need. */ + tree expr = make_node (DEFAULT_ARG); + /* Save away the noexcept-specifier; we will process it when the + class is complete. */ + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last); + DEFARG_INSTANTIATIONS (expr) = NULL; + 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, + NULL, + /*return_cond=*/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_throw_specifier (fndecl, fn); + } +} + /* Parse an (optional) noexcept-specification. noexcept-specification: @@ -24947,6 +25140,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser, if (cp_parser_is_keyword (token, RID_NOEXCEPT)) { tree expr; + + /* [class.mem]/6 says that a noexcept-specifer (within the + member-specification of the class) is a complete-class context of + a class. So, if the noexcept-specifier has the optional expression, + just save the tokens, and reparse this after we're done with the + class. */ + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN + && at_class_scope_p () + && TYPE_BEING_DEFINED (current_class_type) + && !LAMBDA_TYPE_P (current_class_type)) + return cp_parser_save_noexcept (parser); + cp_lexer_consume_token (parser->lexer); if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) @@ -28120,7 +28325,12 @@ cp_parser_save_member_function_body (cp_parser* parser, return error_mark_node; } - /* Remember it, if there default args to post process. */ + /* Remember if there is a noexcept-specifier to post process. */ + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)); + if (UNPARSED_NOEXCEPT_SPEC_P (spec)) + vec_safe_push (unparsed_noexcepts, fn); + + /* 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 diff --git gcc/cp/parser.h gcc/cp/parser.h index 8bfa3f3b9c4..df0c6c1960e 100644 --- gcc/cp/parser.h +++ gcc/cp/parser.h @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry { /* Nested classes go in this vector, so that we can do some final processing after parsing any NSDMIs. */ vec *classes; + + /* Functions with noexcept-specifiers that require post-processing. */ + vec *noexcepts; }; diff --git gcc/cp/pt.c gcc/cp/pt.c index e99de71ea9e..382a14f7d6a 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -24988,8 +24988,9 @@ dependent_type_p_r (tree type) if (tree noex = TREE_PURPOSE (spec)) /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't affect overload resolution and treating it as dependent breaks - things. */ + things. Same for an unparsed noexcept expression. */ if (TREE_CODE (noex) != DEFERRED_NOEXCEPT + && TREE_CODE (noex) != DEFAULT_ARG && value_dependent_expression_p (noex)) return true; return false; diff --git gcc/cp/search.c gcc/cp/search.c index d700fe328f4..3e6494dd98f 100644 --- gcc/cp/search.c +++ gcc/cp/search.c @@ -1850,6 +1850,38 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p) NULL, &lfd); } +/* Check throw specifier of OVERRIDER is at least as strict as + the one of BASEFN. */ + +bool +maybe_check_throw_specifier (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 throw specifier for %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. */ @@ -1860,7 +1892,6 @@ check_final_overrider (tree overrider, tree basefn) tree base_type = TREE_TYPE (basefn); tree over_return = fndecl_declared_return_type (overrider); tree base_return = fndecl_declared_return_type (basefn); - tree over_throw, base_throw; int fail = 0; @@ -1944,21 +1975,8 @@ check_final_overrider (tree overrider, tree basefn) return 0; } - /* Check throw specifier is at least as strict. */ - maybe_instantiate_noexcept (basefn); - maybe_instantiate_noexcept (overrider); - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn)); - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider)); - - if (!comp_except_specs (base_throw, over_throw, ce_derived)) - { - auto_diagnostic_group d; - error ("looser throw specifier for %q+#F", overrider); - inform (DECL_SOURCE_LOCATION (basefn), - "overridden function is %q#F", basefn); - DECL_INVALID_OVERRIDER_P (overrider) = 1; - return 0; - } + if (!maybe_check_throw_specifier (overrider, basefn)) + return 0; /* Check for conflicting type attributes. But leave transaction_safe for set_one_vmethod_tm_attributes. */ diff --git gcc/cp/tree.c gcc/cp/tree.c index 97074dfab56..a7d1e58c73a 100644 --- gcc/cp/tree.c +++ gcc/cp/tree.c @@ -2542,6 +2542,7 @@ canonical_eh_spec (tree raises) if (raises == NULL_TREE) return raises; else if (DEFERRED_NOEXCEPT_SPEC_P (raises) + || UNPARSED_NOEXCEPT_SPEC_P (raises) || uses_template_parms (raises) || uses_template_parms (TREE_PURPOSE (raises))) /* Keep a dependent or deferred exception specification. */ @@ -3653,6 +3654,7 @@ cp_tree_equal (tree t1, tree t2) case TEMPLATE_DECL: case IDENTIFIER_NODE: case SSA_NAME: + case DEFAULT_ARG: return false; case BASELINK: diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c index 64e36efd17e..6fec77d8269 100644 --- gcc/cp/typeck2.c +++ gcc/cp/typeck2.c @@ -2302,6 +2302,12 @@ merge_exception_specifiers (tree list, tree add) { tree noex, orig_list; + /* We don't want to lose the unparsed operand lest we miss diagnostics. */ + if (UNPARSED_NOEXCEPT_SPEC_P (list)) + return list; + else if (UNPARSED_NOEXCEPT_SPEC_P (add)) + return add; + /* No exception-specifier or noexcept(false) are less strict than anything else. Prefer the newer variant (LIST). */ if (!list || list == noexcept_false_spec) diff --git gcc/testsuite/g++.dg/cpp0x/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C new file mode 100644 index 00000000000..43b38c2446f --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept34.C @@ -0,0 +1,147 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert(X, #X) + +struct S { + void f1() noexcept(noexcept(i)) { } + void f2() noexcept(noexcept(fn())) { } + void f3() noexcept(noexcept(fnx())) { } + void f4() noexcept(noexcept(i)); + void f5() noexcept(noexcept(fn())); + void f6() noexcept(noexcept(fnx())); + + void f7() noexcept(1); + void f8() noexcept(0); + void f9() noexcept(b); + void f10() noexcept(!b); + + int i; + static constexpr auto b = true; + void fny() noexcept(noexcept(fn())); + void fn(); + void fnx() noexcept; +}; + +S s; +SA(noexcept(s.f1())); +SA(!noexcept(s.f2())); +SA(noexcept(s.f3())); +SA(noexcept(s.f4())); +SA(!noexcept(s.f5())); +SA(noexcept(s.f6())); +SA(noexcept(s.f7())); +SA(!noexcept(s.f8())); +SA(noexcept(s.f9())); +SA(!noexcept(s.f10())); + +struct S2 { + struct V { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void f3() noexcept(noexcept(fn())) { } + void f4() noexcept(noexcept(fnx())) { } + void fn(); + void fnx() noexcept; + } v; + void fn(); + void fnx(); +}; + +S2 s2; +SA(!noexcept(s2.v.f1())); +SA(noexcept(s2.v.f2())); +SA(!noexcept(s2.v.f3())); +SA(noexcept(s2.v.f4())); + +struct S3 { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void fn(); + void fnx() noexcept; +}; + +void +S3::f1() noexcept(noexcept(fn())) +{ +} + +void +S3::f2() noexcept(noexcept(fnx())) +{ +} + +struct S4 { + int f1 (int p) noexcept(noexcept(p)) { return p; } + int f2 (int p) noexcept(noexcept(p)); + int f3 (int p = 10) noexcept(noexcept(p)); + int f4 () noexcept(noexcept(S4{})); +}; + +S4 s4; +SA(noexcept(s4.f1(1))); +SA(noexcept(s4.f2(1))); +SA(noexcept(s4.f3())); +SA(noexcept(s4.f4())); + +template +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 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 +struct S6 { + void f1() noexcept(noexcept(x)); + T x; +}; + +struct S7 { + template + void f1 () noexcept(noexcept(U(1))) { } + + template + void f2() noexcept(noexcept(N)); + + template + 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 +struct S9 { + S9() noexcept(noexcept(w)) { } + S9 &operator=(S9 &&) noexcept(T::X); + W w; +}; diff --git gcc/testsuite/g++.dg/cpp0x/noexcept35.C gcc/testsuite/g++.dg/cpp0x/noexcept35.C new file mode 100644 index 00000000000..b3859de9ebc --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept35.C @@ -0,0 +1,26 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +struct S { + void f1() noexcept(noexcept(fn())); + void f2() noexcept(noexcept(fnx())); + void fn(); + void fnx() noexcept; +}; + +void +S::f1() noexcept // { dg-error "different exception specifier" } +{ +} + +void +S::f2() // { dg-error "different exception specifier" } +{ +} + +struct S2 { + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" } + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" } + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" } + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" } +}; diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C new file mode 100644 index 00000000000..12c6d364099 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C @@ -0,0 +1,9 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +template 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) {} +}; diff --git gcc/testsuite/g++.dg/cpp0x/noexcept37.C gcc/testsuite/g++.dg/cpp0x/noexcept37.C new file mode 100644 index 00000000000..a81032f28e9 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept37.C @@ -0,0 +1,14 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +void fn1(void()); +template class A { + void _M_local_data(); + A() noexcept(_M_local_data); +}; + +class B { + void _S_initialize(); + static void _S_initialize_once(); +}; +void B::_S_initialize() { fn1(_S_initialize_once); } diff --git gcc/testsuite/g++.dg/cpp0x/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C new file mode 100644 index 00000000000..9e5545bc022 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept38.C @@ -0,0 +1,23 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +struct A +{ + virtual void f(); + virtual void g() noexcept; + virtual void h() noexcept(false); +}; + +struct B : A +{ + void f() noexcept(true); + void g() noexcept(true); + void h() noexcept(true); +}; + +struct D : A +{ + void f() noexcept(false); + void g() noexcept(false); // { dg-error "looser throw specifier" } + void h() noexcept(false); +}; diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C new file mode 100644 index 00000000000..da7490d651c --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/noexcept39.C @@ -0,0 +1,28 @@ +// PR c++/86476 - noexcept-specifier is a complete-class context +// { dg-do compile { target c++11 } } + +void f() noexcept(false); +void g() noexcept(true); +void h() noexcept; + +struct B { + friend void f() noexcept(false); + friend void g() noexcept(false); // { dg-error "different exception specifier" } + friend void h() noexcept(false); // { dg-error "different exception specifier" } +}; + +struct C { + friend void f() noexcept(true); // { dg-error "different exception specifier" } + friend void g() noexcept(true); // { dg-error "different exception specifier" } + friend void h() noexcept(true); // { dg-error "different exception specifier" } +}; + +void o() noexcept(false); +void p() noexcept(true); +void q() noexcept; + +struct D { + friend void o() noexcept(true); // { dg-error "different exception specifier" } + friend void p() noexcept(true); + friend void q() noexcept(true); +};