aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog10
-rw-r--r--gcc/cp/constexpr.c38
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-new.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C15
5 files changed, 69 insertions, 3 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index abcbba5..77c7b10 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@
+2020-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ 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 <jason@redhat.com>
PR c++/92552 - ICE with inherited constrained default ctor.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index c8852bd..417af18 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1041,8 +1041,11 @@ struct constexpr_global_ctx {
auto_vec<tree, 16> heap_vars;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *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<tree, 10> 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 3e778e9..517c851 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2020-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ 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 <richard.sandiford@arm.com>
* 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 90cb425..6316ff2 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 0000000..552d3c1
--- /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 ());