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.

Marek

Reply via email to