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))
return var;
constexpr_global_ctx *global = (constexpr_global_ctx *) data;
@@ -8221,10 +8218,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"))
@@ -10789,10 +10783,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
@@ -11417,12 +11408,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))
@@ -13918,4 +13904,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 947bf62ff98..2d6445d6d4f 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 6c8d0e2de71..c7917f38c9b 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -7556,11 +7556,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,
@@ -7569,9 +7569,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 00000000000..43deea408d8
--- /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;
+}
base-commit: f31d4844cf3c35566ba7e06a2097dc51ea55369d
--
2.55.0