From d176b48f7d416fc9b88b236141cd37fcde679333 Mon Sep 17 00:00:00 2001 From: Daveed Vandevoorde Date: Tue, 27 Oct 2026 19:13:57 +0000 Subject: [PATCH] Destroy array elements in decreasing order in constant evaluation [GH #400] During constant evaluation, the elements of arrays (complete objects, array members, and arrays destroyed by a delete[] expression) are now destroyed in decreasing order of their index, instead of in increasing order. --- src/interpret.c | 22 +++--- .../gh-400-array-destruction-order.txt | 23 +++++++ .../tests/changes/.gh-400.rto/default.1.1.txt | 1 + tests/tests/changes/gh-400.sft.cpp | 68 +++++++++++++++++++ 4 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 src/pending_changes/gh-400-array-destruction-order.txt create mode 100644 tests/tests/changes/.gh-400.rto/default.1.1.txt create mode 100644 tests/tests/changes/gh-400.sft.cpp diff --git a/src/interpret.c b/src/interpret.c index b06b001b3c..079d7c59ea 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -6005,9 +6005,11 @@ Perform the destructions for the current storage stack. check_assertion(result); nonvirtual = TRUE; } /* if */ - for (int k = 0; k 0; --k) { + if (!do_constexpr_dtor(ips, dtor, pos, + sub_obj + (a_byte_count)(k-1)*size, + complete_obj, nonvirtual)) { result = FALSE; goto done; } /* if */ @@ -24147,8 +24149,9 @@ the iwp_1st_resume visit destroys the subobjects and undoes that mapping. get_mapped_byte_count(&persistent_map, bcp, offset); sub_dip = dtor_init->initializer; } /* if */ - sub_bytes = result_storage+offset; - for (int k = 0; k 0; --k) { + sub_bytes = result_storage+offset+(a_byte_count)(k-1)*elem_size; /* Clear the derivation/active-field state. */ *(void**)sub_bytes = NULL; if (!do_constexpr_dtor(ips, sub_dip->destructor, pos, sub_bytes, @@ -24158,7 +24161,6 @@ the iwp_1st_resume visit destroys the subobjects and undoes that mapping. } else { mark_subobject_uninitialized(sub_bytes, complete_object); } /* if */ - sub_bytes += elem_size; } /* for */ } /* for */ if (!mark_whole_subobject_uninitialized(ips, result_storage, class_type, @@ -26655,14 +26657,16 @@ Evaluate the given delete-expression. } else { /* Perform necessary destructions and mark subobject uninitialized. */ a_dynamic_init_ptr dip = ndsp->dynamic_init; - a_byte_count k = 0, length = allocation->length, elem_size; + a_byte_count k, length = allocation->length, elem_size; a_byte *arr = (a_byte*)allocation+allocation->prefix_size, - *elem = arr; + *elem; a_type_ptr elem_type = skip_typerefs(allocation->elem_type); elem_size = value_bytes_for_type(ips, elem_type, &result); if (!result) goto done; unmark_complete_object_initialized(arr); - for (; k 0; --k) { + elem = arr + (k-1)*elem_size; if (dip != NULL) { /* Run the destructor. For the non-array case, this might be a virtual call: Make sure we start from the original address since diff --git a/src/pending_changes/gh-400-array-destruction-order.txt b/src/pending_changes/gh-400-array-destruction-order.txt new file mode 100644 index 0000000000..7445aef19b --- /dev/null +++ b/src/pending_changes/gh-400-array-destruction-order.txt @@ -0,0 +1,23 @@ +[GH #400] +Order of destruction of array elements in constant evaluation + +During constant evaluation, the elements of an array (including an array +member and an array destroyed by a delete[] expression) were destroyed in +increasing order of their index instead of in decreasing order. For +example, with --c++20: + + struct S { + int* log; + int id; + constexpr ~S() { *log = *log * 10 + id; } + }; + constexpr int f() { + int log = 0; + { + S a[3] = {{&log, 1}, {&log, 2}, {&log, 3}}; + } + return log; + } + static_assert(f() == 321); // Previously failed. + +This is now fixed. diff --git a/tests/tests/changes/.gh-400.rto/default.1.1.txt b/tests/tests/changes/.gh-400.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/changes/.gh-400.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/changes/gh-400.sft.cpp b/tests/tests/changes/gh-400.sft.cpp new file mode 100644 index 0000000000..5fcf528b14 --- /dev/null +++ b/tests/tests/changes/gh-400.sft.cpp @@ -0,0 +1,68 @@ +//type:fp +//options:--c++20 +//remark:[GH #400] Constant evaluation destroys array elements in decreasing +//remark:order of their index + +struct S { + int* log; + int id; + constexpr ~S() { *log = *log * 10 + id; } +}; + +constexpr int local_array() { + int log = 0; + { + S a[3] = {{&log, 1}, {&log, 2}, {&log, 3}}; + } + return log; +} +static_assert(local_array() == 321); // Previously failed. + +constexpr int multidim_array() { + int log = 0; + { + S a[2][2] = {{{&log, 1}, {&log, 2}}, {{&log, 3}, {&log, 4}}}; + } + return log; +} +static_assert(multidim_array() == 4321); // Previously failed. + +struct M { S m[3]; }; +constexpr int member_array() { + int log = 0; + { + M x = {{{&log, 1}, {&log, 2}, {&log, 3}}}; + } + return log; +} +static_assert(member_array() == 321); // Previously failed. + +struct N { + S m[2]; + constexpr N(int* log) : m{{log, 1}, {log, 2}} {} + constexpr ~N() { *m[0].log = *m[0].log * 10 + 9; } +}; +constexpr int member_array_user_dtor() { + int log = 0; + { + N x(&log); + } + return log; +} +static_assert(member_array_user_dtor() == 921); // Previously failed. + +constexpr int array_delete() { + int log = 0; + S* p = new S[3]{{&log, 1}, {&log, 2}, {&log, 3}}; + delete[] p; + return log; +} +static_assert(array_delete() == 321); // Previously failed. + +constexpr int single_delete() { + int log = 0; + S* p = new S{&log, 5}; + delete p; + return log; +} +static_assert(single_delete() == 5);