On 10/23/26 5:27 PM, Marek Polacek wrote:
On Fri, Oct 23, 2026 at 04:01:35PM -0400, Jason Merrill wrote:
On 10/5/26 10:59 AM, Marek Polacek wrote:
On Sat, Oct 03, 2026 at 09:32:38AM +0200, Jason Merrill wrote:
On 10/2/26 11:19 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
In this PR we have
consteval{
const int* m = std::allocator<int>().allocate(1);
std::meta::reflect_constant(m);
}
which is invalid: reflect_constant throws meta::exception unless the
template-id TCls<V> would be valid ([meta.reflection.result]/5).
[temp.arg.nontype]/1 wants the invented declaration T x = E; to satisfy
the semantic constraints of a constexpr variable with static storage duration.
[dcl.constexpr]/6: A constexpr variable shall be constant-initializable.
[expr.const.init]/4: A variable v is constant-initializable if [...]
-- the full-expression of its initialization is a constant expression
-- if x has static or thread storage duration, x is constexpr-representable
[...]
[expr.const.init]/3: x is constexpr-representable at P if every object
that a constituent value of x points to is constexpr-referenceable
from P.
[expr.const.init]/2: An object o is constexpr-referenceable from a point P
if ... it has static storage duration, or automatic storage duration with
certain scope conditions.
But the array object allocate gave us has dynamic storage duration as per
[allocator.members]/5 -> it's not constexpr-referenceable -> this TCls<V>
is invalid so reflect_constant should throw. But we trip on the assert
because decl is a heap variable.
There is no way AFAIK to check the failure sooner than invalid_tparm_referent_p
(here we are called from eval_reflect_constant) so this patch extends the
existing assert.
PR c++/125649
gcc/cp/ChangeLog:
* constexpr.cc (addr_of_non_const_var): Use heap_var_p.
(non_const_var_error): Likewise.
(cxx_eval_constant_expression): Likewise.
(find_heap_var_refs): Likewise.
(heap_var_p): New.
* cp-tree.h (heap_var_p): Declare.
* pt.cc (invalid_tparm_referent_p): Add heap_var_p to an
assert.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/reflect_constant10.C: New test.
---
gcc/cp/constexpr.cc | 35 +++++++++--------
gcc/cp/cp-tree.h | 1 +
gcc/cp/pt.cc | 18 ++++++---
.../g++.dg/reflect/reflect_constant10.C | 38 +++++++++++++++++++
4 files changed, 68 insertions(+), 24 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/reflect_constant10.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 31ef5bf1111..a99659f9685 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3276,10 +3276,7 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees,
void *data)
if (tree var = get_base_address (TREE_OPERAND (*tp, 0)))
if (VAR_P (var) && TREE_STATIC (var))
{
- if (DECL_NAME (var) == heap_uninit_identifier
- || DECL_NAME (var) == heap_identifier
- || DECL_NAME (var) == heap_vec_uninit_identifier
- || DECL_NAME (var) == heap_vec_identifier)
+ if (heap_var_p (var))
Why don't we want to include _deleted in all cases?
I didn't want to change behavior by introducing that helper. E.g.,
in non_const_var_error the second branch would be dead if the first
one accepted _deleted, and cxx_eval_constant_expression would also
behave differently for the void* -> T* cast.
Ah, that makes sense for those cases, but what about this one?
The heap_var_p call in addr_of_non_const_var probably shouldn't include
_deleted: addr_of_non_const_var is used to decide if we can cache a call
that contains the heap var:
int *p = allocate (...);
foo (p); // cannot cache otherwise...
*p = 42;
foo (p); // ...this could do the wrong thing
deallocate(p);
foo (p); // error
foo (p); // error whether it's cached or not
so (not) caching a call with _deleted should make no difference.
Fair enough, OK.
Jason