From: Jakub Jelinek Date: Sat, 5 Oct 2019 07:38:21 +0000 (+0200) Subject: PR c++/91369 - Implement P0784R7: constexpr new X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8e007055dd1374ca4c44406a4ead172be0dfa3a8;p=gcc.git PR c++/91369 - Implement P0784R7: constexpr new PR c++/91369 - Implement P0784R7: constexpr new c-family/ * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_constexpr_dynamic_alloc=201907 for -std=c++2a. cp/ * cp-tree.h (enum cp_tree_index): Add CPTI_HEAP_UNINIT_IDENTIFIER, CPTI_HEAP_IDENTIFIER and CPTI_HEAP_DELETED_IDENTIFIER. (heap_uninit_identifier, heap_identifier, heap_deleted_identifier): Define. (type_has_constexpr_destructor, build_new_constexpr_heap_type, cxx_constant_dtor): Declare. * class.c (type_maybe_constexpr_default_constructor): Make static. (type_maybe_constexpr_destructor, type_has_constexpr_destructor): New functions. (finalize_literal_type_property): For c++2a, don't clear CLASSTYPE_LITERAL_P for types without trivial destructors unless they have non-constexpr destructors. (explain_non_literal_class): For c++2a, complain about non-constexpr destructors rather than about non-trivial destructors. * constexpr.c: Include stor-layout.h. (struct constexpr_global_ctx): New type. (struct constexpr_ctx): Add global field, remove values and constexpr_ops_count. (cxx_replaceable_global_alloc_fn): New inline function. (cxx_eval_call_expression): For c++2a allow calls to replaceable global allocation functions, for new return address of a heap uninit var, for delete record its deletion. Change ctx->values->{get,put} to ctx->global->values.{get,put}. (non_const_var_error): Add auto_diagnostic_group sentinel. Emit special diagnostics for heap variables. (cxx_eval_store_expression): Change ctx->values->{get,put} to ctx->global->values.{get,put}. (cxx_eval_loop_expr): Initialize jump_target if NULL. Change new_ctx.values->remove to ctx->global->values.remove. (cxx_eval_constant_expression): Change *ctx->constexpr_ops_count to ctx->global->constexpr_ops_count. Change ctx->values->{get,put} to ctx->global->values.{get,put}. : Formatting fix. On cast of replaceable global allocation function to some pointer type, adjust the type of the heap variable and change name from heap_uninit_identifier to heap_identifier. (find_heap_var_refs): New function. (cxx_eval_outermost_constant_expr): Add constexpr_dtor argument, handle evaluation of constexpr dtors and add tracking of heap variables. Use tf_no_cleanup for get_target_expr_with_sfinae. (cxx_constant_value): Adjust cxx_eval_outermost_constant_expr caller. (cxx_constant_dtor): New function. (maybe_constant_value, fold_non_dependent_expr_template, maybe_constant_init_1): Adjust cxx_eval_outermost_constant_expr callers. (potential_constant_expression_1): Ignore clobbers. Allow COND_EXPR_IS_VEC_DELETE for c++2a. * decl.c (initialize_predefined_identifiers): Add heap identifiers. (decl_maybe_constant_destruction): New function. (cp_finish_decl): Don't clear TREE_READONLY for constexpr variables with non-trivial, but constexpr destructors. (register_dtor_fn): For constexpr variables with constexpr non-trivial destructors call cxx_maybe_build_cleanup instead of adding destructor calls at runtime. (expand_static_init): For constexpr variables with constexpr non-trivial destructors call cxx_maybe_build_cleanup. (grokdeclarator): Allow constexpr destructors for c++2a. Formatting fix. (cxx_maybe_build_cleanup): For constexpr variables with constexpr non-trivial destructors call cxx_constant_dtor instead of adding destructor calls at runtime. * init.c: Include stor-layout.h. (build_new_constexpr_heap_type, maybe_wrap_new_for_constexpr): New functions. (build_new_1): For c++2a and new[], add cast around the alloc call to help constexpr evaluation figure out the type of the heap storage. (build_vec_delete_1): Set DECL_INITIAL of tbase and emit a DECL_EXPR for it instead of initializing an uninitialized variable. * method.c: Include intl.h. (SFK_CTOR_P, SFK_DTOR_P, SFK_ASSIGN_P, SFK_COPY_P, SFK_MOVE_P): Move definitions earlier. (process_subob_fn): Add sfk argument, adjust non-constexpr call diagnostics based on it. (walk_field_subobs): Formatting fixes. Adjust process_subob_fn caller. (synthesized_method_base_walk): Likewise. (synthesized_method_walk): Set *constexpr_p to true for dtors in c++2a. Fix up DR number in comment. (implicitly_declare_fn): Formatting fix. * typeck2.c (store_init_value): Don't call cp_fully_fold_init on initializers of automatic non-constexpr variables in constexpr functions. testsuite/ * g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for c++2a. * g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics in c++17_down, adjust expected wording. * g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down. * g++.dg/cpp2a/constexpr-dtor1.C: New test. * g++.dg/cpp2a/constexpr-dtor2.C: New test. * g++.dg/cpp2a/constexpr-dtor3.C: New test. * g++.dg/cpp2a/constexpr-new1.C: New test. * g++.dg/cpp2a/constexpr-new2.C: New test. * g++.dg/cpp2a/constexpr-new3.C: New test. * g++.dg/cpp2a/constexpr-new4.C: New test. * g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and __cpp_constexpr_dynamic_alloc tests. Tweak __cpp_* tests for c++2a features to use style like older features, including #ifdef test. * g++.dg/ext/is_literal_type3.C: New test. From-SVN: r276622 --- diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 1ebc4d9f764..7deaff065cf 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2019-10-05 Jakub Jelinek + + PR c++/91369 - Implement P0784R7: constexpr new + * c-cppbuiltin.c (c_cpp_builtins): Predefine + __cpp_constexpr_dynamic_alloc=201907 for -std=c++2a. + 2019-10-04 Joseph Myers PR c/82752 diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index fc68bc4d0c4..9e0ce428127 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -989,6 +989,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_constinit=201907"); cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806"); cpp_define (pfile, "__cpp_impl_destroying_delete=201806"); + cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907"); } if (flag_concepts) cpp_define (pfile, "__cpp_concepts=201507"); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e705bcce438..b40825ee869 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,88 @@ +2019-10-05 Jakub Jelinek + + PR c++/91369 - Implement P0784R7: constexpr new + * cp-tree.h (enum cp_tree_index): Add CPTI_HEAP_UNINIT_IDENTIFIER, + CPTI_HEAP_IDENTIFIER and CPTI_HEAP_DELETED_IDENTIFIER. + (heap_uninit_identifier, heap_identifier, heap_deleted_identifier): + Define. + (type_has_constexpr_destructor, build_new_constexpr_heap_type, + cxx_constant_dtor): Declare. + * class.c (type_maybe_constexpr_default_constructor): Make static. + (type_maybe_constexpr_destructor, type_has_constexpr_destructor): New + functions. + (finalize_literal_type_property): For c++2a, don't clear + CLASSTYPE_LITERAL_P for types without trivial destructors unless they + have non-constexpr destructors. + (explain_non_literal_class): For c++2a, complain about non-constexpr + destructors rather than about non-trivial destructors. + * constexpr.c: Include stor-layout.h. + (struct constexpr_global_ctx): New type. + (struct constexpr_ctx): Add global field, remove values and + constexpr_ops_count. + (cxx_replaceable_global_alloc_fn): New inline function. + (cxx_eval_call_expression): For c++2a allow calls to replaceable + global allocation functions, for new return address of a heap uninit + var, for delete record its deletion. Change ctx->values->{get,put} to + ctx->global->values.{get,put}. + (non_const_var_error): Add auto_diagnostic_group sentinel. Emit + special diagnostics for heap variables. + (cxx_eval_store_expression): Change ctx->values->{get,put} to + ctx->global->values.{get,put}. + (cxx_eval_loop_expr): Initialize jump_target if NULL. Change + new_ctx.values->remove to ctx->global->values.remove. + (cxx_eval_constant_expression): Change *ctx->constexpr_ops_count + to ctx->global->constexpr_ops_count. Change ctx->values->{get,put} to + ctx->global->values.{get,put}. + : Formatting fix. On cast of replaceable global + allocation function to some pointer type, adjust the type of + the heap variable and change name from heap_uninit_identifier + to heap_identifier. + (find_heap_var_refs): New function. + (cxx_eval_outermost_constant_expr): Add constexpr_dtor argument, + handle evaluation of constexpr dtors and add tracking of heap + variables. Use tf_no_cleanup for get_target_expr_with_sfinae. + (cxx_constant_value): Adjust cxx_eval_outermost_constant_expr caller. + (cxx_constant_dtor): New function. + (maybe_constant_value, fold_non_dependent_expr_template, + maybe_constant_init_1): Adjust cxx_eval_outermost_constant_expr + callers. + (potential_constant_expression_1): Ignore clobbers. Allow + COND_EXPR_IS_VEC_DELETE for c++2a. + * decl.c (initialize_predefined_identifiers): Add heap identifiers. + (decl_maybe_constant_destruction): New function. + (cp_finish_decl): Don't clear TREE_READONLY for constexpr variables + with non-trivial, but constexpr destructors. + (register_dtor_fn): For constexpr variables with constexpr non-trivial + destructors call cxx_maybe_build_cleanup instead of adding destructor + calls at runtime. + (expand_static_init): For constexpr variables with constexpr + non-trivial destructors call cxx_maybe_build_cleanup. + (grokdeclarator): Allow constexpr destructors for c++2a. Formatting + fix. + (cxx_maybe_build_cleanup): For constexpr variables with constexpr + non-trivial destructors call cxx_constant_dtor instead of adding + destructor calls at runtime. + * init.c: Include stor-layout.h. + (build_new_constexpr_heap_type, maybe_wrap_new_for_constexpr): New + functions. + (build_new_1): For c++2a and new[], add cast around the alloc call + to help constexpr evaluation figure out the type of the heap storage. + (build_vec_delete_1): Set DECL_INITIAL of tbase and emit a DECL_EXPR + for it instead of initializing an uninitialized variable. + * method.c: Include intl.h. + (SFK_CTOR_P, SFK_DTOR_P, SFK_ASSIGN_P, SFK_COPY_P, SFK_MOVE_P): Move + definitions earlier. + (process_subob_fn): Add sfk argument, adjust non-constexpr call + diagnostics based on it. + (walk_field_subobs): Formatting fixes. Adjust process_subob_fn caller. + (synthesized_method_base_walk): Likewise. + (synthesized_method_walk): Set *constexpr_p to true for dtors in c++2a. + Fix up DR number in comment. + (implicitly_declare_fn): Formatting fix. + * typeck2.c (store_init_value): Don't call cp_fully_fold_init on + initializers of automatic non-constexpr variables in constexpr + functions. + 2019-10-04 Jakub Jelinek PR c++/71504 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 3838f765a1d..4abcfaf4c1d 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_p (tree, tree, splay_tree); static tree end_of_base (tree); static tree get_vcall_index (tree, tree); static bool type_maybe_constexpr_default_constructor (tree); +static bool type_maybe_constexpr_destructor (tree); static bool field_poverlapping_p (tree); /* Return a COND_EXPR that executes TRUE_STMT if this execution of the @@ -5242,7 +5243,7 @@ type_has_constexpr_default_constructor (tree t) without forcing a lazy declaration (which might cause undesired instantiations). */ -bool +static bool type_maybe_constexpr_default_constructor (tree t) { if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t) @@ -5252,6 +5253,34 @@ type_maybe_constexpr_default_constructor (tree t) return type_has_constexpr_default_constructor (t); } +/* Returns true iff class T has a constexpr destructor. */ + +bool +type_has_constexpr_destructor (tree t) +{ + tree fns; + + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + /* Non-trivial, we need to check subobject destructors. */ + lazily_declare_fn (sfk_destructor, t); + fns = CLASSTYPE_DESTRUCTOR (t); + return (fns && DECL_DECLARED_CONSTEXPR_P (fns)); +} + +/* Returns true iff class T has a constexpr destructor or has an + implicitly declared destructor that we can't tell if it's constexpr + without forcing a lazy declaration (which might cause undesired + instantiations). */ + +static bool +type_maybe_constexpr_destructor (tree t) +{ + if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t)) + /* Assume it's constexpr. */ + return true; + return type_has_constexpr_destructor (t); +} + /* Returns true iff class TYPE has a virtual destructor. */ bool @@ -5503,8 +5532,11 @@ finalize_literal_type_property (tree t) { tree fn; - if (cxx_dialect < cxx11 - || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)) + if (cxx_dialect < cxx11) + CLASSTYPE_LITERAL_P (t) = false; + else if (CLASSTYPE_LITERAL_P (t) + && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) + && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t))) CLASSTYPE_LITERAL_P (t) = false; else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t)) CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17); @@ -5558,8 +5590,12 @@ explain_non_literal_class (tree t) inform (UNKNOWN_LOCATION, " %qT is a closure type, which is only literal in " "C++17 and later", t); - else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)) + else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)) inform (UNKNOWN_LOCATION, " %q+T has a non-trivial destructor", t); + else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) + && !type_maybe_constexpr_destructor (t)) + inform (UNKNOWN_LOCATION, " %q+T does not have % destructor", + t); else if (CLASSTYPE_NON_AGGREGATE (t) && !TYPE_HAS_TRIVIAL_DFLT (t) && !LAMBDA_TYPE_P (t) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 2dc57f145c3..f50cc04c293 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "timevar.h" #include "fold-const-call.h" +#include "stor-layout.h" static bool verify_constant (tree, bool, bool *, bool *); #define VERIFY_CONSTANT(X) \ @@ -1006,17 +1007,35 @@ enum constexpr_switch_state { css_default_processing }; +/* The constexpr expansion context part which needs one instance per + cxx_eval_outermost_constant_expr invocation. VALUES is a map of values of + variables initialized within the expression. */ + +struct constexpr_global_ctx { + /* Values for any temporaries or local variables within the + constant-expression. */ + hash_map values; + /* Number of cxx_eval_constant_expression calls (except skipped ones, + on simple constants or location wrappers) encountered during current + cxx_eval_outermost_constant_expr call. */ + HOST_WIDE_INT constexpr_ops_count; + /* Heap VAR_DECLs created during the evaluation of the outermost constant + expression. */ + auto_vec heap_vars; + /* Constructor. */ + constexpr_global_ctx () : constexpr_ops_count (0) {} +}; + /* The constexpr expansion context. CALL is the current function expansion, CTOR is the current aggregate initializer, OBJECT is the - object being initialized by CTOR, either a VAR_DECL or a _REF. VALUES - is a map of values of variables initialized within the expression. */ + object being initialized by CTOR, either a VAR_DECL or a _REF. */ struct constexpr_ctx { + /* The part of the context that needs to be unique to the whole + cxx_eval_outermost_constant_expr invocation. */ + constexpr_global_ctx *global; /* The innermost call we're evaluating. */ constexpr_call *call; - /* Values for any temporaries or local variables within the - constant-expression. */ - hash_map *values; /* SAVE_EXPRs that we've seen within the current LOOP_EXPR. NULL if we aren't inside a loop. */ vec *save_exprs; @@ -1027,10 +1046,6 @@ struct constexpr_ctx { tree object; /* If inside SWITCH_EXPR. */ constexpr_switch_state *css_state; - /* Number of cxx_eval_constant_expression calls (except skipped ones, - on simple constants or location wrappers) encountered during current - cxx_eval_outermost_constant_expr call. */ - HOST_WIDE_INT *constexpr_ops_count; /* Whether we should error on a non-constant expression or fail quietly. */ bool quiet; @@ -1578,6 +1593,17 @@ modifying_const_object_error (tree expr, tree obj) inform (location_of (obj), "originally declared % here"); } +/* Return true if FNDECL is a replaceable global allocation function that + should be useable during constant expression evaluation. */ + +static inline bool +cxx_replaceable_global_alloc_fn (tree fndecl) +{ + return (cxx_dialect >= cxx2a + && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fndecl)) + && CP_DECL_CONTEXT (fndecl) == global_namespace); +} + /* Subroutine of cxx_eval_constant_expression. Evaluate the call expression tree T in the context of OLD_CALL expression evaluation. */ @@ -1656,6 +1682,62 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, lval, non_constant_p, overflow_p); if (!DECL_DECLARED_CONSTEXPR_P (fun)) { + if (cxx_replaceable_global_alloc_fn (fun)) + { + const int nargs = call_expr_nargs (t); + tree arg0 = NULL_TREE; + for (int i = 0; i < nargs; ++i) + { + tree arg = CALL_EXPR_ARG (t, i); + arg = cxx_eval_constant_expression (ctx, arg, false, + non_constant_p, overflow_p); + VERIFY_CONSTANT (arg); + if (i == 0) + arg0 = arg; + } + gcc_assert (arg0); + if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun))) + { + tree type = build_array_type_nelts (char_type_node, + tree_to_uhwi (arg0)); + tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, + type); + DECL_ARTIFICIAL (var) = 1; + TREE_STATIC (var) = 1; + ctx->global->heap_vars.safe_push (var); + ctx->global->values.put (var, NULL_TREE); + return fold_convert (ptr_type_node, build_address (var)); + } + else + { + STRIP_NOPS (arg0); + if (TREE_CODE (arg0) == ADDR_EXPR + && VAR_P (TREE_OPERAND (arg0, 0))) + { + tree var = TREE_OPERAND (arg0, 0); + if (DECL_NAME (var) == heap_uninit_identifier + || DECL_NAME (var) == heap_identifier) + { + DECL_NAME (var) = heap_deleted_identifier; + ctx->global->values.remove (var); + return void_node; + } + else if (DECL_NAME (var) == heap_deleted_identifier) + { + if (!ctx->quiet) + error_at (loc, "deallocation of already deallocated " + "storage"); + *non_constant_p = true; + return t; + } + } + if (!ctx->quiet) + error_at (loc, "deallocation of storage that was " + "not previously allocated"); + *non_constant_p = true; + return t; + } + } if (!ctx->quiet) { if (!lambda_static_thunk_p (fun)) @@ -1675,7 +1757,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, new_ctx.object = AGGR_INIT_EXPR_SLOT (t); tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL); CONSTRUCTOR_NO_CLEARING (ctor) = true; - ctx->values->put (new_ctx.object, ctor); + ctx->global->values.put (new_ctx.object, ctor); ctx = &new_ctx; } @@ -1877,7 +1959,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, arg = unshare_constructor (arg); if (TREE_CODE (arg) == CONSTRUCTOR) vec_safe_push (ctors, arg); - ctx->values->put (remapped, arg); + ctx->global->values.put (remapped, arg); remapped = DECL_CHAIN (remapped); } /* Add the RESULT_DECL to the values map, too. */ @@ -1887,11 +1969,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, slot = AGGR_INIT_EXPR_SLOT (t); tree addr = build_address (slot); addr = build_nop (TREE_TYPE (res), addr); - ctx->values->put (res, addr); - ctx->values->put (slot, NULL_TREE); + ctx->global->values.put (res, addr); + ctx->global->values.put (slot, NULL_TREE); } else - ctx->values->put (res, NULL_TREE); + ctx->global->values.put (res, NULL_TREE); /* Track the callee's evaluated SAVE_EXPRs so that we can forget their values after the call. */ @@ -1916,7 +1998,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, result = void_node; else { - result = *ctx->values->get (slot ? slot : res); + result = *ctx->global->values.get (slot ? slot : res); if (result == NULL_TREE && !*non_constant_p) { if (!ctx->quiet) @@ -1934,8 +2016,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, && CLASS_TYPE_P (TREE_TYPE (new_obj)) && CP_TYPE_CONST_P (TREE_TYPE (new_obj))) { - /* Subobjects might not be stored in ctx->values but we can - get its CONSTRUCTOR by evaluating *this. */ + /* Subobjects might not be stored in ctx->global->values but we + can get its CONSTRUCTOR by evaluating *this. */ tree e = cxx_eval_constant_expression (ctx, new_obj, /*lval*/false, non_constant_p, @@ -1947,17 +2029,17 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned int i; tree save_expr; FOR_EACH_VEC_ELT (save_exprs, i, save_expr) - ctx_with_save_exprs.values->remove (save_expr); + ctx->global->values.remove (save_expr); /* Remove the parms/result from the values map. Is it worth bothering to do this when the map itself is only live for one constexpr evaluation? If so, maybe also clear out other vars from call, maybe in BIND_EXPR handling? */ - ctx->values->remove (res); + ctx->global->values.remove (res); if (slot) - ctx->values->remove (slot); + ctx->global->values.remove (slot); for (tree parm = parms; parm; parm = TREE_CHAIN (parm)) - ctx->values->remove (parm); + ctx->global->values.remove (parm); /* Free any parameter CONSTRUCTORs we aren't returning directly. */ while (!ctors->is_empty ()) @@ -3077,7 +3159,7 @@ verify_ctor_sanity (const constexpr_ctx *ctx, tree type) (TREE_TYPE (type), TREE_TYPE (otype))))); } gcc_assert (!ctx->object || !DECL_P (ctx->object) - || *(ctx->values->get (ctx->object)) == ctx->ctor); + || *(ctx->global->values.get (ctx->object)) == ctx->ctor); } /* Subroutine of cxx_eval_constant_expression. @@ -3610,7 +3692,23 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, static void non_const_var_error (tree r) { + auto_diagnostic_group d; tree type = TREE_TYPE (r); + if (DECL_NAME (r) == heap_uninit_identifier + || DECL_NAME (r) == heap_identifier) + { + error ("the content of uninitialized storage is not usable " + "in a constant expression"); + inform (DECL_SOURCE_LOCATION (r), "allocated here"); + return; + } + if (DECL_NAME (r) == heap_deleted_identifier) + { + error ("use of allocated storage after deallocation in a " + "constant expression"); + inform (DECL_SOURCE_LOCATION (r), "allocated here"); + return; + } error ("the value of %qD is not usable in a constant " "expression", r); /* Avoid error cascade. */ @@ -3854,7 +3952,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, DECL_NAME to handle TARGET_EXPR temporaries, which are fair game. */ valp = NULL; else if (DECL_P (object)) - valp = ctx->values->get (object); + valp = ctx->global->values.get (object); else valp = NULL; if (!valp) @@ -4056,7 +4154,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p); if (ctors->is_empty()) /* The hash table might have moved since the get earlier. */ - valp = ctx->values->get (object); + valp = ctx->global->values.get (object); } /* Don't share a CONSTRUCTOR that might be changed later. */ @@ -4332,6 +4430,12 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, tree *jump_target) { constexpr_ctx new_ctx = *ctx; + tree local_target; + if (!jump_target) + { + local_target = NULL_TREE; + jump_target = &local_target; + } tree body, cond = NULL_TREE, expr = NULL_TREE; int count = 0; @@ -4410,7 +4514,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, unsigned int i; tree save_expr; FOR_EACH_VEC_ELT (save_exprs, i, save_expr) - new_ctx.values->remove (save_expr); + ctx->global->values.remove (save_expr); save_exprs.truncate (0); if (++count >= constexpr_loop_limit) @@ -4434,7 +4538,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, unsigned int i; tree save_expr; FOR_EACH_VEC_ELT (save_exprs, i, save_expr) - new_ctx.values->remove (save_expr); + ctx->global->values.remove (save_expr); return NULL_TREE; } @@ -4586,14 +4690,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } /* Avoid excessively long constexpr evaluations. */ - if (++*ctx->constexpr_ops_count >= constexpr_ops_limit) + if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit) { if (!ctx->quiet) error_at (cp_expr_loc_or_input_loc (t), "% evaluation operation count exceeds limit of " "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)", constexpr_ops_limit); - *ctx->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT); + ctx->global->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT); *non_constant_p = true; return t; } @@ -4610,7 +4714,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* We ask for an rvalue for the RESULT_DECL when indirecting through an invisible reference, or in named return value optimization. */ - if (tree *p = ctx->values->get (t)) + if (tree *p = ctx->global->values.get (t)) return *p; else { @@ -4666,7 +4770,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) r = TARGET_EXPR_INITIAL (r); if (VAR_P (r)) - if (tree *p = ctx->values->get (r)) + if (tree *p = ctx->global->values.get (r)) if (*p != NULL_TREE) r = *p; if (DECL_P (r)) @@ -4693,7 +4797,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case PARM_DECL: if (lval && !TYPE_REF_P (TREE_TYPE (t))) /* glvalue use. */; - else if (tree *p = ctx->values->get (r)) + else if (tree *p = ctx->global->values.get (r)) r = *p; else if (lval) /* Defer in case this is only used for its type. */; @@ -4735,7 +4839,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, new_ctx.object = r; new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL); CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true; - new_ctx.values->put (r, new_ctx.ctor); + ctx->global->values.put (r, new_ctx.ctor); ctx = &new_ctx; } @@ -4751,12 +4855,12 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (CLASS_TYPE_P (TREE_TYPE (r)) && CP_TYPE_CONST_P (TREE_TYPE (r))) TREE_READONLY (init) = true; - ctx->values->put (r, init); + ctx->global->values.put (r, init); } else if (ctx == &new_ctx) /* We gave it a CONSTRUCTOR above. */; else - ctx->values->put (r, NULL_TREE); + ctx->global->values.put (r, NULL_TREE); } break; @@ -4782,7 +4886,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL); CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true; new_ctx.object = TARGET_EXPR_SLOT (t); - ctx->values->put (new_ctx.object, new_ctx.ctor); + ctx->global->values.put (new_ctx.object, new_ctx.ctor); ctx = &new_ctx; } /* Pass false for 'lval' because this indicates @@ -4797,7 +4901,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, { tree slot = TARGET_EXPR_SLOT (t); r = unshare_constructor (r); - ctx->values->put (slot, r); + ctx->global->values.put (slot, r); return slot; } break; @@ -4837,13 +4941,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case SAVE_EXPR: /* Avoid evaluating a SAVE_EXPR more than once. */ - if (tree *p = ctx->values->get (t)) + if (tree *p = ctx->global->values.get (t)) r = *p; else { r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false, non_constant_p, overflow_p); - ctx->values->put (t, r); + ctx->global->values.put (t, r); if (ctx->save_exprs) ctx->save_exprs->safe_push (t); } @@ -5177,8 +5281,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (VOID_TYPE_P (type)) return void_node; - if (TREE_CODE (op) == PTRMEM_CST - && !TYPE_PTRMEM_P (type)) + if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type)) op = cplus_expand_constant (op); if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR) @@ -5232,6 +5335,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } } + if (INDIRECT_TYPE_P (type) + && TREE_CODE (op) == NOP_EXPR + && TREE_TYPE (op) == ptr_type_node + && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR + && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0)) + && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), + 0)) == heap_uninit_identifier) + { + tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0); + tree var_size = TYPE_SIZE_UNIT (TREE_TYPE (var)); + tree elt_type = TREE_TYPE (type); + tree cookie_size = NULL_TREE; + if (TREE_CODE (elt_type) == RECORD_TYPE + && TYPE_NAME (elt_type) == heap_identifier) + { + tree fld1 = TYPE_FIELDS (elt_type); + tree fld2 = DECL_CHAIN (fld1); + elt_type = TREE_TYPE (TREE_TYPE (fld2)); + cookie_size = TYPE_SIZE_UNIT (TREE_TYPE (fld1)); + } + DECL_NAME (var) = heap_identifier; + TREE_TYPE (var) + = build_new_constexpr_heap_type (elt_type, cookie_size, + var_size); + TREE_TYPE (TREE_OPERAND (op, 0)) + = build_pointer_type (TREE_TYPE (var)); + } + if (op == oldop && tcode != UNARY_PLUS_EXPR) /* We didn't fold at the top so we could check for ptr-int conversion. */ @@ -5473,6 +5604,7 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) return NULL_TREE; } + static void instantiate_constexpr_fns (tree t) { @@ -5481,34 +5613,58 @@ instantiate_constexpr_fns (tree t) input_location = loc; } +/* Look for heap variables in the expression *TP. */ + +static tree +find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/) +{ + if (VAR_P (*tp) + && (DECL_NAME (*tp) == heap_uninit_identifier + || DECL_NAME (*tp) == heap_identifier + || DECL_NAME (*tp) == heap_deleted_identifier)) + return *tp; + + if (TYPE_P (*tp)) + *walk_subtrees = 0; + return NULL_TREE; +} + /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression. STRICT has the same sense as for constant_value_1: true if we only allow conforming C++ constant expressions, or false if we want a constant value even if it doesn't conform. MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as - per P0595 even when ALLOW_NON_CONSTANT is true. */ + per P0595 even when ALLOW_NON_CONSTANT is true. + CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable. + OBJECT must be non-NULL in that case. */ static tree cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, bool strict = true, bool manifestly_const_eval = false, + bool constexpr_dtor = false, tree object = NULL_TREE) { auto_timevar time (TV_CONSTEXPR); bool non_constant_p = false; bool overflow_p = false; - hash_map map; - HOST_WIDE_INT constexpr_ctx_count = 0; - constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, - &constexpr_ctx_count, allow_non_constant, strict, + constexpr_global_ctx global_ctx; + constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL, + allow_non_constant, strict, manifestly_const_eval || !allow_non_constant }; tree type = initialized_type (t); tree r = t; if (VOID_TYPE_P (type)) - return t; + { + if (constexpr_dtor) + /* Used for destructors of array elements. */ + type = TREE_TYPE (object); + else + return t; + } if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) { /* In C++14 an NSDMI can participate in aggregate initialization, @@ -5518,8 +5674,22 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, update ctx.values for the VAR_DECL. We use the same strategy for C++11 constexpr constructors that refer to the object being initialized. */ - ctx.ctor = build_constructor (type, NULL); - CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true; + if (constexpr_dtor) + { + gcc_assert (object && VAR_P (object)); + gcc_assert (DECL_DECLARED_CONSTEXPR_P (object)); + gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object)); + ctx.ctor = unshare_expr (DECL_INITIAL (object)); + TREE_READONLY (ctx.ctor) = false; + /* Temporarily force decl_really_constant_value to return false + for it, we want to use ctx.ctor for the current value instead. */ + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false; + } + else + { + ctx.ctor = build_constructor (type, NULL); + CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true; + } if (!object) { if (TREE_CODE (t) == TARGET_EXPR) @@ -5532,7 +5702,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (object))); if (object && DECL_P (object)) - map.put (object, ctx.ctor); + global_ctx.values.put (object, ctx.ctor); if (TREE_CODE (r) == TARGET_EXPR) /* Avoid creating another CONSTRUCTOR when we expand the TARGET_EXPR. */ @@ -5543,13 +5713,15 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); - verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); + if (!constexpr_dtor) + verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); + else + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true; /* Mutable logic is a bit tricky: we want to allow initialization of constexpr variables with mutable members, but we can't copy those members to another constexpr variable. */ - if (TREE_CODE (r) == CONSTRUCTOR - && CONSTRUCTOR_MUTABLE_POISON (r)) + if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r)) { if (!allow_non_constant) error ("%qE is not a constant expression because it refers to " @@ -5557,8 +5729,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, non_constant_p = true; } - if (TREE_CODE (r) == CONSTRUCTOR - && CONSTRUCTOR_NO_CLEARING (r)) + if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r)) { if (!allow_non_constant) error ("%qE is not a constant expression because it refers to " @@ -5567,6 +5738,32 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, non_constant_p = true; } + if (!global_ctx.heap_vars.is_empty ()) + { + tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs, + NULL); + unsigned int i; + if (heap_var) + { + if (!allow_non_constant && !non_constant_p) + error_at (DECL_SOURCE_LOCATION (heap_var), + "%qE is not a constant expression because it refers to " + "a result of %", t); + r = t; + non_constant_p = true; + } + FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var) + if (DECL_NAME (heap_var) != heap_deleted_identifier) + { + if (!allow_non_constant && !non_constant_p) + error_at (DECL_SOURCE_LOCATION (heap_var), + "%qE is not a constant expression because allocated " + "storage has not been deallocated", t); + r = t; + non_constant_p = true; + } + } + /* Technically we should check this for all subexpressions, but that runs into problems with our internal representation of pointer subtraction and the 5.19 rules are still in flux. */ @@ -5592,6 +5789,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (non_constant_p && !allow_non_constant) return error_mark_node; + else if (constexpr_dtor) + return r; else if (non_constant_p && TREE_CONSTANT (r)) { /* If __builtin_is_constant_evaluated () was evaluated to true @@ -5599,7 +5798,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, punt. */ if (manifestly_const_eval) return cxx_eval_outermost_constant_expr (t, true, strict, - false, object); + false, false, object); /* This isn't actually constant, so unset TREE_CONSTANT. Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires it to be set if it is invariant address, even when it is not @@ -5627,7 +5826,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, return t; else if (TREE_CODE (t) != CONSTRUCTOR) { - r = get_target_expr (r); + r = get_target_expr_sfinae (r, tf_warning_or_error | tf_no_cleanup); TREE_CONSTANT (r) = true; } } @@ -5642,7 +5841,16 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, tree cxx_constant_value (tree t, tree decl) { - return cxx_eval_outermost_constant_expr (t, false, true, true, decl); + return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl); +} + +/* Like cxx_constant_value, but used for evaluation of constexpr destructors + of constexpr variables. The actual initializer of DECL is not modified. */ + +void +cxx_constant_dtor (tree t, tree decl) +{ + cxx_eval_outermost_constant_expr (t, false, true, true, true, decl); } /* Helper routine for fold_simple function. Either return simplified @@ -5746,14 +5954,14 @@ maybe_constant_value (tree t, tree decl, bool manifestly_const_eval) return t; if (manifestly_const_eval) - return cxx_eval_outermost_constant_expr (t, true, true, true, decl); + return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl); if (cv_cache == NULL) cv_cache = hash_map::create_ggc (101); if (tree *cached = cv_cache->get (t)) return *cached; - r = cxx_eval_outermost_constant_expr (t, true, true, false, decl); + r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl); gcc_checking_assert (r == t || CONVERT_EXPR_P (t) || TREE_CODE (t) == VIEW_CONVERT_EXPR @@ -5815,7 +6023,7 @@ fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, tree r = cxx_eval_outermost_constant_expr (t, true, true, manifestly_const_eval, - NULL_TREE); + false, NULL_TREE); /* cp_tree_equal looks through NOPs, so allow them. */ gcc_checking_assert (r == t || CONVERT_EXPR_P (t) @@ -5919,7 +6127,7 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant, else t = cxx_eval_outermost_constant_expr (t, allow_non_constant, /*strict*/false, - manifestly_const_eval, decl); + manifestly_const_eval, false, decl); if (TREE_CODE (t) == TARGET_EXPR) { tree init = TARGET_EXPR_INITIAL (t); @@ -6213,7 +6421,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, if (!DECL_DECLARED_CONSTEXPR_P (fun) /* Allow any built-in function; if the expansion isn't constant, we'll deal with that then. */ - && !fndecl_built_in_p (fun)) + && !fndecl_built_in_p (fun) + /* In C++2a, replaceable global allocation functions + are constant expressions. */ + && !cxx_replaceable_global_alloc_fn (fun)) { if (flags & tf_error) { @@ -6442,6 +6653,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, goto fail; if (!RECUR (TREE_OPERAND (t, 0), any)) return false; + /* Just ignore clobbers. */ + if (TREE_CLOBBER_P (TREE_OPERAND (t, 1))) + return true; if (!RECUR (TREE_OPERAND (t, 1), rval)) return false; return true; @@ -6911,7 +7125,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return true; case COND_EXPR: - if (COND_EXPR_IS_VEC_DELETE (t)) + if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx2a) { if (flags & tf_error) error_at (loc, "% is not a constant expression"); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index be1a44e4373..b82b5808197 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -172,6 +172,9 @@ enum cp_tree_index CPTI_VALUE_IDENTIFIER, CPTI_FUN_IDENTIFIER, CPTI_CLOSURE_IDENTIFIER, + CPTI_HEAP_UNINIT_IDENTIFIER, + CPTI_HEAP_IDENTIFIER, + CPTI_HEAP_DELETED_IDENTIFIER, CPTI_LANG_NAME_C, CPTI_LANG_NAME_CPLUSPLUS, @@ -310,6 +313,9 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; #define value_identifier cp_global_trees[CPTI_VALUE_IDENTIFIER] #define fun_identifier cp_global_trees[CPTI_FUN_IDENTIFIER] #define closure_identifier cp_global_trees[CPTI_CLOSURE_IDENTIFIER] +#define heap_uninit_identifier cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER] +#define heap_identifier cp_global_trees[CPTI_HEAP_IDENTIFIER] +#define heap_deleted_identifier cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER] #define lang_name_c cp_global_trees[CPTI_LANG_NAME_C] #define lang_name_cplusplus cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS] @@ -6342,6 +6348,7 @@ extern bool vbase_has_user_provided_move_assign (tree); extern tree default_init_uninitialized_part (tree); extern bool trivial_default_constructor_is_constexpr (tree); extern bool type_has_constexpr_default_constructor (tree); +extern bool type_has_constexpr_destructor (tree); extern bool type_has_virtual_destructor (tree); extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared); extern bool classtype_has_non_deleted_move_ctor (tree); @@ -6648,6 +6655,7 @@ extern tree build_offset_ref (tree, tree, bool, extern tree throw_bad_array_new_length (void); extern bool type_has_new_extended_alignment (tree); extern unsigned malloc_alignment (void); +extern tree build_new_constexpr_heap_type (tree, tree, tree); extern tree build_new (vec **, tree, tree, vec **, int, tsubst_flags_t); @@ -7747,6 +7755,7 @@ extern bool require_constant_expression (tree); extern bool require_rvalue_constant_expression (tree); extern bool require_potential_rvalue_constant_expression (tree); extern tree cxx_constant_value (tree, tree = NULL_TREE); +extern void cxx_constant_dtor (tree, tree); extern tree cxx_constant_init (tree, tree = NULL_TREE); extern tree maybe_constant_value (tree, tree = NULL_TREE, bool = false); extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index d6cca65eceb..ea9a0011e24 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4146,6 +4146,9 @@ initialize_predefined_identifiers (void) {"value", &value_identifier, cik_normal}, {"_FUN", &fun_identifier, cik_normal}, {"__closure", &closure_identifier, cik_normal}, + {"heap uninit", &heap_uninit_identifier, cik_normal}, + {"heap ", &heap_identifier, cik_normal}, + {"heap deleted", &heap_deleted_identifier, cik_normal}, {NULL, NULL, cik_normal} }; @@ -7006,6 +7009,19 @@ notice_forced_label_r (tree *tp, int *walk_subtrees, void *) return NULL_TREE; } +/* Return true if DECL has either a trivial destructor, or for C++2A + is constexpr and has a constexpr destructor. */ + +static bool +decl_maybe_constant_destruction (tree decl, tree type) +{ + return (TYPE_HAS_TRIVIAL_DESTRUCTOR (type) + || (cxx_dialect >= cxx2a + && VAR_P (decl) + && DECL_DECLARED_CONSTEXPR_P (decl) + && type_has_constexpr_destructor (strip_array_types (type)))); +} + /* Finish processing of a declaration; install its line number and initial value. If the length of an array type is not known before, @@ -7430,7 +7446,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, TREE_READONLY (decl) = 1; /* Likewise if it needs destruction. */ - if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) + if (!decl_maybe_constant_destruction (decl, type)) TREE_READONLY (decl) = 0; } @@ -8312,6 +8328,13 @@ register_dtor_fn (tree decl) if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)) return void_node; + if (decl_maybe_constant_destruction (decl, type) + && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) + { + cxx_maybe_build_cleanup (decl, tf_warning_or_error); + return void_node; + } + /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or "__aeabi_atexit"), and DECL is a class object, we can just pass the destructor to "__cxa_atexit"; we don't have to build a temporary @@ -8429,7 +8452,7 @@ expand_static_init (tree decl, tree init) gcc_assert (TREE_STATIC (decl)); /* Some variables require no dynamic initialization. */ - if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) + if (decl_maybe_constant_destruction (decl, TREE_TYPE (decl))) { /* Make sure the destructor is callable. */ cxx_maybe_build_cleanup (decl, tf_warning_or_error); @@ -12697,12 +12720,13 @@ grokdeclarator (const cp_declarator *declarator, "a destructor cannot be %"); return error_mark_node; } - if (constexpr_p) - { - error_at (declspecs->locations[ds_constexpr], - "a destructor cannot be %"); - return error_mark_node; - } + if (constexpr_p && cxx_dialect < cxx2a) + { + error_at (declspecs->locations[ds_constexpr], + "% destructors only available" + " with %<-std=c++2a%> or %<-std=gnu++2a%>"); + return error_mark_node; + } } else if (sfk == sfk_constructor && friendp && !ctype) { @@ -12739,10 +12763,11 @@ grokdeclarator (const cp_declarator *declarator, } /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node. */ - function_context = (ctype != NULL_TREE) ? - decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE; - publicp = (! friendp || ! staticp) - && function_context == NULL_TREE; + function_context + = (ctype != NULL_TREE + ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE); + publicp = ((! friendp || ! staticp) + && function_context == NULL_TREE); decl = grokfndecl (ctype, type, TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR @@ -16742,6 +16767,9 @@ cxx_maybe_build_cleanup (tree decl, tsubst_flags_t complain) cleanup = error_mark_node; else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)) /* Discard the call. */; + else if (decl_maybe_constant_destruction (decl, type) + && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) + cxx_constant_dtor (call, decl); else if (cleanup) cleanup = cp_build_compound_expr (cleanup, call, complain); else diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 857f360c761..1c51e26febe 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "asan.h" +#include "stor-layout.h" static bool begin_init_stmts (tree *, tree *); static tree finish_init_stmts (bool, tree, tree); @@ -2860,6 +2861,82 @@ std_placement_new_fn_p (tree alloc_fn) return false; } +/* For element type ELT_TYPE, return the appropriate type of the heap object + containing such element(s). COOKIE_SIZE is NULL or the size of cookie + in bytes. FULL_SIZE is NULL if it is unknown how big the heap allocation + will be, otherwise size of the heap object. If COOKIE_SIZE is NULL, + return array type ELT_TYPE[FULL_SIZE / sizeof(ELT_TYPE)], otherwise return + struct { size_t[COOKIE_SIZE/sizeof(size_t)]; ELT_TYPE[N]; } + where N is nothing (flexible array member) if FULL_SIZE is NULL, otherwise + it is computed such that the size of the struct fits into FULL_SIZE. */ + +tree +build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree full_size) +{ + gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size)); + gcc_assert (full_size == NULL_TREE || tree_fits_uhwi_p (full_size)); + unsigned HOST_WIDE_INT csz = cookie_size ? tree_to_uhwi (cookie_size) : 0; + tree itype2 = NULL_TREE; + if (full_size) + { + unsigned HOST_WIDE_INT fsz = tree_to_uhwi (full_size); + gcc_assert (fsz >= csz); + fsz -= csz; + fsz /= int_size_in_bytes (elt_type); + itype2 = build_index_type (size_int (fsz - 1)); + if (!cookie_size) + return build_cplus_array_type (elt_type, itype2); + } + else + gcc_assert (cookie_size); + csz /= int_size_in_bytes (sizetype); + tree itype1 = build_index_type (size_int (csz - 1)); + tree atype1 = build_cplus_array_type (sizetype, itype1); + tree atype2 = build_cplus_array_type (elt_type, itype2); + tree rtype = cxx_make_type (RECORD_TYPE); + TYPE_NAME (rtype) = heap_identifier; + tree fld1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype1); + tree fld2 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype2); + DECL_FIELD_CONTEXT (fld1) = rtype; + DECL_FIELD_CONTEXT (fld2) = rtype; + DECL_ARTIFICIAL (fld1) = true; + DECL_ARTIFICIAL (fld2) = true; + TYPE_FIELDS (rtype) = fld1; + DECL_CHAIN (fld1) = fld2; + layout_type (rtype); + return rtype; +} + +/* Help the constexpr code to find the right type for the heap variable + by adding a NOP_EXPR around ALLOC_CALL if needed for cookie_size. + Return ALLOC_CALL or ALLOC_CALL cast to a pointer to + struct { size_t[cookie_size/sizeof(size_t)]; elt_type[]; }. */ + +static tree +maybe_wrap_new_for_constexpr (tree alloc_call, tree elt_type, tree cookie_size) +{ + if (cxx_dialect < cxx2a) + return alloc_call; + + if (current_function_decl != NULL_TREE + && !DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + return alloc_call; + + tree call_expr = extract_call_expr (alloc_call); + if (call_expr == error_mark_node) + return alloc_call; + + tree alloc_call_fndecl = cp_get_callee_fndecl_nofold (call_expr); + if (alloc_call_fndecl == NULL_TREE + || !IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl)) + || CP_DECL_CONTEXT (alloc_call_fndecl) != global_namespace) + return alloc_call; + + tree rtype = build_new_constexpr_heap_type (elt_type, cookie_size, + NULL_TREE); + return build_nop (build_pointer_type (rtype), alloc_call); +} + /* Generate code for a new-expression, including calling the "operator new" function, initializing the object, and, if an exception occurs during construction, cleaning up. The arguments are as for @@ -3327,6 +3404,10 @@ build_new_1 (vec **placement, tree type, tree nelts, } } + if (cookie_size) + alloc_call = maybe_wrap_new_for_constexpr (alloc_call, elt_type, + cookie_size); + /* In the simple case, we can stop now. */ pointer_type = build_pointer_type (type); if (!cookie_size && !is_initialized) @@ -3902,17 +3983,11 @@ build_vec_delete_1 (tree base, tree maxindex, tree type, fold_convert (sizetype, maxindex)); tbase = create_temporary_var (ptype); - tbase_init - = cp_build_modify_expr (input_location, tbase, NOP_EXPR, - fold_build_pointer_plus_loc (input_location, - fold_convert (ptype, - base), - virtual_size), - complain); - if (tbase_init == error_mark_node) - return error_mark_node; - controller = build3 (BIND_EXPR, void_type_node, tbase, - NULL_TREE, NULL_TREE); + DECL_INITIAL (tbase) + = fold_build_pointer_plus_loc (input_location, fold_convert (ptype, base), + virtual_size); + tbase_init = build_stmt (input_location, DECL_EXPR, tbase); + controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE); TREE_SIDE_EFFECTS (controller) = 1; body = build1 (EXIT_EXPR, void_type_node, diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 53fa85b9790..01bf534aef2 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "varasm.h" #include "toplev.h" +#include "intl.h" #include "common/common-target.h" static void do_build_copy_assign (tree); @@ -1237,12 +1238,24 @@ is_xible (enum tree_code code, tree to, tree from) return !!expr; } +/* Categorize various special_function_kinds. */ +#define SFK_CTOR_P(sfk) \ + ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) +#define SFK_DTOR_P(sfk) \ + ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor) +#define SFK_ASSIGN_P(sfk) \ + ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment) +#define SFK_COPY_P(sfk) \ + ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment) +#define SFK_MOVE_P(sfk) \ + ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment) + /* Subroutine of synthesized_method_walk. Update SPEC_P, TRIVIAL_P and DELETED_P or give an error message MSG with argument ARG. */ static void -process_subob_fn (tree fn, tree *spec_p, bool *trivial_p, - bool *deleted_p, bool *constexpr_p, +process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p, + bool *trivial_p, bool *deleted_p, bool *constexpr_p, bool diag, tree arg, bool dtor_from_ctor = false) { if (!fn || fn == error_mark_node) @@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p, bool *trivial_p, if (diag) { inform (DECL_SOURCE_LOCATION (fn), - "defaulted constructor calls non-% %qD", fn); + SFK_DTOR_P (sfk) + ? G_("defaulted destructor calls non-% %qD") + : G_("defaulted constructor calls non-% %qD"), + fn); explain_invalid_constexpr_fn (fn); } } } -/* Categorize various special_function_kinds. */ -#define SFK_CTOR_P(sfk) \ - ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) -#define SFK_DTOR_P(sfk) \ - ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor) -#define SFK_ASSIGN_P(sfk) \ - ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment) -#define SFK_COPY_P(sfk) \ - ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment) -#define SFK_MOVE_P(sfk) \ - ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment) - /* Subroutine of synthesized_method_walk to allow recursion into anonymous aggregates. If DTOR_FROM_CTOR is true, we're walking subobject destructors called from a synthesized constructor, in which case we don't consider @@ -1318,8 +1322,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, { tree mem_type, argtype, rval; - if (TREE_CODE (field) != FIELD_DECL - || DECL_ARTIFICIAL (field)) + if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field)) continue; /* Variant members only affect deletedness. In particular, they don't @@ -1457,7 +1460,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); - process_subob_fn (rval, spec_p, trivial_p, deleted_p, + process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p, constexpr_p, diag, field, dtor_from_ctor); } } @@ -1510,23 +1513,23 @@ synthesized_method_base_walk (tree binfo, tree base_binfo, && DECL_CONTEXT (*inheriting_ctor) == DECL_CONTEXT (rval)) *inheriting_ctor = DECL_CLONED_FUNCTION (rval); - process_subob_fn (rval, spec_p, trivial_p, deleted_p, + process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p, constexpr_p, diag, BINFO_TYPE (base_binfo)); - if (SFK_CTOR_P (sfk) && - (!BINFO_VIRTUAL_P (base_binfo) - || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo)))) + if (SFK_CTOR_P (sfk) + && (!BINFO_VIRTUAL_P (base_binfo) + || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo)))) { /* In a constructor we also need to check the subobject destructors for cleanup of partially constructed objects. */ tree dtor = locate_fn_flags (base_binfo, complete_dtor_identifier, NULL_TREE, flags, diag ? tf_warning_or_error : tf_none); - /* Note that we don't pass down trivial_p; the subobject - destructors don't affect triviality of the constructor. Nor - do they affect constexpr-ness (a constant expression doesn't - throw) or exception-specification (a throw from one of the - dtors would be a double-fault). */ - process_subob_fn (dtor, NULL, NULL, deleted_p, NULL, false, + /* Note that we don't pass down trivial_p; the subobject + destructors don't affect triviality of the constructor. Nor + do they affect constexpr-ness (a constant expression doesn't + throw) or exception-specification (a throw from one of the + dtors would be a double-fault). */ + process_subob_fn (dtor, sfk, NULL, NULL, deleted_p, NULL, false, BINFO_TYPE (base_binfo), /*dtor_from_ctor*/true); } @@ -1608,7 +1611,8 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, member is a constexpr function. */ if (constexpr_p) *constexpr_p = (SFK_CTOR_P (sfk) - || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)); + || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14) + || (SFK_DTOR_P (sfk) && cxx_dialect >= cxx2a)); bool expected_trivial = type_has_trivial_fn (ctype, sfk); if (trivial_p) @@ -1704,8 +1708,8 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, else if (vec_safe_is_empty (vbases)) /* No virtual bases to worry about. */; else if (ABSTRACT_CLASS_TYPE_P (ctype) && cxx_dialect >= cxx14 - /* DR 1658 specifis that vbases of abstract classes are - ignored for both ctors and dtors. Except DR 2338 + /* DR 1658 specifies that vbases of abstract classes are + ignored for both ctors and dtors. Except DR 2336 overrides that skipping when determing the eh-spec of a virtual destructor. */ && sfk != sfk_virtual_destructor) @@ -2046,7 +2050,8 @@ implicitly_declare_fn (special_function_kind kind, tree type, constexpr_p = false; /* A trivial copy/move constructor is also a constexpr constructor, unless the class has virtual bases (7.1.5p4). */ - else if (trivial_p && cxx_dialect >= cxx11 + else if (trivial_p + && cxx_dialect >= cxx11 && (kind == sfk_copy_constructor || kind == sfk_move_constructor) && !CLASSTYPE_VBASECLASSES (type)) diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index ec0e6a7e33d..54d6b848157 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -902,7 +902,13 @@ store_init_value (tree decl, tree init, vec** cleanups, int flags) value = oldval; } } - value = cp_fully_fold_init (value); + /* Don't fold initializers of automatic variables in constexpr functions, + that might fold away something that needs to be diagnosed at constexpr + evaluation time. */ + if (!current_function_decl + || !DECL_DECLARED_CONSTEXPR_P (current_function_decl) + || TREE_STATIC (decl)) + value = cp_fully_fold_init (value); /* Handle aggregate NSDMI in non-constant initializers, too. */ value = replace_placeholders (value, decl); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 10ba0d9ed7d..8a0a03526e7 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,23 @@ 2019-10-05 Jakub Jelinek + PR c++/91369 - Implement P0784R7: constexpr new + * g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for + c++2a. + * g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics + in c++17_down, adjust expected wording. + * g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down. + * g++.dg/cpp2a/constexpr-dtor1.C: New test. + * g++.dg/cpp2a/constexpr-dtor2.C: New test. + * g++.dg/cpp2a/constexpr-dtor3.C: New test. + * g++.dg/cpp2a/constexpr-new1.C: New test. + * g++.dg/cpp2a/constexpr-new2.C: New test. + * g++.dg/cpp2a/constexpr-new3.C: New test. + * g++.dg/cpp2a/constexpr-new4.C: New test. + * g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and + __cpp_constexpr_dynamic_alloc tests. Tweak __cpp_* tests for c++2a + features to use style like older features, including #ifdef test. + * g++.dg/ext/is_literal_type3.C: New test. + PR tree-optimization/91734 * gcc.dg/pr91734.c: New test. diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C index 4a453a4d361..999f9b7851e 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C @@ -5,8 +5,9 @@ struct A { ~A(); }; constexpr int f(int i) { return i; } constexpr int g(A* ap) { - return f((delete[] ap, 42)); // { dg-message "" } + return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } } } A a; constexpr int i = g(&a); // { dg-error "" } + // { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 } diff --git a/gcc/testsuite/g++.dg/cpp0x/locations1.C b/gcc/testsuite/g++.dg/cpp0x/locations1.C index 6de0368fcbc..62a72ea2804 100644 --- a/gcc/testsuite/g++.dg/cpp0x/locations1.C +++ b/gcc/testsuite/g++.dg/cpp0x/locations1.C @@ -11,7 +11,7 @@ struct S { virtual S(); // { dg-error "3:constructors cannot be declared .virtual." } constexpr int s = 1; // { dg-error "3:non-static data member .s. declared .constexpr." } - constexpr ~S(); // { dg-error "3:a destructor cannot be .constexpr." } + constexpr ~S(); // { dg-error "3:'constexpr' destructors only available with" "" { target c++17_down } } }; typedef constexpr int my_int; // { dg-error "9:.constexpr. cannot appear in a typedef declaration" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C index 9e300b9a866..90cb42595c4 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C @@ -4,7 +4,7 @@ constexpr int *f4(bool b) { if (b) { return nullptr; } else { - return new int{42}; // { dg-error "call to non-.constexpr." } + return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } } } } static_assert(f4(true) == nullptr, ""); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C new file mode 100644 index 00000000000..a2ea6bfb338 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C @@ -0,0 +1,9 @@ +// P0784R7 +// { dg-do compile { target c++11 } } + +struct S +{ + constexpr S () : s (0) {} + constexpr ~S () {} // { dg-error "'constexpr' destructors only available with" "" { target c++17_down } } + int s; +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C new file mode 100644 index 00000000000..397cfb0f1a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C @@ -0,0 +1,66 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +struct S +{ + constexpr S () : r (4), s (3) { --r; s -= 2; } + constexpr ~S () { if (s == 1) s = 0; else asm (""); if (s == 0 && r == 3) r = 0; else asm (""); } + int r, s; +}; +struct T : public S +{ + constexpr T () : t (2) {} + int t; + S u; +}; +struct U : public S +{ + constexpr U (int x) : u (x) {} + constexpr ~U () = default; + int u; + S v; +}; + +constexpr S a; +constexpr T b; +constexpr U c = 3; +static_assert (a.s == 1 && a.r == 3); +static_assert (b.s == 1 && b.r == 3 && b.t == 2 && b.u.s == 1 && b.u.r == 3); +static_assert (c.s == 1 && c.r == 3 && c.u == 3 && c.v.s == 1 && c.v.r == 3); + +void +foo () +{ + static constexpr S d; + static constexpr T e; + static constexpr U f = 4; + static_assert (d.s == 1 && d.r == 3); + static_assert (e.s == 1 && e.r == 3 && e.t == 2 && e.u.s == 1 && e.u.r == 3); + static_assert (f.s == 1 && f.r == 3 && f.u == 4 && f.v.s == 1 && f.v.r == 3); + if (1) + { + constexpr S g; + constexpr T h; + constexpr U i = 5; + static_assert (g.s == 1 && g.r == 3); + static_assert (h.s == 1 && h.r == 3 && h.t == 2 && h.u.s == 1 && h.u.r == 3); + static_assert (i.s == 1 && i.r == 3 && i.u == 5 && i.v.s == 1 && i.v.r == 3); + } +} + +constexpr bool +bar () +{ + S j; + T k; + U l = 6; + if (j.s != 1 || j.r != 3) + return false; + if (k.s != 1 || k.r != 3 || k.t != 2 || k.u.s != 1 || k.u.r != 3) + return false; + if (l.s != 1 || l.r != 3 || l.u != 6 || l.v.s != 1 || l.v.r != 3) + return false; + return true; +} + +static_assert (bar ()); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C new file mode 100644 index 00000000000..dcfa4e6caf2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C @@ -0,0 +1,185 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +struct S +{ + constexpr S () : s (0) {} + constexpr ~S () {} + int s; +}; +struct T // { dg-message "'T' is not literal because" } +{ // { dg-message "'T' does not have 'constexpr' destructor" "" { target *-*-* } .-1 } + constexpr T () : t (0) {} + ~T () {} // { dg-message "defaulted destructor calls non-'constexpr' 'T::~T\\(\\)'" } + int t; +}; +struct U : public S +{ + constexpr U () : u (0) {} + constexpr ~U () = default; // { dg-error "explicitly defaulted function 'constexpr U::~U\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" } + int u; + T t; +}; +struct V : virtual public S +{ + V () : v (0) {} + constexpr ~V () = default; // { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" } + int v; +}; +struct W0 +{ + constexpr W0 () : w (0) {} + constexpr W0 (int x) : w (x) {} + constexpr ~W0 () { if (w == 5) asm (""); w = 3; } + int w; +}; +struct W1 +{ + constexpr W1 () : w (0) {} + constexpr W1 (int x) : w (x) {} + constexpr ~W1 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W2 +{ + constexpr W2 () : w (0) {} + constexpr W2 (int x) : w (x) {} + constexpr ~W2 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W3 +{ + constexpr W3 () : w (0) {} + constexpr W3 (int x) : w (x) {} + constexpr ~W3 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W4 +{ + constexpr W4 () : w (0) {} + constexpr W4 (int x) : w (x) {} + constexpr ~W4 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W5 +{ + constexpr W5 () : w (0) {} + constexpr W5 (int x) : w (x) {} + constexpr ~W5 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W6 +{ + constexpr W6 () : w (0) {} + constexpr W6 (int x) : w (x) {} + constexpr ~W6 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W7 +{ + constexpr W7 () : w (0) {} + constexpr W7 (int x) : w (x) {} + constexpr ~W7 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct W8 +{ + constexpr W8 () : w (0) {} + constexpr W8 (int x) : w (x) {} + constexpr ~W8 () { if (w == 5) asm (""); w = 3; } // { dg-error "inline assembly is not a constant expression" } + // { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 } + int w; +}; +struct X : public T +{ + constexpr X () : x (0) {} + constexpr ~X () = default; // { dg-error "explicitly defaulted function 'constexpr X::~X\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" } + int x; +}; +constexpr S s; +constexpr T t; // { dg-error "the type 'const T' of 'constexpr' variable 't' is not literal" } +constexpr W0 w1; +constexpr W0 w2 = 12; +constexpr W1 w3 = 5; // { dg-message "in 'constexpr' expansion of" } +constexpr W0 w4[3] = { 1, 2, 3 }; +constexpr W2 w5[3] = { 4, 5, 6 }; // { dg-message "in 'constexpr' expansion of" } + +void +f1 () +{ + constexpr S s2; + constexpr W0 w6; + constexpr W0 w7 = 12; + constexpr W3 w8 = 5; // { dg-message "in 'constexpr' expansion of" } + constexpr W0 w9[3] = { 1, 2, 3 }; + constexpr W4 w10[3] = { 4, 5, 6 }; // { dg-message "in 'constexpr' expansion of" } +} + +constexpr int +f2 () +{ + constexpr S s3; + constexpr W0 w11; + constexpr W0 w12 = 12; + constexpr W5 w13 = 5; // { dg-message "in 'constexpr' expansion of" } + constexpr W0 w14[3] = { 1, 2, 3 }; + constexpr W6 w15[3] = { 4, 5, 6 }; // { dg-message "in 'constexpr' expansion of" } + return 0; +} + +constexpr int +f3 () +{ + S s3; + W0 w11; + W0 w12 = 12; + W0 w14[3] = { 1, 2, 3 }; + return 0; +} + +constexpr int x3 = f3 (); + +constexpr int +f4 () +{ + W7 w13 = 5; + return 0; +} + +constexpr int x4 = f4 (); // { dg-message "in 'constexpr' expansion of" } + +constexpr int +f5 () +{ + W8 w15[3] = { 4, 5, 6 }; // { dg-message "in 'constexpr' expansion of" } + return 0; +} + +constexpr int x5 = f5 (); // { dg-message "in 'constexpr' expansion of" } + +void +f6 () +{ + constexpr T t2; // { dg-error "the type 'const T' of 'constexpr' variable 't2' is not literal" } +} + +constexpr int +f7 () +{ + constexpr T t3; // { dg-error "the type 'const T' of 'constexpr' variable 't3' is not literal" } + return 0; +} + +constexpr int +f8 () +{ + T t4; // { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C new file mode 100644 index 00000000000..873edd4d231 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C @@ -0,0 +1,39 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +struct S { constexpr S () : s (5) {} constexpr S (int x) : s (x) {} int s; }; + +constexpr bool +foo () +{ + int r = 0; + S *p = new S (); + p->s += 3; + r += p->s; + delete p; + p = new S (12); + p->s = p->s * 2; + r += p->s; + delete p; + int *q = new int; + *q = 25; + r += *q; + delete q; + q = new int (1); + r += *q; + if (!q) + return false; + delete q; + q = new int[5]{1,2,3,4,5}; + r += q[0] + q[4]; + delete[] q; + q = new int[4]; + q[0] = 6; + q[1] = 7; + q[3] = 8; + r += q[0] + q[1] + q[3]; + delete[] q; + return r == 5 + 3 + 2 * 12 + 25 + 1 + 1 + 5 + 6 + 7 + 8; +} +constexpr bool a = foo (); +static_assert (a); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C new file mode 100644 index 00000000000..be5496293b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C @@ -0,0 +1,21 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +template +constexpr bool +foo (const char (&x)[N]) +{ + int **p = new int *[N]; + for (int i = 0; i < N; i++) + p[i] = new int (x[i]); + for (int i = 0; i < N; i++) + if (*p[i] != x[i]) + return false; + for (int i = 0; i < N; ++i) + delete p[i]; + delete[] p; + return true; +} + +constexpr bool a = foo ("foobar"); +static_assert (a); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C new file mode 100644 index 00000000000..3380df74468 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C @@ -0,0 +1,73 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +constexpr int * +f1 () +{ + return new int (2); // { dg-error "is not a constant expression because it refers to a result of" } +} + +constexpr auto v1 = f1 (); + +constexpr bool +f2 () +{ + int *p = new int (3); // { dg-error "is not a constant expression because allocated storage has not been deallocated" } + return false; +} + +constexpr auto v2 = f2 (); + +constexpr bool +f3 () +{ + int *p = new int (3); + int *q = p; + delete p; + delete q; // { dg-error "deallocation of already deallocated storage" } + return false; +} + +constexpr auto v3 = f3 (); // { dg-message "in 'constexpr' expansion of" } + +constexpr bool +f4 (int *p) +{ + delete p; // { dg-error "deallocation of storage that was not previously allocated" } + return false; +} + +int q; +constexpr auto v4 = f4 (&q); // { dg-message "in 'constexpr' expansion of" } + +constexpr bool +f5 () +{ + int *p = new int; // { dg-message "allocated here" } + return *p == 1; +} + +constexpr auto v5 = f5 (); // { dg-error "the content of uninitialized storage is not usable in a constant expression" } + // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } + +constexpr bool +f6 () +{ + int *p = new int (2); // { dg-message "allocated here" } + int *q = p; + delete p; + return *q == 2; +} + +constexpr auto v6 = f6 (); // { dg-error "use of allocated storage after deallocation in a constant expression" } + // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } + +constexpr int * +f7 () +{ + int *p = new int (2); // { dg-error "is not a constant expression because it refers to a result of" } + delete p; + return p; +} + +constexpr auto v7 = f7 (); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C new file mode 100644 index 00000000000..6cac9835463 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C @@ -0,0 +1,29 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +struct S +{ + constexpr S () : s (0) { s++; } + constexpr S (int x) : s (x) { s += 2; } + constexpr ~S () { if (s != 35) asm (""); s = 5; } + int s; +}; + +constexpr bool +foo () +{ + S *p = new S (7); + if (p->s != 9) return false; + p->s = 35; + delete p; + p = new S[3] { 11, 13, 15 }; + if (p[0].s != 13 || p[1].s != 15 || p[2].s != 17) return false; + p[0].s = 35; + p[2].s = 35; + p[1].s = 35; + delete[] p; + return true; +} + +constexpr bool a = foo (); +static_assert (a); diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C index 086109adbf2..2b9cfd0effc 100644 --- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C +++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C @@ -430,16 +430,34 @@ // C++20 features -#if __cpp_conditional_explicit != 201806 -# error "__cpp_conditional_explicit != 201806" +#ifndef __cpp_conditional_explicit +# error "__cpp_conditional_explicit" +#elif __cpp_conditional_explicit != 201806 +# error "__cpp_conditional_explicit != 201806" #endif -#if __cpp_nontype_template_parameter_class != 201806 -# error "__cpp_nontype_template_parameter_class != 201806" +#ifndef __cpp_nontype_template_parameter_class +# error "__cpp_nontype_template_parameter_class" +#elif __cpp_nontype_template_parameter_class != 201806 +# error "__cpp_nontype_template_parameter_class != 201806" #endif -#if __cpp_impl_destroying_delete != 201806 -# error "__cpp_impl_destroying_delete != 201806" +#ifndef __cpp_impl_destroying_delete +# error "__cpp_impl_destroying_delete" +#elif __cpp_impl_destroying_delete != 201806 +# error "__cpp_impl_destroying_delete != 201806" +#endif + +#ifndef __cpp_constinit +# error "__cpp_constinit" +#elif __cpp_constinit != 201907 +# error "__cpp_constinit != 201907" +#endif + +#ifndef __cpp_constexpr_dynamic_alloc +# error "__cpp_constexpr_dynamic_alloc" +#elif __cpp_constexpr_dynamic_alloc != 201907 +# error "__cpp_constexpr_dynamic_alloc != 201907" #endif #ifdef __has_cpp_attribute @@ -484,8 +502,6 @@ # error "__has_cpp_attribute" #endif -// C++2A features: - #ifndef __cpp_char8_t # error "__cpp_char8_t" #elif __cpp_char8_t != 201811 diff --git a/gcc/testsuite/g++.dg/ext/is_literal_type3.C b/gcc/testsuite/g++.dg/ext/is_literal_type3.C new file mode 100644 index 00000000000..22d8494a2b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/is_literal_type3.C @@ -0,0 +1,26 @@ +// { dg-do compile { target c++11 } } + +struct S { + constexpr S () : n{} { } + ~S () { n = 1; } + int n; +}; + +static_assert(!__is_literal_type(S), ""); + +#ifdef __cpp_constexpr_dynamic_alloc +struct T { + constexpr T () : n{} { } + constexpr ~T () { n = 1; } + int n; +}; + +static_assert(__is_literal_type(T), ""); + +struct U : public T { + constexpr U () : u{} { } + int u; +}; + +static_assert(__is_literal_type(U), ""); +#endif