From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org, Jonathan Wakely <jwakely@redhat.com>,
Marek Polacek <polacek@redhat.com>
Subject: Re: [C++ PATCH] PR c++/91369 - Implement P0784R7: constexpr new
Date: Tue, 01 Oct 2019 21:56:00 -0000 [thread overview]
Message-ID: <688ebe68-e83f-9559-a1c7-883758b2bd74@redhat.com> (raw)
In-Reply-To: <20190927203104.GY15914@tucnak>
On 9/27/19 4:31 PM, Jakub Jelinek wrote:
> Hi!
>
> The following patch attempts to implement P0784R7, which includes constexpr
> destructors and constexpr new/delete/new[]/delete[].
>
> ::operator new is allowed during constexpr evaluation and returns address of
> an artificial VAR_DECL with special name. At this point we don't really
> know the type of the heap storage, just size. Later on when we encounter
> cast to the corresponding pointer type, we change the name of the var and
> type to match the type from the new expression (for new[] we need to do
> further stuff as at the point where build_new_1 is called, we might not know
> the exact array size, but we shall know that during the constexpr
> evaluation, and cookie handling also complicates it a little bit).
> When we first store into such heap objects, a ctor is created for them on
> the fly. Finally, ::operator delete marks those heap VAR_DECLs as deleted
> and cxx_eval_outermost_constant_expr checks if everything that has been
> allocated has been also deallocated and verifies addresses of those heap
> vars aren't leaking into the return value.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2019-09-27 Jakub Jelinek <jakub@redhat.com>
>
> 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, 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_ctx): Add heap_vars field.
> (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.
> (initialized_type): Handle destructors for c++2a.
> (cxx_fold_indirect_ref): Also handle array fields in structures.
> (non_const_var_error): Add auto_diagnostic_group sentinel. Emit
> special diagnostics for heap variables.
> (cxx_eval_store_expression): Create ctor for heap variables on the
> first write. Formatting fix. Handle const_object_being_modified
> with array type.
> (cxx_eval_loop_expr): Initialize jump_target if NULL.
> (cxx_eval_constant_expression) <case CLEANUP_STMT>: If not skipping
> upon entry to body, run cleanup with the same *jump_target as it
> started to run the cleanup even if the body returns, breaks or
> continues.
> <case NOP_EXPR>: 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. Allow CLEANUP_STMT.
> * decl.c (initialize_predefined_identifiers): Add heap identifiers.
> (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_constant_dtor 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_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.
>
> --- gcc/c-family/c-cppbuiltin.c.jj 2019-09-26 21:34:21.188923996 +0200
> +++ gcc/c-family/c-cppbuiltin.c 2019-09-27 18:25:41.346059343 +0200
> @@ -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");
> --- gcc/cp/cp-tree.h.jj 2019-09-27 12:22:54.042880699 +0200
> +++ gcc/cp/cp-tree.h 2019-09-27 18:25:40.903065957 +0200
> @@ -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
> #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]
>
> @@ -6324,6 +6330,7 @@ extern bool vbase_has_user_provided_move
> 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);
> @@ -7729,6 +7736,7 @@ extern bool require_constant_expression
> 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);
> --- gcc/cp/class.c.jj 2019-09-26 21:34:21.434920308 +0200
> +++ gcc/cp/class.c 2019-09-27 18:25:40.904065942 +0200
> @@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_
> 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 (
> 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
> 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 %<constexpr%> destructor",
> + t);
> else if (CLASSTYPE_NON_AGGREGATE (t)
> && !TYPE_HAS_TRIVIAL_DFLT (t)
> && !LAMBDA_TYPE_P (t)
> --- gcc/cp/constexpr.c.jj 2019-09-27 20:33:37.600208356 +0200
> +++ gcc/cp/constexpr.c 2019-09-27 20:38:38.203710246 +0200
> @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
> #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) \
> @@ -1031,6 +1032,9 @@ struct constexpr_ctx {
> 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. */
> + vec<tree> *heap_vars;
>
> /* Whether we should error on a non-constant expression or fail quietly. */
> bool quiet;
> @@ -1666,6 +1670,58 @@ cxx_eval_call_expression (const constexp
> lval, non_constant_p, overflow_p);
> if (!DECL_DECLARED_CONSTEXPR_P (fun))
> {
> + if (cxx_dialect >= cxx2a
> + && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun))
> + && CP_DECL_CONTEXT (fun) == global_namespace)
> + {
> + 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->heap_vars->safe_push (var);
> + 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->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;
> + }
Don't we need an error for trying to deallocate something that wasn't
allocated within the constexpr evaluation?
> + }
> + }
> + }
> if (!ctx->quiet)
> {
> if (!lambda_static_thunk_p (fun))
> @@ -2998,8 +3054,8 @@ base_field_constructor_elt (vec<construc
> }
>
> /* Some of the expressions fed to the constexpr mechanism are calls to
> - constructors, which have type void. In that case, return the type being
> - initialized by the constructor. */
> + constructors or destructors, which have type void. In that case,
> + return the type being initialized by the constructor. */
>
> static tree
> initialized_type (tree t)
> @@ -3011,8 +3067,10 @@ initialized_type (tree t)
> {
> /* A constructor call has void type, so we need to look deeper. */
> tree fn = get_function_named_in_call (t);
> - if (fn && TREE_CODE (fn) == FUNCTION_DECL
> - && DECL_CXX_CONSTRUCTOR_P (fn))
> + if (fn
> + && TREE_CODE (fn) == FUNCTION_DECL
> + && (DECL_CXX_CONSTRUCTOR_P (fn)
> + || (cxx_dialect >= cxx2a && DECL_CXX_DESTRUCTOR_P (fn))))
> type = DECL_CONTEXT (fn);
Why is this needed? A destructor doesn't initialize anything, so
returning void seems appropriate.
> }
> else if (TREE_CODE (t) == COMPOUND_EXPR)
> @@ -3434,11 +3492,24 @@ cxx_fold_indirect_ref (location_t loc, t
> {
> tree field = TYPE_FIELDS (optype);
> for (; field; field = DECL_CHAIN (field))
> - if (TREE_CODE (field) == FIELD_DECL
> - && TREE_TYPE (field) != error_mark_node
> - && integer_zerop (byte_position (field))
> - && similar_type_p (TREE_TYPE (field), type))
> + if (TREE_CODE (field) != FIELD_DECL
> + || TREE_TYPE (field) == error_mark_node
> + || !integer_zerop (byte_position (field)))
> + continue;
> + else if (similar_type_p (TREE_TYPE (field), type))
> return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE);
> + else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
> + && similar_type_p (TREE_TYPE (TREE_TYPE (field)), type))
> + {
> + tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
> + tree min_val = size_zero_node;
> + if (type_domain && TYPE_MIN_VALUE (type_domain))
> + min_val = TYPE_MIN_VALUE (type_domain);
> + op = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
> + op, field, NULL_TREE);
> + return build4_loc (loc, ARRAY_REF, type, op, min_val,
> + NULL_TREE, NULL_TREE);
> + }
> }
> }
> else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
> @@ -3521,12 +3592,44 @@ cxx_fold_indirect_ref (location_t loc, t
> {
> tree field = TYPE_FIELDS (op00type);
> for (; field; field = DECL_CHAIN (field))
> - if (TREE_CODE (field) == FIELD_DECL
> - && TREE_TYPE (field) != error_mark_node
> - && tree_int_cst_equal (byte_position (field), op01)
> - && similar_type_p (TREE_TYPE (field), type))
> + if (TREE_CODE (field) != FIELD_DECL
> + || TREE_TYPE (field) == error_mark_node)
> + continue;
> + else if (tree_int_cst_equal (byte_position (field), op01)
> + && similar_type_p (TREE_TYPE (field), type))
> return fold_build3 (COMPONENT_REF, type, op00,
> field, NULL_TREE);
> + else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
> + && tree_int_cst_le (byte_position (field), op01)
> + && similar_type_p (TREE_TYPE (TREE_TYPE (field)),
> + type))
> + {
> + tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
> + tree min_val = size_zero_node;
> + tree max_val = NULL_TREE;
> + if (type_domain && TYPE_MIN_VALUE (type_domain))
> + min_val = TYPE_MIN_VALUE (type_domain);
> + if (type_domain && TYPE_MAX_VALUE (type_domain))
> + max_val = TYPE_MAX_VALUE (type_domain);
> + offset_int off = wi::to_offset (op01);
> + off -= wi::to_offset (byte_position (field));
> + offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (type));
> + offset_int remainder;
> + off = wi::divmod_trunc (off, el_sz, SIGNED, &remainder);
> + if (remainder == 0
> + && TREE_CODE (min_val) == INTEGER_CST
> + && (max_val == NULL_TREE
> + || (TREE_CODE (max_val) == INTEGER_CST
> + && off <= wi::to_offset (max_val))))
> + {
> + off = off + wi::to_offset (min_val);
> + op00 = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
> + op00, field, NULL_TREE);
> + op01 = wide_int_to_tree (sizetype, off);
> + return build4_loc (loc, ARRAY_REF, type, op00, op01,
> + NULL_TREE, NULL_TREE);
> + }
> + }
I think we want to factor this function more, so we don't have the same
code in multiple places for handling an array, and an array member, and
a pointer to array. Do you want to take a look at bug 71504 while
you're touching this code?
> @@ -3645,7 +3748,23 @@ cxx_eval_indirect_ref (const constexpr_c
> 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. */
> @@ -3892,6 +4011,15 @@ cxx_eval_store_expression (const constex
> valp = ctx->values->get (object);
> else
> valp = NULL;
> + if (!valp
> + && VAR_P (object)
> + && DECL_NAME (object) == heap_identifier)
> + {
> + tree ctor = build_constructor (type, NULL);
> + CONSTRUCTOR_NO_CLEARING (ctor) = true;
> + ctx->values->put (object, ctor);
> + valp = ctx->values->get (object);
> + }
Instead of this, how about giving the object NULL_TREE value when we
create it in cxx_eval_call_expression?
> if (!valp)
> {
> /* A constant-expression cannot modify objects from outside the
> @@ -3905,7 +4033,7 @@ cxx_eval_store_expression (const constex
> bool no_zero_init = true;
>
> releasing_vec ctors;
> - while (!refs->is_empty())
> + while (!refs->is_empty ())
> {
> if (*valp == NULL_TREE)
> {
> @@ -4046,7 +4174,9 @@ cxx_eval_store_expression (const constex
> if (const_object_being_modified)
> {
> bool fail = false;
> - if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
> + tree const_objtype
> + = strip_array_types (TREE_TYPE (const_object_being_modified));
> + if (!CLASS_TYPE_P (const_objtype))
This looks like an unrelated bugfix; you might commit it (and the
others) separately if that's convenient.
> fail = true;
> else
> {
> @@ -4365,6 +4495,12 @@ cxx_eval_loop_expr (const constexpr_ctx
> 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;
> @@ -4907,14 +5043,21 @@ cxx_eval_constant_expression (const cons
> break;
>
> case CLEANUP_STMT:
> - r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> + {
> + tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
> + r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> + non_constant_p, overflow_p,
> + jump_target);
> + if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
> + /* Also evaluate the cleanup. If we weren't skipping at the
> + start of the CLEANUP_BODY, change jump_target temporarily
> + to &initial_jump_target, so that even a return or break or
> + continue in the body doesn't skip the cleanup. */
This also looks like an unrelated bugfix.
> + cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
> non_constant_p, overflow_p,
> - jump_target);
> - if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
> - /* Also evaluate the cleanup. */
> - cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
> - non_constant_p, overflow_p,
> - jump_target);
> + jump_target ? &initial_jump_target
> + : NULL);
> + }
> break;
>
> /* These differ from cxx_eval_unary_expression in that this doesn't
> @@ -5203,8 +5346,7 @@ cxx_eval_constant_expression (const cons
> 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)
> @@ -5258,6 +5400,81 @@ cxx_eval_constant_expression (const cons
> }
> }
>
> + 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_type = TREE_TYPE (type);
> + tree cookie_type = NULL_TREE;
> + bool array_p = false;
> + HOST_WIDE_INT cookie_size = 0;
> + if (TREE_CODE (var_type) == ARRAY_TYPE
> + && TYPE_DOMAIN (var_type) == NULL_TREE)
> + {
> + var_type = TREE_TYPE (var_type);
> + array_p = true;
> + }
> + else if (TREE_CODE (var_type) == RECORD_TYPE
> + && TYPE_NAME (var_type) == NULL_TREE)
> + if (tree fld1 = TYPE_FIELDS (var_type))
> + if (TREE_CODE (fld1) == FIELD_DECL
> + && DECL_NAME (fld1) == NULL_TREE
> + && DECL_ARTIFICIAL (fld1)
> + && TREE_CODE (TREE_TYPE (fld1)) == ARRAY_TYPE
> + && COMPLETE_TYPE_P (TREE_TYPE (fld1)))
> + if (tree fld2 = DECL_CHAIN (fld1))
> + if (TREE_CODE (fld2) == FIELD_DECL
> + && DECL_NAME (fld2) == NULL_TREE
> + && DECL_ARTIFICIAL (fld2)
> + && TREE_CODE (TREE_TYPE (fld2)) == ARRAY_TYPE
> + && TYPE_DOMAIN (TREE_TYPE (fld2)) == NULL_TREE
> + && DECL_CHAIN (fld2) == NULL_TREE)
Maybe give the struct a magic name so you don't need to do as much
checking of the FIELD_DECLs?
> + {
> + var_type = TREE_TYPE (TREE_TYPE (fld2));
> + array_p = true;
> + cookie_type = TREE_TYPE (fld1);
> + cookie_size = int_size_in_bytes (TREE_TYPE (fld1));
> + }
> + HOST_WIDE_INT sz1 = int_size_in_bytes (var_type);
> + HOST_WIDE_INT sz2 = int_size_in_bytes (TREE_TYPE (var));
> + if (sz1 <= sz2 && cookie_size <= sz2)
> + {
> + DECL_NAME (var) = heap_identifier;
> + if (array_p && sz1 > 0)
> + {
> + sz2 -= cookie_size;
> + sz2 /= sz1;
> + tree idx_type = build_index_type (size_int (sz2 - 1));
> + var_type = build_cplus_array_type (var_type, idx_type);
> + if (cookie_type)
> + {
> + location_t loc = cp_expr_loc_or_input_loc (t);
> + tree vtype = cxx_make_type (RECORD_TYPE);
> + tree fld1 = build_decl (loc, FIELD_DECL, NULL_TREE,
> + cookie_type);
> + tree fld2 = build_decl (loc, FIELD_DECL, NULL_TREE,
> + var_type);
> + DECL_FIELD_CONTEXT (fld1) = vtype;
> + DECL_FIELD_CONTEXT (fld2) = vtype;
> + DECL_ARTIFICIAL (fld1) = true;
> + DECL_ARTIFICIAL (fld2) = true;
> + TYPE_FIELDS (vtype) = fld1;
> + DECL_CHAIN (fld1) = fld2;
> + layout_type (vtype);
> + var_type = vtype;
So here you're completing the type of the array member of the struct.
> + TREE_TYPE (var) = var_type;
> + TREE_TYPE (TREE_OPERAND (op, 0))
> + = build_pointer_type (var_type);
> + }
> + }
Let's factor out all of this code, too.
> +
> if (op == oldop && tcode != UNARY_PLUS_EXPR)
> /* We didn't fold at the top so we could check for ptr-int
> conversion. */
> @@ -5499,6 +5716,7 @@ instantiate_cx_fn_r (tree *tp, int *walk
>
> return NULL_TREE;
> }
> +
> static void
> instantiate_constexpr_fns (tree t)
> {
> @@ -5507,17 +5725,36 @@ 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);
> @@ -5525,16 +5762,23 @@ cxx_eval_outermost_constant_expr (tree t
> bool non_constant_p = false;
> bool overflow_p = false;
> hash_map<tree,tree> map;
> + auto_vec<tree, 16> heap_vars;
> HOST_WIDE_INT constexpr_ctx_count = 0;
>
> constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
> - &constexpr_ctx_count, allow_non_constant, strict,
> - manifestly_const_eval || !allow_non_constant };
> + &constexpr_ctx_count, &heap_vars, allow_non_constant,
> + strict, manifestly_const_eval || !allow_non_constant };
As we add more stuff to constexpr_ctx, creating new ones on the stack
becomes more and more expensive. We should really split off the parts
that change frequently: Maybe just ctor/object, maybe also
call/save_exprs/...?
> tree type = initialized_type (t);
> tree r = t;
> if (VOID_TYPE_P (type))
> - return t;
> + {
> + if (TREE_CODE (t) == BIND_EXPR && 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,
> @@ -5544,8 +5788,22 @@ cxx_eval_outermost_constant_expr (tree t
> 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)
> @@ -5569,13 +5827,15 @@ cxx_eval_outermost_constant_expr (tree t
> 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 "
> @@ -5583,8 +5843,7 @@ cxx_eval_outermost_constant_expr (tree t
> 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 "
> @@ -5593,6 +5852,32 @@ cxx_eval_outermost_constant_expr (tree t
> non_constant_p = true;
> }
>
> + if (!heap_vars.is_empty ())
> + {
> + tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
> + NULL);
Doesn't verify_constant already complain about remaining references to
allocated objects?
> + 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 %<operator new%>", t);
> + r = t;
> + non_constant_p = true;
> + }
> + FOR_EACH_VEC_ELT (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. */
> @@ -5618,6 +5903,8 @@ cxx_eval_outermost_constant_expr (tree t
>
> 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
> @@ -5625,7 +5912,7 @@ cxx_eval_outermost_constant_expr (tree t
> 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
> @@ -5653,7 +5940,7 @@ cxx_eval_outermost_constant_expr (tree t
> 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;
> }
> }
> @@ -5668,7 +5955,16 @@ cxx_eval_outermost_constant_expr (tree t
> 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
> @@ -5772,14 +6068,14 @@ maybe_constant_value (tree t, tree decl,
> 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<tree, tree>::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
> @@ -5841,7 +6137,7 @@ fold_non_dependent_expr_template (tree t
>
> 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)
> @@ -5945,7 +6241,7 @@ maybe_constant_init_1 (tree t, tree decl
> 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);
> @@ -6239,7 +6535,12 @@ potential_constant_expression_1 (tree t,
> 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_dialect < cxx2a
> + || !IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun))
> + || CP_DECL_CONTEXT (fun) != global_namespace))
This is the second occurrence of this three-line test for a constexpr
(de)allocation function, let's factor it out.
> {
> if (flags & tf_error)
> {
> @@ -6468,6 +6769,9 @@ potential_constant_expression_1 (tree t,
> 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;
> @@ -6937,7 +7241,7 @@ potential_constant_expression_1 (tree t,
> 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, "%<delete[]%> is not a constant expression");
> @@ -6983,6 +7287,12 @@ potential_constant_expression_1 (tree t,
> return true;
>
> case CLEANUP_STMT:
> + if (!RECUR (CLEANUP_BODY (t), any))
> + return false;
> + if (!CLEANUP_EH_ONLY (t) && !RECUR (CLEANUP_EXPR (t), any))
> + return false;
> + return true;
> +
> case EMPTY_CLASS_EXPR:
> case PREDICT_EXPR:
> return false;
> --- gcc/cp/decl.c.jj 2019-09-26 21:34:21.673916726 +0200
> +++ gcc/cp/decl.c 2019-09-27 18:25:40.898066032 +0200
> @@ -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}
> };
>
> @@ -7430,7 +7433,11 @@ cp_finish_decl (tree decl, tree init, bo
> TREE_READONLY (decl) = 1;
>
> /* Likewise if it needs destruction. */
> - if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
> + if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
> + && (cxx_dialect < cxx2a
> + || !DECL_DECLARED_CONSTEXPR_P (decl)
> + || !type_has_constexpr_destructor
> + (strip_array_types (type))))
This could use a decl_maybe_constant_destruction predicate.
> TREE_READONLY (decl) = 0;
> }
>
> @@ -8319,6 +8326,27 @@ register_dtor_fn (tree decl)
> if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
> return void_node;
>
> + if (cxx_dialect >= cxx2a
> + && DECL_DECLARED_CONSTEXPR_P (decl)
> + && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
> + && type_has_constexpr_destructor (strip_array_types (type)))
...which we'd use again here.
> + {
> + int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR;
> + tree addr, call;
> +
> + if (TREE_CODE (type) == ARRAY_TYPE)
> + addr = decl;
> + else
> + addr = build_address (decl);
> +
> + call = build_delete (TREE_TYPE (addr), addr,
> + sfk_complete_destructor, flags, 0,
> + tf_warning_or_error);
> + if (call != error_mark_node)
> + cxx_constant_dtor (call, decl);
> + return void_node;
Why not use the result of build_cleanup?
> + }
> +
> /* 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
> @@ -8432,11 +8460,15 @@ register_dtor_fn (tree decl)
> static void
> expand_static_init (tree decl, tree init)
> {
> + tree type = TREE_TYPE (decl);
> gcc_assert (VAR_P (decl));
> gcc_assert (TREE_STATIC (decl));
>
> /* Some variables require no dynamic initialization. */
> - if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
> + if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))
> + || (cxx_dialect >= cxx2a
> + && DECL_DECLARED_CONSTEXPR_P (decl)
> + && type_has_constexpr_destructor (strip_array_types (type))))
> {
> /* Make sure the destructor is callable. */
> cxx_maybe_build_cleanup (decl, tf_warning_or_error);
> @@ -12702,12 +12734,13 @@ grokdeclarator (const cp_declarator *dec
> "a destructor cannot be %<concept%>");
> return error_mark_node;
> }
> - if (constexpr_p)
> - {
> - error_at (declspecs->locations[ds_constexpr],
> - "a destructor cannot be %<constexpr%>");
> - return error_mark_node;
> - }
> + if (constexpr_p && cxx_dialect < cxx2a)
> + {
> + error_at (declspecs->locations[ds_constexpr],
> + "%<constexpr%> destructors only available"
> + " with %<-std=c++2a%> or %<-std=gnu++2a%>");
> + return error_mark_node;
> + }
> }
> else if (sfk == sfk_constructor && friendp && !ctype)
> {
> @@ -12744,10 +12777,11 @@ grokdeclarator (const cp_declarator *dec
> }
>
> /* 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
> @@ -16752,6 +16786,12 @@ cxx_maybe_build_cleanup (tree decl, tsub
> cleanup = error_mark_node;
> else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
> /* Discard the call. */;
> + else if (cxx_dialect >= cxx2a
> + && VAR_P (decl)
> + && DECL_DECLARED_CONSTEXPR_P (decl)
> + && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
> + && type_has_constexpr_destructor (strip_array_types (type)))
> + cxx_constant_dtor (call, decl);
> else if (cleanup)
> cleanup = cp_build_compound_expr (cleanup, call, complain);
> else
> --- gcc/cp/init.c.jj 2019-09-26 21:34:21.598917851 +0200
> +++ gcc/cp/init.c 2019-09-27 19:15:56.473042238 +0200
> @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.
> #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);
> @@ -3332,6 +3333,48 @@ build_new_1 (vec<tree, va_gc> **placemen
> }
> }
>
> + tree alloc_call_call = extract_call_expr (alloc_call);
> + tree alloc_call_fndecl = NULL_TREE;
> + if (alloc_call_call != error_mark_node)
> + alloc_call_fndecl = cp_get_callee_fndecl_nofold (alloc_call_call);
> + if (array_p
> + && cxx_dialect >= cxx2a
> + && (current_function_decl == NULL_TREE
> + || DECL_DECLARED_CONSTEXPR_P (current_function_decl))
> + && alloc_call_fndecl
> + && IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl))
> + && CP_DECL_CONTEXT (alloc_call_fndecl) == global_namespace)
> + {
> + /* Help the constexpr code to find the right type for the heap variable
> + by adding a NOP_EXPR around alloc_call.
> + If not using cookies, use array type, otherwise structure with
> + two array types. */
i.e.
struct {
size_t cookie[N];
elt array[];
};
I guess you want an array for the cookie rather than a non-array size_t
data member to handle ARM cookies that also include the element size.
Please include this in the comment.
> + tree atype = build_cplus_array_type (elt_type, NULL_TREE);
> + if (cookie_size)
> + {
> + gcc_assert (tree_fits_uhwi_p (cookie_size));
> + unsigned HOST_WIDE_INT sz = tree_to_uhwi (cookie_size);
> + sz /= int_size_in_bytes (sizetype);
> + tree atype2 = build_index_type (size_int (sz - 1));
> + atype2 = build_cplus_array_type (sizetype, atype2);
> + tree atype3 = cxx_make_type (RECORD_TYPE);
> + tree fld1 = build_decl (input_location, FIELD_DECL, NULL_TREE,
> + atype2);
> + tree fld2 = build_decl (input_location, FIELD_DECL, NULL_TREE,
> + atype);
> + DECL_FIELD_CONTEXT (fld1) = atype3;
> + DECL_FIELD_CONTEXT (fld2) = atype3;
> + DECL_ARTIFICIAL (fld1) = true;
> + DECL_ARTIFICIAL (fld2) = true;
> + TYPE_FIELDS (atype3) = fld1;
> + DECL_CHAIN (fld1) = fld2;
> + layout_type (atype3);
> + atype = atype3;
> + }
> + pointer_type = build_pointer_type (atype);
> + alloc_call = build_nop (pointer_type, alloc_call);
> + }
All of this could be factored out into a function called something like
maybe_wrap_new_for_constexpr.
> +
> /* In the simple case, we can stop now. */
> pointer_type = build_pointer_type (type);
> if (!cookie_size && !is_initialized)
> @@ -3905,17 +3948,11 @@ build_vec_delete_1 (tree base, tree maxi
> 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,
> --- gcc/cp/method.c.jj 2019-09-26 21:34:21.331921851 +0200
> +++ gcc/cp/method.c 2019-09-27 18:25:40.902065972 +0200
> @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.
> #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,
> 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,
> if (diag)
> {
> inform (DECL_SOURCE_LOCATION (fn),
> - "defaulted constructor calls non-%<constexpr%> %qD", fn);
> + SFK_DTOR_P (sfk)
> + ? G_("destructor calls non-%<constexpr%> %qD")
Not "defaulted"?
> + : G_("defaulted constructor calls non-%<constexpr%> %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_
> {
> 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_
>
> 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
> && 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, spe
> 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, spe
> 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_
> 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))
> --- gcc/cp/typeck2.c.jj 2019-09-26 21:34:26.299847376 +0200
> +++ gcc/cp/typeck2.c 2019-09-27 18:25:40.899066017 +0200
> @@ -902,7 +902,13 @@ store_init_value (tree decl, tree init,
> 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);
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C.jj 2016-03-05 07:46:49.554128302 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C 2019-09-27 20:26:06.317971681 +0200
> @@ -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 }
> --- gcc/testsuite/g++.dg/cpp0x/locations1.C.jj 2019-09-26 21:34:22.205908750 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/locations1.C 2019-09-27 18:25:41.346059343 +0200
> @@ -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" }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-new.C.jj 2019-09-26 21:34:22.281907610 +0200
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-new.C 2019-09-27 18:25:41.303059984 +0200
> @@ -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, "");
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C.jj 2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C 2019-09-27 18:25:41.320059731 +0200
> @@ -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;
> +};
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C.jj 2019-09-27 18:25:41.303059984 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C 2019-09-27 18:25:41.303059984 +0200
> @@ -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 ());
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj 2019-09-27 18:25:41.303059984 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C 2019-09-27 18:25:41.303059984 +0200
> @@ -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 "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;
> +}
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C.jj 2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C 2019-09-27 20:06:50.047298452 +0200
> @@ -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);
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C.jj 2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C 2019-09-27 20:23:16.107524585 +0200
> @@ -0,0 +1,21 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +template <int N>
> +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);
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C.jj 2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C 2019-09-27 18:25:41.320059731 +0200
> @@ -0,0 +1,63 @@
> +// 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 'operator new'" }
> +}
> +
> +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 "call to non-'constexpr' function" }
> + 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 }
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C.jj 2019-09-27 18:25:41.319059745 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C 2019-09-27 20:07:33.928641928 +0200
> @@ -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);
> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj 2019-09-26 21:34:22.341906710 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C 2019-09-27 18:25:41.319059745 +0200
> @@ -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
> --- gcc/testsuite/g++.dg/ext/is_literal_type3.C.jj 2019-09-27 18:25:40.904065942 +0200
> +++ gcc/testsuite/g++.dg/ext/is_literal_type3.C 2019-09-27 18:25:40.904065942 +0200
> @@ -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
>
> Jakub
>
next prev parent reply other threads:[~2019-10-01 21:56 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-27 20:31 Jakub Jelinek
2019-10-01 21:56 ` Jason Merrill [this message]
2019-10-02 11:36 ` Jakub Jelinek
2019-10-02 15:22 ` Jason Merrill
2019-10-03 18:25 ` [C++ PATCH] Improve cxx_fold_indirect_ref (PR c++/71504) Jakub Jelinek
2019-10-03 19:57 ` Jason Merrill
2019-10-03 20:57 ` [C++ PATCH] Improve cxx_fold_indirect_ref (PR c++/71504, take 2) Jakub Jelinek
2019-10-04 4:44 ` Jason Merrill
2019-10-03 18:38 ` [C++ PATCH] PR c++/91369 - Implement P0784R7: constexpr new Jakub Jelinek
2019-10-03 20:07 ` Jason Merrill
2019-10-04 17:50 ` Jakub Jelinek
2019-10-04 19:34 ` Jason Merrill
2019-10-05 7:39 ` Jakub Jelinek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=688ebe68-e83f-9559-a1c7-883758b2bd74@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=jwakely@redhat.com \
--cc=polacek@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).