diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 625410327b8a..ef37b3043a54 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 000000000000..1551746b42ba --- /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());