From: Jakub Jelinek Date: Tue, 7 Jan 2020 07:13:50 +0000 (+0100) Subject: re PR c++/91369 (Implement P0784R7: constexpr new) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f74f6092ace420bd8a4498697754b06fc6da63bd;p=gcc.git re PR c++/91369 (Implement P0784R7: constexpr new) PR c++/91369 * constexpr.c (struct constexpr_global_ctx): Add heap_alloc_count member, initialize it to zero in ctor. (cxx_eval_call_expression): Bump heap_dealloc_count when deleting a heap object. Don't cache calls to functions which allocate some heap objects and don't deallocate them or deallocate some heap objects they didn't allocate. * g++.dg/cpp1y/constexpr-new.C: Expect an error explaining why static_assert failed for C++2a. * g++.dg/cpp2a/constexpr-new9.C: New test. From-SVN: r279943 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index abcbba559e3..77c7b107c88 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2020-01-07 Jakub Jelinek + + PR c++/91369 + * constexpr.c (struct constexpr_global_ctx): Add heap_alloc_count + member, initialize it to zero in ctor. + (cxx_eval_call_expression): Bump heap_dealloc_count when deleting + a heap object. Don't cache calls to functions which allocate some + heap objects and don't deallocate them or deallocate some heap + objects they didn't allocate. + 2020-01-06 Jason Merrill PR c++/92552 - ICE with inherited constrained default ctor. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index c8852bd9309..417af182a2a 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1041,8 +1041,11 @@ struct constexpr_global_ctx { auto_vec heap_vars; /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */ vec *cleanups; + /* Number of heap VAR_DECL deallocations. */ + unsigned heap_dealloc_count; /* Constructor. */ - constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL) {} + constexpr_global_ctx () + : constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0) {} }; /* The constexpr expansion context. CALL is the current function @@ -2056,6 +2059,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { DECL_NAME (var) = heap_deleted_identifier; ctx->global->values.remove (var); + ctx->global->heap_dealloc_count++; return void_node; } else if (DECL_NAME (var) == heap_deleted_identifier) @@ -2281,6 +2285,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } else { + bool cacheable = true; if (result && result != error_mark_node) /* OK */; else if (!DECL_SAVED_TREE (fun)) @@ -2346,6 +2351,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, auto_vec save_exprs; ctx_with_save_exprs.save_exprs = &save_exprs; ctx_with_save_exprs.call = &new_call; + unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); + unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; tree jump_target = NULL_TREE; cxx_eval_constant_expression (&ctx_with_save_exprs, body, @@ -2417,6 +2424,33 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, /* Make the unshared function copy we used available for re-use. */ save_fundef_copy (fun, copy); + + /* If the call allocated some heap object that hasn't been + deallocated during the call, or if it deallocated some heap + object it has not allocated, the call isn't really stateless + for the constexpr evaluation and should not be cached. + It is fine if the call allocates something and deallocates it + too. */ + if (entry + && (save_heap_alloc_count != ctx->global->heap_vars.length () + || (save_heap_dealloc_count + != ctx->global->heap_dealloc_count))) + { + tree heap_var; + unsigned int i; + if ((ctx->global->heap_vars.length () + - ctx->global->heap_dealloc_count) + != save_heap_alloc_count - save_heap_dealloc_count) + cacheable = false; + else + FOR_EACH_VEC_ELT_FROM (ctx->global->heap_vars, i, heap_var, + save_heap_alloc_count) + if (DECL_NAME (heap_var) != heap_deleted_identifier) + { + cacheable = false; + break; + } + } } if (result == error_mark_node) @@ -2426,7 +2460,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, else if (!result) result = void_node; if (entry) - entry->result = result; + entry->result = cacheable ? result : error_mark_node; } /* The result of a constexpr function must be completely initialized. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3e778e9181e..517c851869a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2020-01-07 Jakub Jelinek + + PR c++/91369 + * g++.dg/cpp1y/constexpr-new.C: Expect an error explaining why + static_assert failed for C++2a. + * g++.dg/cpp2a/constexpr-new9.C: New test. + 2020-01-06 Richard Sandiford * gcc.target/aarch64/sve/asrdiv_1.c: Remove trailing %s. diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C index 90cb42595c4..6316ff24bbc 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C @@ -5,7 +5,7 @@ constexpr int *f4(bool b) { return nullptr; } else { return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } } - } + } // { dg-error "is not a constant expression because allocated storage has not been deallocated" "" { target c++2a } .-1 } } static_assert(f4(true) == nullptr, ""); static_assert(f4(false) == nullptr, ""); // { dg-error "non-.constant. condition|" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C new file mode 100644 index 00000000000..552d3c1b1cf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C @@ -0,0 +1,15 @@ +// PR c++/91369 +// { dg-do compile { target c++2a } } + +struct S { + constexpr S (int *i) : i{i} {} + constexpr ~S () { delete[] i; } + int *i; +}; + +constexpr S foo (int x) { return { new int[x] () }; } +constexpr bool bar () { foo (1); return true; } +constexpr bool baz () { foo (1); return false; } + +static_assert (bar ()); +static_assert (!baz ());