gcc/cp/ChangeLog:
PR c++/97427
* constexpr.c (cxx_set_object_constness): New function.
(cxx_eval_call_expression): Set new_obj for destructors too.
Call cxx_set_object_constness to set/unset TREE_READONLY of
the object under construction/destruction.
gcc/testsuite/ChangeLog:
PR c++/97427
* g++.dg/cpp2a/constexpr-dtor10.C: New test.
---
gcc/cp/constexpr.c | 49 +++++++++++++------
gcc/testsuite/g++.dg/cpp2a/constexpr-dtor10.C | 16 ++++++
2 files changed, 49 insertions(+), 16 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-dtor10.C
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 625410327b8..ef37b3043a5 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2187,6 +2187,27 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t,
tree thunk_fndecl,
non_constant_p, overflow_p);
}
+/* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set
+ its TREE_READONLY flag according to READONLY_P. Used for constexpr
+ 'tors to detect modifying const objects in a constexpr context. */
+
+static void
+cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
+ bool readonly_p, bool *non_constant_p,
+ bool *overflow_p)
+{
+ if (CLASS_TYPE_P (TREE_TYPE (object))
+ && CP_TYPE_CONST_P (TREE_TYPE (object)))
+ {
+ /* 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, object, /*lval*/false,
+ non_constant_p, overflow_p);
+ if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
+ TREE_READONLY (e) = readonly_p;
+ }
+}
+
/* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */
@@ -2515,11 +2536,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx,
tree t,
depth_ok = push_cx_call_context (t);
- /* Remember the object we are constructing. */
+ /* Remember the object we are constructing or destructing. */
tree new_obj = NULL_TREE;
- if (DECL_CONSTRUCTOR_P (fun))
+ if (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun))
{
- /* In a constructor, it should be the first `this' argument.
+ /* In a cdtor, it should be the first `this' argument.
At this point it has already been evaluated in the call
to cxx_bind_parameters_in_call. */
new_obj = TREE_VEC_ELT (new_call.bindings, 0);
@@ -2656,6 +2677,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree
t,
unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
+ /* If this is a constexpr destructor, the object's const and volatile
+ semantics are no longer in effect; see [class.dtor]p5. */
+ if (new_obj && DECL_DESTRUCTOR_P (fun))
+ cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false,
+ non_constant_p, overflow_p);
+
tree jump_target = NULL_TREE;
cxx_eval_constant_expression (&ctx_with_save_exprs, body,
lval, non_constant_p, overflow_p,
@@ -2686,19 +2713,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree
t,
the object is no longer under construction, and its possible
'const' semantics now apply. Make a note of this fact by
marking the CONSTRUCTOR TREE_READONLY. */
- if (new_obj
- && CLASS_TYPE_P (TREE_TYPE (new_obj))
- && CP_TYPE_CONST_P (TREE_TYPE (new_obj)))
- {
- /* 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,
- overflow_p);
- if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
- TREE_READONLY (e) = true;
- }
+ if (new_obj && DECL_CONSTRUCTOR_P (fun))
+ cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true,
+ non_constant_p, overflow_p);
/* Forget the saved values of the callee's SAVE_EXPRs and
TARGET_EXPRs. */
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor10.C
b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor10.C
new file mode 100644
index 00000000000..1551746b42b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor10.C
@@ -0,0 +1,16 @@
+// PR c++/97427
+// { dg-do compile { target c++20 } }
+
+struct Foo {
+ int n = 1;
+ constexpr ~Foo() {
+ n = 0;
+ }
+};
+
+constexpr bool foo() {
+ const Foo b;
+ return true;
+}
+
+static_assert(foo());
base-commit: d3f293348768667c07770e433ff00af51fee73a2