https://gcc.gnu.org/g:a099b3ef74189650f81b99cc1aa149d78aec1197
commit r17-5654-ga099b3ef74189650f81b99cc1aa149d78aec1197 Author: Marek Polacek <[email protected]> Date: Fri Oct 2 12:57:10 2026 -0400 c++: ICE with reflect_constant and std::allocator::allocate [PR125649] 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 -- immediately after the initializing declaration of v, the object x declared by v 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. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/constexpr.cc | 35 ++++++++++----------- gcc/cp/cp-tree.h | 1 + gcc/cp/pt.cc | 18 +++++++---- gcc/testsuite/g++.dg/reflect/reflect_constant10.C | 38 +++++++++++++++++++++++ 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 953f1d038eca..ec4969843b72 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3294,10 +3294,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)) return var; constexpr_global_ctx *global = (constexpr_global_ctx *) data; @@ -8239,10 +8236,7 @@ non_const_var_error (location_t loc, tree r, bool fundef_p) { auto_diagnostic_group d; tree type = TREE_TYPE (r); - if (DECL_NAME (r) == heap_uninit_identifier - || DECL_NAME (r) == heap_identifier - || DECL_NAME (r) == heap_vec_uninit_identifier - || DECL_NAME (r) == heap_vec_identifier) + if (heap_var_p (r)) { if (constexpr_error (loc, fundef_p, "the content of uninitialized " "storage is not usable in a constant expression")) @@ -10807,10 +10801,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (decl && VAR_P (decl) && DECL_ARTIFICIAL (decl) - && (DECL_NAME (decl) == heap_identifier - || DECL_NAME (decl) == heap_uninit_identifier - || DECL_NAME (decl) == heap_vec_identifier - || DECL_NAME (decl) == heap_vec_uninit_identifier)) + && heap_var_p (decl)) /* OK */; /* P2738 (C++26): a conversion from a prvalue P of type "pointer to cv void" to a pointer-to-object type T unless P is a null @@ -11435,12 +11426,7 @@ instantiate_constexpr_fns (tree t) static tree find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/) { - if (VAR_P (*tp) - && (DECL_NAME (*tp) == heap_uninit_identifier - || DECL_NAME (*tp) == heap_identifier - || DECL_NAME (*tp) == heap_vec_uninit_identifier - || DECL_NAME (*tp) == heap_vec_identifier - || DECL_NAME (*tp) == heap_deleted_identifier)) + if (VAR_P (*tp) && heap_var_p (*tp, /*include_deleted_p=*/true)) return *tp; if (TYPE_P (*tp)) @@ -13936,4 +13922,17 @@ fini_constexpr (void) fundef_copies_table = NULL; } +/* Return true if T is one of the heap vars. Include "heap deleted" if + INCLUDE_DELETED_P. */ + +bool +heap_var_p (tree t, bool include_deleted_p/*=false*/) +{ + return (DECL_NAME (t) == heap_uninit_identifier + || DECL_NAME (t) == heap_identifier + || DECL_NAME (t) == heap_vec_uninit_identifier + || DECL_NAME (t) == heap_vec_identifier + || (include_deleted_p && DECL_NAME (t) == heap_deleted_identifier)); +} + #include "gt-cp-constexpr.h" diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index fbcd0d21328b..73fd3e8aa7a0 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9470,6 +9470,7 @@ extern void diagnose_failing_condition (tree, location_t, bool, extern bool replace_decl (tree *, tree, tree); extern tree cxa_allocate_and_throw_exception (location_t, const constexpr_ctx *, tree); +extern bool heap_var_p (tree, bool = false); /* Whether our evaluation wants a prvalue (e.g. CONSTRUCTOR or _CST), a glvalue (e.g. VAR_DECL or _REF), or nothing. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 26bc7c21db31..e6a68bf1d374 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -7559,11 +7559,11 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain) "because %qD has no linkage", expr, type, decl); return true; } - /* For a constant template parameter of reference or pointer type, - or for each non-static data member of reference or pointer type - in a constant template parameter of class type or subobject thereof, - the reference or pointer value shall not refer or point to - (respectively): + /* [temp.arg.nontype]/6: For a constant template parameter of + reference or pointer type, or for each non-static data member of + reference or pointer type in a constant template parameter of class + type or subobject thereof, the reference or pointer value shall not + refer or point to (respectively): -- a temporary object, -- a string literal object, -- the result of a typeid expression, @@ -7572,9 +7572,15 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain) else if (VAR_P (decl) && DECL_ARTIFICIAL (decl) && !DECL_NTTP_OBJECT_P (decl)) { + /* The heap_var_p check doesn't cover any of the bullets above, + but we can get here with an invalid reflect_constant arg. + Heap vars are not constexpr-referenceable, so they're not valid + here either. */ gcc_checking_assert (DECL_TINFO_P (decl) || DECL_FNAME_P (decl) - || DECL_IGNORED_P (decl)); + || DECL_IGNORED_P (decl) + || heap_var_p (decl, + /*include_deleted_p=*/true)); if (complain & tf_error) error ("the address of %qD is not a valid template argument", decl); diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant10.C b/gcc/testsuite/g++.dg/reflect/reflect_constant10.C new file mode 100644 index 000000000000..43deea408d8c --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/reflect_constant10.C @@ -0,0 +1,38 @@ +// PR c++/125649 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <memory> +#include <meta> + +consteval { + const int *m = std::allocator<int>().allocate(1); + std::meta::reflect_constant(m); +} // { dg-error "reflect_constant failed" } + +consteval { + const int *m = std::allocator<int>().allocate(1); + std::meta::reflect_constant(m + 1); +} // { dg-error "reflect_constant failed" } + +// This is well-formed. +consteval { + int *p = std::allocator<int>().allocate(1); + std::allocator<int>().deallocate(p, 1); + bool threw = false; + try { std::meta::reflect_constant(p); } + catch (std::meta::exception&) { threw = true; } + if (!threw) + throw; +} + +// This is well-formed. +consteval { + int *p = std::allocator<int>().allocate(1); + bool threw = false; + try { std::meta::reflect_constant(p); } + catch (std::meta::exception&) { threw = true; } + std::allocator<int>().deallocate(p, 1); + if (!threw) + throw; +}
