From 8c2d918cd55d0ee3c77c85b3d1f405eda1cd11b4 Mon Sep 17 00:00:00 2001 From: Daveed Vandevoorde Date: Mon, 9 Nov 2026 17:14:27 +0000 Subject: [PATCH] Count the characters, not the bytes, of a string literal initializer of an array new-expression in constant evaluation [GH #501] --- src/interpret.c | 6 +++-- .../gh-501-array-new-wide-string.txt | 15 +++++++++++ .../.gh-501-neg.rto/default.1.1.txt | 9 +++++++ .../regressions/.gh-501.rto/default.1.1.txt | 1 + tests/tests/regressions/gh-501-neg.sft.cpp | 11 ++++++++ tests/tests/regressions/gh-501.sft.cpp | 26 +++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/pending_changes/gh-501-array-new-wide-string.txt create mode 100644 tests/tests/regressions/.gh-501-neg.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-501.rto/default.1.1.txt create mode 100644 tests/tests/regressions/gh-501-neg.sft.cpp create mode 100644 tests/tests/regressions/gh-501.sft.cpp diff --git a/src/interpret.c b/src/interpret.c index eb9c769d5a..79be653dda 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -26825,8 +26825,10 @@ Evaluate the given new-expression. a_constant_ptr cp = init_cp->variant.aggregate.first_constant; for (; cp != NULL; cp = cp->next) ++n_init_elems; } else { - /* ck_string case. */ - n_init_elems = init_cp->variant.string.length; + /* ck_string case (whose length is in bytes). */ + a_type_ptr etp = skip_typerefs( + init_tp->variant.array.element_type); + n_init_elems = init_cp->variant.string.length / etp->size; } /* if */ if (n_init_elems > orig_alloc_length) { info_with_pos_num(ec_constexpr_alloc_too_small, diff --git a/src/pending_changes/gh-501-array-new-wide-string.txt b/src/pending_changes/gh-501-array-new-wide-string.txt new file mode 100644 index 0000000000..599226b33f --- /dev/null +++ b/src/pending_changes/gh-501-array-new-wide-string.txt @@ -0,0 +1,15 @@ +[GH #501] +Array new with a wide string literal initializer in constant evaluation + +During constant evaluation, the initializer of an array new-expression that is +a string literal of a character type wider than one byte (e.g., wchar_t) was +treated as having as many elements as the literal has bytes, so a large enough +array could be diagnosed as too small for it. For example: + + constexpr int f(int n) { + auto p = new wchar_t[n]{L"ab"}; + int r = p[1]; + delete[] p; + return r; + } + static_assert(f(3) == L'b'); // Previously an error; now okay. diff --git a/tests/tests/regressions/.gh-501-neg.rto/default.1.1.txt b/tests/tests/regressions/.gh-501-neg.rto/default.1.1.txt new file mode 100644 index 0000000000..ae8e52163a --- /dev/null +++ b/tests/tests/regressions/.gh-501-neg.rto/default.1.1.txt @@ -0,0 +1,9 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c +"Test_name.c", line 11: error: expression must have a constant value + static_assert(g(2) == L'b'); // Error: too few elements for the initializer. + ^ +"Test_name.c", line 6: note: number of dynamically-allocated elements (2) too small for initializer + auto p = new wchar_t[n]{L"ab"}; + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-501.rto/default.1.1.txt b/tests/tests/regressions/.gh-501.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/regressions/.gh-501.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/regressions/gh-501-neg.sft.cpp b/tests/tests/regressions/gh-501-neg.sft.cpp new file mode 100644 index 0000000000..172e757d1d --- /dev/null +++ b/tests/tests/regressions/gh-501-neg.sft.cpp @@ -0,0 +1,11 @@ +//type:fn +//options_all:--c++20 +//remark:Array new with a too-long wide string literal initializer + +constexpr int g(int n) { + auto p = new wchar_t[n]{L"ab"}; + int r = p[1]; + delete[] p; + return r; +} +static_assert(g(2) == L'b'); // Error: too few elements for the initializer. diff --git a/tests/tests/regressions/gh-501.sft.cpp b/tests/tests/regressions/gh-501.sft.cpp new file mode 100644 index 0000000000..32cc7683d7 --- /dev/null +++ b/tests/tests/regressions/gh-501.sft.cpp @@ -0,0 +1,26 @@ +//type:fp +//options_all:--c++20 +//remark:Constexpr array new with a wide string literal initializer + +// The number of elements of a string literal initializer is compared with the +// number of allocated elements, regardless of the character size. +constexpr int g(int n) { + auto p = new wchar_t[n]{L"ab"}; + int r = p[1] + p[n - 1]; + delete[] p; + return r; +} +static_assert(g(3) == L'b'); +static_assert(g(4) == L'b'); + +constexpr int h(int n) { + auto p16 = new char16_t[n]{u"ab"}; + auto p32 = new char32_t[n]{U"ab"}; + auto p8 = new char8_t[n]{u8"ab"}; + int r = (p16[1] == u'b') + (p32[1] == U'b') + (p8[1] == u8'b'); + delete[] p16; + delete[] p32; + delete[] p8; + return r; +} +static_assert(h(3) == 3);