https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jakub at gcc dot gnu.org, | |jason at gcc dot gnu.org, | |redi at gcc dot gnu.org --- Comment #1 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Just playing around a little bit, on: struct S { constexpr S () : s (5) {}; int s; }; constexpr bool foo () { S *p = new S (); delete p; return true; } (so, trivial dtor rather than introducing constexpr dtors for now): --- gcc/cp/constexpr.c.jj 2019-08-27 12:26:39.335884288 +0200 +++ gcc/cp/constexpr.c 2019-08-30 18:49:59.673689644 +0200 @@ -1666,6 +1666,34 @@ 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) + { + if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun))) + { + const int nargs = call_expr_nargs (t); + tree sz = 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) + sz = arg; + } + gcc_assert (sz); + tree type = build_array_type_nelts (char_type_node, + tree_to_uhwi (sz)); + tree var = build_decl (loc, VAR_DECL, NULL_TREE, type); + DECL_ARTIFICIAL (var) = 1; + return fold_convert (ptr_type_node, build_address (var)); + } + else + /* FIXME */; + } if (!ctx->quiet) { if (!lambda_static_thunk_p (fun)) @@ -6243,7 +6271,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)) { if (flags & tf_error) { This fails because we represent the new expression as COMPOUND_EXPR which first calls the allocation function (handled by the above code) and then constructs the var (effectively through a reinterpret cast not marked as such), and fail on that: r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0, &empty_base); if (r == NULL_TREE) { /* We couldn't fold to a constant value. Make sure it's not something we should have been able to fold. */ tree sub = op0; STRIP_NOPS (sub); if (TREE_CODE (sub) == ADDR_EXPR) { gcc_assert (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t))); /* DR 1188 says we don't have to deal with this. */ if (!ctx->quiet) error ("accessing value of %qE through a %qT glvalue in a " "constant expression", build_fold_indirect_ref (sub), TREE_TYPE (t)); *non_constant_p = true; return t; } if (lval && op0 != orig_op0) return build1 (INDIRECT_REF, TREE_TYPE (t), op0); if (!lval) VERIFY_CONSTANT (t); return t; } So, first of all, is it a good idea to represent the HEAP variables through artifical VAR_DECLs? I guess in the outermost constexpr evaluation context we'd need to track which of those we have allocated and deallocated and do the checking that at the end of outermost constexpr evaluation no allocations are left around, and that we don't deallocate something that hasn't been allocated. As we don't have the actual dynamic type they will have at the end of new expression, should we mark them some way and either allow the first cxx_fold_indirect_ref or the above code to change their type the first time they are stored? Is placement new ok in constexpr contexts or not? Can the global replaceable allocation functions be marked constexpr by the user and if so, what should happen in that case, shall they be still treated as magic allocation functions, something else? For the constexpr dtors, we need to make sure to mark trivial dtors as constexpr, and default dtors as constexpr if they satisfy the rules, verify the restrictions for user defined constexpr dtors, and check for constexpr dtor in literal type check. Plus guess for constexpr variables we need to at some point verify they are constexpr destructible (but on a copy of the variable value, such that changes to the value during destruction don't modify the actual value of the variable we use normally).