[PATCH] c++, libstdc++, v4: Implement C++26 P2641R4 - Checking if a union alternative is active
Jakub Jelinek
jakub@redhat.com
Tue Sep 1 15:53:15 GMT 2026
On Tue, Sep 01, 2026 at 11:01:41AM -0400, Jason Merrill wrote:
> This seems backwards to me, since void_node is also used for out-of-lifetime
> subobjects (which are still within storage duration).
Incremental:
--- a/gcc/cp/constexpr.cc 2026-08-31 21:24:22.190633669 +0200
+++ b/gcc/cp/constexpr.cc 2026-09-01 17:29:28.984871915 +0200
@@ -1181,7 +1181,7 @@ enum constexpr_switch_state {
class constexpr_global_ctx {
/* Values for any temporaries or local variables within the
constant-expression. Objects outside their lifetime have
- value 'void_node' or 'void_list_node', the latter if they are outside
+ value 'void_node' or 'void_list_node', the former if they are outside
of lifetime but still before storage has been deallocated. */
hash_map<tree,tree> values;
public:
@@ -1285,7 +1285,7 @@ public:
if (TREE_CODE (t) == VAR_DECL
|| TREE_CODE (t) == PARM_DECL
|| TREE_CODE (t) == RESULT_DECL)
- values.put (t, past_storage_end ? void_node : void_list_node);
+ values.put (t, past_storage_end ? void_list_node : void_node);
else
values.remove (t);
}
@@ -2628,7 +2628,7 @@ cxx_eval_is_within_lifetime (const const
check_mutable = true;
}
}
- if (!valp || *valp == void_node)
+ if (!valp || *valp == void_list_node)
{
if (!ctx->quiet)
{
@@ -2656,7 +2656,7 @@ cxx_eval_is_within_lifetime (const const
return t;
}
tree val = *valp;
- if (val == void_list_node)
+ if (val == void_node)
return boolean_false_node;
unsigned i;
tree ref;
seems to work just fine too, so if you prefer that, the following
patch includes that. Tested so far with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make -j32 check-g++
2026-09-01 Jakub Jelinek <jakub@redhat.com>
gcc/cp/
* cp-tree.h: Implement C++26 P2641R4 - Checking if a union
alternative is active.
(enum cp_built_in_function): Add CP_BUILT_IN_IS_WITHIN_LIFETIME.
* tree.cc (builtin_valid_in_constant_expr_p): Handle
CP_BUILT_IN_IS_WITHIN_LIFETIME.
* decl.cc (cxx_init_decl_processing): Create decl for
CP_BUILT_IN_IS_WITHIN_LIFETIME.
* semantics.cc (finish_call_expr): Perform semantic checking of
__builtin_is_within_lifetime.
* constexpr.cc (constexpr_global_ctx::get_value): New method.
(eval_and_check_array_index): Add forward declaration.
(cxx_eval_is_within_lifetime): New function.
(cxx_eval_builtin_function_call): Handle
CP_BUILT_IN_IS_WITHIN_LIFETIME.
gcc/testsuite/
* g++.dg/cpp26/within-lifetime1.C: New test.
* g++.dg/cpp26/within-lifetime2.C: New test.
* g++.dg/cpp26/within-lifetime3.C: New test.
* g++.dg/cpp26/within-lifetime4.C: New test.
* g++.dg/cpp26/within-lifetime5.C: New test.
* g++.dg/cpp26/within-lifetime6.C: New test.
* g++.dg/cpp26/within-lifetime7.C: New test.
libstdc++-v3/
* include/bits/version.def (within_lifetime): New.
* include/bits/version.h: Regenerate.
* include/std/type_traits: Define __glibcxx_want_within_lifetime
before including bits/version.h.
(std::is_within_lifetime): New function template.
* src/c++23/std.cc.in (std::is_within_lifetime): Export.
--- a/gcc/cp/cp-tree.h 2026-08-29 14:18:54.467771290 +0200
+++ b/gcc/cp/cp-tree.h 2026-08-31 17:18:05.988079258 +0200
@@ -7211,6 +7211,7 @@ enum cp_built_in_function {
CP_BUILT_IN_CONSTEXPR_DIAG,
CP_BUILT_IN_CURRENT_EXCEPTION,
CP_BUILT_IN_UNCAUGHT_EXCEPTIONS,
+ CP_BUILT_IN_IS_WITHIN_LIFETIME,
CP_BUILT_IN_LAST
};
--- a/gcc/cp/tree.cc 2026-08-31 09:40:40.507713716 +0200
+++ b/gcc/cp/tree.cc 2026-08-31 17:18:06.022184287 +0200
@@ -578,6 +578,7 @@ builtin_valid_in_constant_expr_p (const_
case CP_BUILT_IN_CONSTEXPR_DIAG:
case CP_BUILT_IN_CURRENT_EXCEPTION:
case CP_BUILT_IN_UNCAUGHT_EXCEPTIONS:
+ case CP_BUILT_IN_IS_WITHIN_LIFETIME:
return true;
default:
break;
--- a/gcc/cp/decl.cc 2026-08-31 17:12:10.714890019 +0200
+++ b/gcc/cp/decl.cc 2026-08-31 17:18:06.023574417 +0200
@@ -5673,6 +5673,12 @@ cxx_init_decl_processing (void)
BUILT_IN_FRONTEND, NULL, NULL_TREE);
set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+ decl = add_builtin_function ("__builtin_is_within_lifetime",
+ bool_vaftype, CP_BUILT_IN_IS_WITHIN_LIFETIME,
+ BUILT_IN_FRONTEND, NULL, NULL_TREE);
+ set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+ SET_DECL_IMMEDIATE_FUNCTION_P (decl);
+
integer_two_node = build_int_cst (NULL_TREE, 2);
/* Guess at the initial static decls size. */
--- a/gcc/cp/semantics.cc 2026-08-27 17:41:43.924633203 +0200
+++ b/gcc/cp/semantics.cc 2026-08-31 17:18:06.025090753 +0200
@@ -3540,6 +3540,32 @@ finish_call_expr (tree fn, vec<tree, va_
warn_for_memset (input_location, arg0, arg2, literal_mask);
}
+ if (TREE_CODE (fn) == FUNCTION_DECL
+ && fndecl_built_in_p (fn, CP_BUILT_IN_IS_WITHIN_LIFETIME,
+ BUILT_IN_FRONTEND))
+ {
+ /* Unless users call the builtin directly, the following 2 checks
+ should be ensured from std::is_within_lifetime template. */
+ if (vec_safe_length (*args) != 1)
+ {
+ if (complain & tf_error)
+ error ("%<__builtin_is_within_lifetime%> needs a single "
+ "argument");
+ return error_mark_node;
+ }
+ tree arg = (**args)[0];
+ if (error_operand_p (arg))
+ return error_mark_node;
+ tree ptype = TREE_TYPE (arg);
+ if (!POINTER_TYPE_P (ptype))
+ {
+ if (complain & tf_error)
+ error ("%<__builtin_is_within_lifetime%> argument type "
+ "%qT is not pointer type", ptype);
+ return error_mark_node;
+ }
+ }
+
/* A call to a namespace-scope function. */
result = build_new_function_call (fn, args, orig_complain);
}
--- a/gcc/cp/constexpr.cc 2026-08-31 09:35:42.611573034 +0200
+++ b/gcc/cp/constexpr.cc 2026-09-01 17:29:28.984871915 +0200
@@ -1180,8 +1180,9 @@ enum constexpr_switch_state {
class constexpr_global_ctx {
/* Values for any temporaries or local variables within the
- constant-expression. Objects outside their lifetime have
- value 'void_node'. */
+ constant-expression. Objects outside their lifetime have
+ value 'void_node' or 'void_list_node', the former if they are outside
+ of lifetime but still before storage has been deallocated. */
hash_map<tree,tree> values;
public:
/* Number of cxx_eval_constant_expression calls (except skipped ones,
@@ -1242,24 +1243,28 @@ public:
bool is_outside_lifetime (tree t)
{
if (tree *p = values.get (t))
- if (*p == void_node)
+ if (*p == void_node || *p == void_list_node)
return true;
return false;
}
tree get_value (tree t)
{
if (tree *p = values.get (t))
- if (*p != void_node)
+ if (*p != void_node && *p != void_list_node)
return *p;
return NULL_TREE;
}
+ tree *get_raw_value_ptr (tree t)
+ {
+ return values.get (t);
+ }
tree *get_value_ptr (tree t, bool initializing)
{
if (modifiable && !modifiable->contains (t))
return nullptr;
if (tree *p = values.get (t))
{
- if (*p != void_node)
+ if (*p != void_node && *p != void_list_node)
return p;
else if (initializing)
{
@@ -1275,12 +1280,12 @@ public:
if (!already_in_map && modifiable)
modifiable->add (t);
}
- void destroy_value (tree t)
+ void destroy_value (tree t, bool past_storage_end = true)
{
if (TREE_CODE (t) == VAR_DECL
|| TREE_CODE (t) == PARM_DECL
|| TREE_CODE (t) == RESULT_DECL)
- values.put (t, void_node);
+ values.put (t, past_storage_end ? void_list_node : void_node);
else
values.remove (t);
}
@@ -2533,6 +2538,253 @@ cxx_eval_constexpr_diag (const constexpr
return void_node;
}
+static tree eval_and_check_array_index (const constexpr_ctx *, tree, bool,
+ bool *, bool *, tree *);
+
+/* Attempt to evaluate T which represents a call to
+ __builtin_is_within_lifetime. */
+
+static tree
+cxx_eval_is_within_lifetime (const constexpr_ctx *ctx, tree t,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ location_t loc = EXPR_LOCATION (t);
+ tree arg = CALL_EXPR_ARG (t, 0);
+ arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
+ if (*non_constant_p)
+ return t;
+ /* Need to strip casts to pointers to void, but should preserve
+ pointer casts within the innermost pointer to void cast if
+ any, so that e.g. pointers to heap allocations are handled
+ correctly. */
+ tree p = arg;
+ while (CONVERT_EXPR_P (p) || TREE_CODE (p) == NON_LVALUE_EXPR)
+ {
+ if (!TYPE_PTR_P (TREE_TYPE (p)))
+ break;
+ if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (p))))
+ arg = TREE_OPERAND (p, 0);
+ p = TREE_OPERAND (p, 0);
+ }
+ if (integer_zerop (arg))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qs called with a null pointer",
+ "__builtin_is_within_lifetime");
+ *non_constant_p = true;
+ return t;
+ }
+ if (POINTER_TYPE_P (TREE_TYPE (arg))
+ && (TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == METHOD_TYPE))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qs called with pointer to function",
+ "__builtin_is_within_lifetime");
+ *non_constant_p = true;
+ return t;
+ }
+ arg = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (arg)), arg);
+ arg = cxx_eval_constant_expression (ctx, arg, vc_glvalue,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
+ if (*non_constant_p)
+ return t;
+ auto_vec <tree, 4> refs;
+ tree obj;
+ for (obj = arg; ; obj = TREE_OPERAND (obj, 0))
+ {
+ switch (TREE_CODE (obj))
+ {
+ case COMPONENT_REF:
+ case ARRAY_REF:
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ refs.safe_push (obj);
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ tree *valp = nullptr;
+ bool check_mutable = false;
+ if (DECL_P (obj))
+ {
+ valp = ctx->global->get_raw_value_ptr (obj);
+ if (!valp
+ && VAR_P (obj)
+ && TREE_STATIC (obj)
+ && decl_constant_var_p (obj))
+ {
+ valp = &DECL_INITIAL (obj);
+ check_mutable = true;
+ }
+ }
+ if (!valp || *valp == void_list_node)
+ {
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ if (DECL_P (obj) && DECL_NAME (obj) == heap_deleted_identifier)
+ {
+ error_at (loc, "%qs on allocated storage after deallocation "
+ "is not a constant expression",
+ "__builtin_is_within_lifetime");
+ inform (DECL_SOURCE_LOCATION (obj), "allocated here");
+ }
+ else if (DECL_P (obj) && ctx->global->is_outside_lifetime (obj))
+ {
+ error_at (loc, "%qs on %qE outside its lifetime is not a "
+ "constant expression",
+ "__builtin_is_within_lifetime", obj);
+ inform (DECL_SOURCE_LOCATION (obj), "declared here");
+ }
+ else
+ error_at (loc, "%qs on %qE from outside current evaluation "
+ "is not a constant expression",
+ "__builtin_is_within_lifetime", obj);
+ }
+ *non_constant_p = true;
+ return t;
+ }
+ tree val = *valp;
+ if (val == void_node)
+ return boolean_false_node;
+ unsigned i;
+ tree ref;
+ /* Magic value used for val when inside the following loop when
+ inside of an omitted part of initializer due to it being
+ uninitialized. In this case return boolean_true_node unless
+ there is some union member access, in unitialized union
+ no member is within lifetime. */
+ const tree val_uninit = global_namespace;
+ /* Magic value used for val when inside the following loop when
+ inside of an omitted part of initializer due to it being
+ zero initialized. In this case return boolean_true_node unless
+ there is some union member access except for the first member. */
+ const tree val_zero_init = std_node;
+ if (val == NULL_TREE)
+ val = val_uninit;
+ FOR_EACH_VEC_ELT_REVERSE (refs, i, ref)
+ switch (TREE_CODE (ref))
+ {
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ if (TREE_CODE (val) == COMPLEX_EXPR
+ && (TREE_OPERAND (val, TREE_CODE (ref) == IMAGPART_EXPR)
+ == void_node))
+ return boolean_false_node;
+ return boolean_true_node;
+ case COMPONENT_REF:
+ if (check_mutable && DECL_MUTABLE_P (TREE_OPERAND (ref, 1)))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qs on %<mutable%> sub-object %qD",
+ "__builtin_is_within_lifetime",
+ TREE_OPERAND (ref, 1));
+ *non_constant_p = true;
+ return t;
+ }
+ if (TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == UNION_TYPE)
+ {
+ tree union_type = TREE_TYPE (TREE_OPERAND (ref, 0));
+ if (val == val_zero_init)
+ {
+ if (TREE_OPERAND (ref, 1)
+ != next_aggregate_field (TYPE_FIELDS (union_type)))
+ return boolean_false_node;
+ continue;
+ }
+ else if (val == val_uninit)
+ return boolean_false_node;
+ else
+ {
+ gcc_assert (TREE_CODE (val) == CONSTRUCTOR);
+ if (CONSTRUCTOR_NELTS (val) == 0)
+ {
+ if (CONSTRUCTOR_NO_CLEARING (val))
+ return boolean_false_node;
+ tree first
+ = next_aggregate_field (TYPE_FIELDS (union_type));
+ if (first != TREE_OPERAND (ref, 1))
+ return boolean_false_node;
+ val = val_zero_init;
+ continue;
+ }
+ else
+ {
+ if (CONSTRUCTOR_ELT (val, 0)->index
+ != TREE_OPERAND (ref, 1))
+ return boolean_false_node;
+ val = CONSTRUCTOR_ELT (val, 0)->value;
+ if (val == void_node)
+ return boolean_false_node;
+ continue;
+ }
+ }
+ }
+ if (val == val_zero_init || val == val_uninit)
+ continue;
+ gcc_assert (TREE_CODE (val) == CONSTRUCTOR);
+ unsigned int j;
+ tree field, value;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), j, field, value)
+ if (field == TREE_OPERAND (ref, 1))
+ {
+ if (value == void_node)
+ return boolean_false_node;
+ val = value;
+ ref = NULL_TREE;
+ break;
+ }
+ if (ref == NULL_TREE)
+ continue;
+ if (CONSTRUCTOR_NO_CLEARING (val))
+ val = val_uninit;
+ else
+ val = val_zero_init;
+ continue;
+ case ARRAY_REF:
+ field = eval_and_check_array_index (ctx, ref, false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
+ if (*non_constant_p)
+ return t;
+ if (val == val_zero_init || val == val_uninit)
+ continue;
+ if (TREE_CODE (val) == STRING_CST)
+ return boolean_true_node;
+ gcc_assert (TREE_CODE (val) == CONSTRUCTOR);
+ HOST_WIDE_INT idx;
+ idx = find_array_ctor_elt (val, field, false);
+ if (idx != -1)
+ {
+ val = CONSTRUCTOR_ELT (val, idx)->value;
+ if (val == void_node)
+ return boolean_false_node;
+ continue;
+ }
+ if (CONSTRUCTOR_NO_CLEARING (val))
+ val = val_uninit;
+ else
+ val = val_zero_init;
+ continue;
+ default:
+ gcc_unreachable ();
+ }
+ return boolean_true_node;
+}
+
/* Attempt to evaluate T which represents a call to a builtin function.
We assume here that all builtin functions evaluate to scalar types
represented by _CST nodes. */
@@ -2606,6 +2858,10 @@ cxx_eval_builtin_function_call (const co
return cxx_eval_constexpr_diag (ctx, t, non_constant_p, overflow_p,
jump_target);
+ case CP_BUILT_IN_IS_WITHIN_LIFETIME:
+ return cxx_eval_is_within_lifetime (ctx, t, non_constant_p, overflow_p,
+ jump_target);
+
default:
break;
}
@@ -8171,7 +8427,8 @@ cxx_eval_store_expression (const constex
if (CLOBBER_KIND (init) >= CLOBBER_OBJECT_END
&& refs->is_empty ())
{
- ctx->global->destroy_value (object);
+ ctx->global->destroy_value (object, (CLOBBER_KIND (init)
+ > CLOBBER_OBJECT_END));
return void_node;
}
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-08-31 17:18:06.027032693 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-08-31 17:18:06.027032693 +0200
@@ -0,0 +1,353 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+
+#if __has_builtin(__builtin_is_within_lifetime)
+namespace std {
+ template <class T>
+ consteval bool
+ is_within_lifetime (const T *p) noexcept
+ {
+ return __builtin_is_within_lifetime (p);
+ }
+}
+#endif
+
+namespace std {
+ template <typename T, typename F>
+ constexpr T
+ bit_cast (const F &f) noexcept
+ {
+ return __builtin_bit_cast (T, f);
+ }
+}
+
+constexpr char d = 0;
+struct E { char a; union F { int b; long c; short d; struct G { int e; } f[2]; } g; };
+constexpr E e = {};
+constexpr E f = { .a = 1, .g = { .f = {} } };
+constexpr int s = 42;
+constexpr int t[2] = {};
+
+consteval int
+foo ()
+{
+ char a = 0;
+ struct B { int b; } b = {};
+ struct C : B { int c[2]; union D { int d; long e; } f; } c = {};
+ union H { union I { int a; union J { int b; long c; } d; } e; long f; } g = {};
+ H h;
+ C i;
+ char j;
+ bool k = true;
+ if (!std::is_within_lifetime (&a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&b.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c.c))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c.c[1]))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&c.f.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&c.f.e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&d))
+ return __LINE__;
+ if (!std::is_within_lifetime (&e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&e.a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&e.g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&e.g.b))
+ return __LINE__;
+ if (std::is_within_lifetime (&e.g.c))
+ return __LINE__;
+ if (std::is_within_lifetime (&e.g.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&e.g.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.g))
+ return __LINE__;
+ if (std::is_within_lifetime (&f.g.b))
+ return __LINE__;
+ if (std::is_within_lifetime (&f.g.c))
+ return __LINE__;
+ if (std::is_within_lifetime (&f.g.d))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.g.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.g.f[0]))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.g.f[0].e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&f.g.f[1].e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.e.a))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.e.d))
+ return __LINE__;
+ g.e.d.c = 1;
+ if (!std::is_within_lifetime (&g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.f))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.e.a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.e.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.e.d.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.e.d.c))
+ return __LINE__;
+ g.f = 42;
+ if (!std::is_within_lifetime (&g))
+ return __LINE__;
+ if (std::is_within_lifetime (&g.e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&g.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&h))
+ return __LINE__;
+ if (std::is_within_lifetime (&h.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&h.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&i))
+ return __LINE__;
+ if (!std::is_within_lifetime (&i.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&i.c[1]))
+ return __LINE__;
+ if (!std::is_within_lifetime (&i.f))
+ return __LINE__;
+ if (std::is_within_lifetime (&i.f.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&i.f.e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&j))
+ return __LINE__;
+ if (!std::is_within_lifetime (&k))
+ return __LINE__;
+ unsigned char l = std::bit_cast <unsigned char> (k);
+ if (!std::is_within_lifetime (&l))
+ return __LINE__;
+ struct {} m;
+ if (!std::is_within_lifetime (&m))
+ return __LINE__;
+ unsigned char n = std::bit_cast <unsigned char> (m);
+ if (!std::is_within_lifetime (&n))
+ return __LINE__;
+ int o;
+ if (!std::is_within_lifetime (static_cast <volatile int *> (&o)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&o)))
+ return __LINE__;
+ volatile int p;
+ if (!std::is_within_lifetime (const_cast <int *> (&p)))
+ return __LINE__;
+ struct { union { union { struct { int a; long b; } c; int d; } e; int f; } g; int h; } q;
+ if (!std::is_within_lifetime (&q))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.h))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.f))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.a))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+ return __LINE__;
+ q.g.f = 0;
+ if (!std::is_within_lifetime (&q))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.h))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.f))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.a))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+ return __LINE__;
+ q.g.e.d = 0;
+ if (!std::is_within_lifetime (&q))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.h))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.f))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e.d))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.a))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.c.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+ return __LINE__;
+ q.g.e.c.a = 0;
+ if (!std::is_within_lifetime (&q))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.h))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.f))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e.c))
+ return __LINE__;
+ if (std::is_within_lifetime (&q.g.e.d))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e.c.a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&q.g.e.c.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+ return __LINE__;
+ if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+ return __LINE__;
+ struct { union { int a; short b; }; mutable int c; } r = { .b = 42 };
+ if (!std::is_within_lifetime (&r))
+ return __LINE__;
+ if (std::is_within_lifetime (&r.a))
+ return __LINE__;
+ if (!std::is_within_lifetime (&r.b))
+ return __LINE__;
+ if (!std::is_within_lifetime (&r.c))
+ return __LINE__;
+ if (!std::is_within_lifetime (&s))
+ return __LINE__;
+ if (!std::is_within_lifetime (const_cast <int *> (&s)))
+ return __LINE__;
+ if (!std::is_within_lifetime (const_cast <volatile int *> (&s)))
+ return __LINE__;
+ if (!std::is_within_lifetime (static_cast <const void *> (&s)))
+ return __LINE__;
+ if (!std::is_within_lifetime (t))
+ return __LINE__;
+ if (!std::is_within_lifetime (t + 0))
+ return __LINE__;
+ if (!std::is_within_lifetime (t + 1))
+ return __LINE__;
+ int u[4];
+ if (!std::is_within_lifetime (&u))
+ return __LINE__;
+ if (!std::is_within_lifetime (&u[2]))
+ return __LINE__;
+ u[1] = 42;
+ if (!std::is_within_lifetime (&u))
+ return __LINE__;
+ if (!std::is_within_lifetime (&u[2]))
+ return __LINE__;
+ return 0;
+}
+
+static_assert (foo () == 0);
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C 2026-08-31 17:18:06.027133114 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C 2026-08-31 17:18:06.027133114 +0200
@@ -0,0 +1,65 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+
+#include "../cpp2a/construct_at.h"
+
+namespace std {
+ template <class T>
+ consteval bool
+ is_within_lifetime (const T *p) noexcept
+ {
+ return __builtin_is_within_lifetime (p);
+ }
+}
+
+consteval bool
+foo (int &x, char &y)
+{
+ if (std::is_within_lifetime (&x) || std::is_within_lifetime (&y))
+ return false;
+ std::construct_at (&y, 42);
+ if (std::is_within_lifetime (&x) || !std::is_within_lifetime (&y))
+ return false;
+ std::construct_at (&x, 41);
+ if (!std::is_within_lifetime (&x) || std::is_within_lifetime (&y))
+ return false;
+ return true;
+}
+
+static_assert ([] { union { int a; char b; } u; return foo (u.a, u.b); } ());
+static_assert ([] { union { int a; char b; }; return foo (a, b); } ());
+static_assert ([] { struct { union { int a; char b; }; } s; return foo (s.a, s.b); } ());
+static_assert ([] { struct { union { int a; char b; } u; } s; return foo (s.u.a, s.u.b); } ());
+
+consteval bool
+bar ()
+{
+ union { union { int a; long b; } c; short d; };
+ if (std::is_within_lifetime (&d)
+ || std::is_within_lifetime (&c)
+ || std::is_within_lifetime (&c.a)
+ || std::is_within_lifetime (&c.b))
+ return false;
+ std::construct_at (&d);
+ if (!std::is_within_lifetime (&d)
+ || std::is_within_lifetime (&c)
+ || std::is_within_lifetime (&c.a)
+ || std::is_within_lifetime (&c.b))
+ return false;
+ std::construct_at (&c);
+ std::construct_at (&c.b);
+ if (std::is_within_lifetime (&d)
+ || !std::is_within_lifetime (&c)
+ || std::is_within_lifetime (&c.a)
+ || !std::is_within_lifetime (&c.b))
+ return false;
+ std::construct_at (&c.a);
+ if (std::is_within_lifetime (&d)
+ || !std::is_within_lifetime (&c)
+ || !std::is_within_lifetime (&c.a)
+ || std::is_within_lifetime (&c.b))
+ return false;
+ return true;
+}
+
+static_assert (bar ());
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C 2026-08-31 17:18:06.027254926 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C 2026-08-31 17:51:06.105294794 +0200
@@ -0,0 +1,244 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+
+#include "../cpp2a/construct_at.h"
+
+namespace std {
+ template <class T>
+ consteval bool
+ is_within_lifetime (const T *p) noexcept
+ {
+ return __builtin_is_within_lifetime (p);
+ }
+}
+
+consteval bool
+foo (bool x)
+{
+ std::allocator <int> a;
+ auto b = a.allocate (1);
+#if 0
+ // [allocator.members]/5 says it should start lifetime of the array
+ // but not its elements, and b points to the first element.
+ if (std::is_within_lifetime (b))
+ return false;
+#endif
+ std::construct_at (b);
+ if (!std::is_within_lifetime (b))
+ return false;
+ std::destroy_at (b);
+ if (std::is_within_lifetime (b))
+ return false;
+ a.deallocate (b, 1);
+ if (x)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on allocated storage after deallocation is not a constant expression" }
+ return true;
+}
+
+static_assert (foo (false));
+bool a = foo (true); // { dg-error "call to consteval function 'foo\\\(true\\\)' is not a constant expression" }
+
+consteval bool
+bar (int x)
+{
+ int *a;
+ decltype (nullptr) *b;
+ {
+ int c = 42;
+ a = &c;
+ if (!std::is_within_lifetime (a))
+ return false;
+ }
+ {
+ int c = 42;
+ if (x == 1)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ }
+ if (x == 2)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ {
+ int c[42] = {};
+ a = &c[40];
+ if (!std::is_within_lifetime (a))
+ return false;
+ }
+ if (x == 3)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ {
+ decltype (nullptr) d = nullptr;
+ b = &d;
+ if (!std::is_within_lifetime (b))
+ return false;
+ }
+ {
+ decltype (nullptr) d = nullptr;
+ if (x == 4)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ }
+ if (x == 5)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ {
+ decltype (nullptr) d[42] = {};
+ b = &d[40];
+ if (!std::is_within_lifetime (b))
+ return false;
+ }
+ if (x == 6)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ {
+ int c;
+ a = &c;
+ if (!std::is_within_lifetime (a))
+ return false;
+ }
+ {
+ int c;
+ if (x == 7)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ }
+ if (x == 8)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ {
+ int c[42];
+ a = &c[40];
+ if (!std::is_within_lifetime (a))
+ return false;
+ }
+ if (x == 9)
+ __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+ {
+ decltype (nullptr) d;
+ b = &d;
+ if (!std::is_within_lifetime (b))
+ return false;
+ }
+ {
+ decltype (nullptr) d;
+ if (x == 10)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ }
+ if (x == 11)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ {
+ decltype (nullptr) d[42];
+ b = &d[40];
+ if (!std::is_within_lifetime (b))
+ return false;
+ }
+ if (x == 12)
+ __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+ int e[2];
+ if (!std::is_within_lifetime (&e)
+ || !std::is_within_lifetime (&e[0])
+ || !std::is_within_lifetime (&e[1]))
+ return true;
+ std::destroy_at (&e[0]);
+ if (!std::is_within_lifetime (&e)
+ || std::is_within_lifetime (&e[0])
+ || !std::is_within_lifetime (&e[1]))
+ return false;
+ std::construct_at (&e[0]);
+ std::destroy_at (&e[1]);
+ if (!std::is_within_lifetime (&e)
+ || !std::is_within_lifetime (&e[0])
+ || std::is_within_lifetime (&e[1]))
+ return false;
+ std::construct_at (&e[1]);
+ if (!std::is_within_lifetime (&e)
+ || !std::is_within_lifetime (&e[0])
+ || !std::is_within_lifetime (&e[1]))
+ return false;
+ struct { int a, b; } f;
+ if (!std::is_within_lifetime (&f)
+ || !std::is_within_lifetime (&f.a)
+ || !std::is_within_lifetime (&f.b))
+ return true;
+ std::destroy_at (&f.a);
+ if (!std::is_within_lifetime (&f)
+ || std::is_within_lifetime (&f.a)
+ || !std::is_within_lifetime (&f.b))
+ return false;
+ std::construct_at (&f.a);
+ std::destroy_at (&f.b);
+ if (!std::is_within_lifetime (&f)
+ || !std::is_within_lifetime (&f.a)
+ || std::is_within_lifetime (&f.b))
+ return false;
+ std::construct_at (&f.b);
+ if (!std::is_within_lifetime (&f)
+ || !std::is_within_lifetime (&f.a)
+ || !std::is_within_lifetime (&f.b))
+ return false;
+ return true;
+}
+
+static_assert (bar (0));
+bool b = bar (1); // { dg-error "call to consteval function 'bar\\\(1\\\)' is not a constant expression" }
+bool c = bar (2); // { dg-error "call to consteval function 'bar\\\(2\\\)' is not a constant expression" }
+bool d = bar (3); // { dg-error "call to consteval function 'bar\\\(3\\\)' is not a constant expression" }
+bool e = bar (4); // { dg-error "call to consteval function 'bar\\\(4\\\)' is not a constant expression" }
+bool f = bar (5); // { dg-error "call to consteval function 'bar\\\(5\\\)' is not a constant expression" }
+bool g = bar (6); // { dg-error "call to consteval function 'bar\\\(6\\\)' is not a constant expression" }
+bool h = bar (7); // { dg-error "call to consteval function 'bar\\\(7\\\)' is not a constant expression" }
+bool i = bar (8); // { dg-error "call to consteval function 'bar\\\(8\\\)' is not a constant expression" }
+bool j = bar (9); // { dg-error "call to consteval function 'bar\\\(9\\\)' is not a constant expression" }
+bool k = bar (10); // { dg-error "call to consteval function 'bar\\\(10\\\)' is not a constant expression" }
+bool l = bar (11); // { dg-error "call to consteval function 'bar\\\(11\\\)' is not a constant expression" }
+bool m = bar (12); // { dg-error "call to consteval function 'bar\\\(12\\\)' is not a constant expression" }
+
+struct A { int a = 42; bool b = std::is_within_lifetime (&a); };
+constexpr A n;
+
+struct B { consteval B () : a (42), b (std::is_within_lifetime (&a)) {} int a; bool b; };
+constexpr B o;
+
+struct C { consteval C () { __builtin_is_within_lifetime (this); } } p;
+
+struct D {
+ consteval D () : a (0), b (0), c (0) {}
+ consteval D (int x, int y, int z)
+ : a (x), b (y + std::is_within_lifetime (&this->a) * 32
+ + std::is_within_lifetime (&this->b) * 64
+ + std::is_within_lifetime (&this->c) * 128), c (z) {}
+ constexpr ~D () {}
+ int a, b, c;
+};
+
+consteval int
+qux ()
+{
+ D a[2] = {};
+ std::destroy_at (&a[0]);
+ std::construct_at (&a[0], 4, 5, 6);
+ return a[0].a + a[0].b + a[0].c;
+}
+
+// FIXME: In mem-initializer of b, a should be already constructed,
+// so within lifetime, but b is in the middle of construction and c
+// construction has not started yet.
+static_assert (qux () == 4 + 5 + 6 + 32); // { dg-bogus "note: the comparison reduces to '\\\(239 == 47\\\)'" "" { xfail *-*-* } }
+ // { dg-bogus "static assertion failed" "" { xfail *-*-* } .-1 }
+
+consteval bool
+corge (bool x)
+{
+ int *p;
+ {
+ int a;
+ p = &a;
+ if (!std::is_within_lifetime (&a))
+ return false;
+ std::destroy_at (&a);
+ if (std::is_within_lifetime (&a))
+ return false;
+ std::construct_at (&a);
+ if (!std::is_within_lifetime (&a))
+ return false;
+ }
+ if (x)
+ __builtin_is_within_lifetime (p); // { dg-error "'__builtin_is_within_lifetime' on 'a' outside its lifetime is not a constant expression" }
+ return true;
+}
+
+static_assert (corge (false));
+bool q = corge (true); // { dg-error "call to consteval function 'corge\\\(true\\\)' is not a constant expression" }
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C 2026-08-31 17:18:06.027418748 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C 2026-08-31 17:18:06.027418748 +0200
@@ -0,0 +1,157 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+
+template <typename T>
+consteval bool baz (const T *p) { return __builtin_is_within_lifetime (p); }
+// { dg-error "'__builtin_is_within_lifetime' on 'a' from outside current evaluation is not a constant expression" "" { target *-*-* } .-1 }
+bool a = baz (&a); // { dg-error "is not a constant expression" }
+constexpr bool b = false;
+
+void
+foo (int *p)
+{
+ __builtin_is_within_lifetime (&b);
+ __builtin_is_within_lifetime (nullptr); // { dg-error "'__builtin_is_within_lifetime' argument type 'std::nullptr_t' is not pointer type" }
+ __builtin_is_within_lifetime (static_cast <int *> (nullptr)); // { dg-error "is not a constant expression" }
+ // { dg-error "'__builtin_is_within_lifetime' called with a null pointer" "" { target *-*-* } .-1 }
+ __builtin_is_within_lifetime (&foo); // { dg-error "is not a constant expression" }
+ // { dg-error "'__builtin_is_within_lifetime' called with pointer to function" "" { target *-*-* } .-1 }
+ __builtin_is_within_lifetime (0); // { dg-error "'__builtin_is_within_lifetime' argument type 'int' is not pointer type" }
+ __builtin_is_within_lifetime (0.0); // { dg-error "'__builtin_is_within_lifetime' argument type 'double' is not pointer type" }
+ __builtin_is_within_lifetime (); // { dg-error "'__builtin_is_within_lifetime' needs a single argument" }
+ __builtin_is_within_lifetime (&b, &b); // { dg-error "'__builtin_is_within_lifetime' needs a single argument" }
+ __builtin_is_within_lifetime (p); // { dg-error "call to consteval function '__builtin_is_within_lifetime\\\(p\\\)' is not a constant expression" }
+ // { dg-error "'p' is not a constant expression" "" { target *-*-* } .-1 }
+}
+
+extern int v;
+
+consteval bool
+bar (int x)
+{
+ switch (x)
+ {
+ case 0: __builtin_is_within_lifetime (&b); break;
+ case 1: __builtin_is_within_lifetime (static_cast <int *> (nullptr)); break; // { dg-error "'__builtin_is_within_lifetime' called with a null pointer" }
+ case 2: __builtin_is_within_lifetime (&foo); break; // { dg-error "'__builtin_is_within_lifetime' called with pointer to function" }
+ case 3: __builtin_is_within_lifetime (&v); break; // { dg-error "'__builtin_is_within_lifetime' on 'v' from outside current evaluation is not a constant expression" }
+ }
+ return true;
+}
+
+static_assert (bar (0));
+bool c = bar (1); // { dg-error "is not a constant expression" }
+bool d = bar (2); // { dg-error "is not a constant expression" }
+bool e = bar (3); // { dg-error "is not a constant expression" }
+
+constexpr struct { union { int a; short b; }; mutable int c; } g = { .b = 42 };
+constexpr int s = 42;
+constexpr int t[2] = {};
+
+consteval bool
+qux (int x)
+{
+ if (!__builtin_is_within_lifetime (&g))
+ return false;
+ if (__builtin_is_within_lifetime (&g.a))
+ return false;
+ if (!__builtin_is_within_lifetime (&g.b))
+ return false;
+ if (x == 1)
+ __builtin_is_within_lifetime (&g.c); // { dg-error "'__builtin_is_within_lifetime' on 'mutable' sub-object '<unnamed struct>::c'" }
+ else if (x == 2)
+ __builtin_is_within_lifetime (&s + 1); // { dg-error "'__builtin_is_within_lifetime' on '\\\*\\\(\\\(\\\& s\\\) \\\+ 4\\\)' from outside current evaluation is not a constant expression" }
+ else if (x == 3)
+ __builtin_is_within_lifetime (t + 2); // { dg-error "array subscript value '2' is outside the bounds of array 't' of type 'const int \\\[2\\\]'" }
+ return true;
+}
+
+static_assert (qux (0));
+bool h = qux (1); // { dg-error "is not a constant expression" }
+bool i = qux (2); // { dg-error "is not a constant expression" }
+bool j = qux (3); // { dg-error "is not a constant expression" }
+
+struct A {
+ constexpr A () {}
+ constexpr A (const A &) {}
+ constexpr ~A () {}
+};
+
+template <typename T>
+constexpr T &
+corge (T &&x)
+{
+ return static_cast <T &> (x);
+}
+
+consteval bool
+fred ()
+{
+ static_assert (__builtin_is_within_lifetime (&corge (0)));
+ static_assert (__builtin_is_within_lifetime (&corge (A {})));
+ if (!__builtin_is_within_lifetime (&corge (0)))
+ return false;
+ if (!__builtin_is_within_lifetime (&corge (A {})))
+ return false;
+ return true;
+}
+
+static_assert (fred ());
+
+constexpr const int &k = 0;
+static_assert (__builtin_is_within_lifetime (&k));
+
+template <typename T>
+consteval T *
+waldo ()
+{
+ T t;
+ return &t; // { dg-warning "address of local variable 't' returned" }
+
+}
+
+constexpr bool l = __builtin_is_within_lifetime (waldo <int> ()); // { dg-error "'__builtin_is_within_lifetime' on 't' outside its lifetime is not a constant expression" }
+constexpr bool m = __builtin_is_within_lifetime (waldo <int [2]> ()); // { dg-error "'__builtin_is_within_lifetime' on 't' outside its lifetime is not a constant expression" }
+
+template <typename T, T V>
+struct integral_constant
+{
+ static constexpr T value = V;
+ using value_type = T;
+ using type = integral_constant <T, V>;
+ constexpr operator value_type () const noexcept { return value; }
+ constexpr value_type operator () () const noexcept { return value; }
+};
+
+template <bool V>
+using bool_constant = integral_constant <bool, V>;
+
+using true_type = bool_constant <true>;
+using false_type = bool_constant <false>;
+
+constexpr int n = 42;
+
+template <auto T>
+concept B = bool_constant <__builtin_is_within_lifetime (T ())>::value;
+
+static_assert (B <[] { return &n; }>);
+static_assert (!B <[] { return static_cast <int *> (nullptr); }>);
+static_assert (!B <[] { return static_cast <void (*) (int *)> (&foo); }>);
+
+template <auto T>
+constexpr true_type
+garply () requires B <T>
+{
+ return {};
+}
+
+template <auto T>
+false_type
+garply ()
+{
+ return {};
+}
+
+static_assert (decltype (garply <[] { return &n; }> ())::value);
+static_assert (!decltype (garply <[] { return static_cast <int *> (nullptr); }> ())::value);
+true_type (*o) () = &garply <[] { return &n; }>;
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C 2026-08-31 17:18:06.027556081 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C 2026-08-31 17:18:06.027556081 +0200
@@ -0,0 +1,46 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++23 } }
+// { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } }
+
+namespace std {
+ template <class T>
+ consteval bool
+ is_within_lifetime (const T *p) noexcept
+ {
+ return __builtin_is_within_lifetime (p);
+ }
+}
+
+struct OptBool {
+ union { bool b; char c; };
+
+ // note: this assumes common implementation properties for bool and char:
+ // * sizeof (bool) == sizeof (char), and
+ // * the value representations for true and false are distinct
+ // from the value representation for 2
+ constexpr OptBool () : c (2) { }
+ constexpr OptBool (bool b) : b (b) { }
+
+ constexpr bool has_value () const
+ {
+ if consteval
+ {
+ return std::is_within_lifetime (&b); // during constant evaluation, cannot read from c
+ }
+ else
+ {
+ return c != 2; // during runtime, must read from c
+ }
+ }
+
+ constexpr const bool &operator * () const { return b; }
+};
+
+constexpr OptBool disengaged;
+constexpr OptBool engaged (true);
+static_assert (!disengaged.has_value ());
+static_assert (engaged.has_value ());
+static_assert (*engaged);
+constexpr OptBool engaged2 (false);
+static_assert (engaged2.has_value ());
+static_assert (!*engaged2);
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C 2026-08-31 17:18:06.027652156 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C 2026-08-31 17:18:06.027652156 +0200
@@ -0,0 +1,41 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++26 } }
+// { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } }
+
+#include <type_traits>
+
+#if __cpp_lib_within_lifetime >= 202306L
+struct OptBool {
+ union { bool b; char c; };
+
+ // note: this assumes common implementation properties for bool and char:
+ // * sizeof (bool) == sizeof (char), and
+ // * the value representations for true and false are distinct
+ // from the value representation for 2
+ constexpr OptBool () : c (2) { }
+ constexpr OptBool (bool b) : b (b) { }
+
+ constexpr bool has_value () const
+ {
+ if consteval
+ {
+ return std::is_within_lifetime (&b); // during constant evaluation, cannot read from c
+ }
+ else
+ {
+ return c != 2; // during runtime, must read from c
+ }
+ }
+
+ constexpr const bool &operator * () const { return b; }
+};
+#endif
+
+constexpr OptBool disengaged;
+constexpr OptBool engaged (true);
+static_assert (!disengaged.has_value ());
+static_assert (engaged.has_value ());
+static_assert (*engaged);
+constexpr OptBool engaged2 (false);
+static_assert (engaged2.has_value ());
+static_assert (!*engaged2);
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C 2026-08-31 17:18:06.027754340 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C 2026-08-31 17:18:06.027754340 +0200
@@ -0,0 +1,75 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+#include "../cpp2a/construct_at.h"
+
+consteval bool
+foo (int n)
+{
+ int a[n];
+ if (!__builtin_is_within_lifetime (&a))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[0]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n / 2]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n - 1]))
+ return false;
+ std::destroy_at (&a[0]);
+ if (!__builtin_is_within_lifetime (&a))
+ return false;
+ if (__builtin_is_within_lifetime (&a[0]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n / 2]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n - 1]))
+ return false;
+ std::construct_at (&a[0]);
+ std::destroy_at (&a[n / 2]);
+ if (!__builtin_is_within_lifetime (&a))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[0]))
+ return false;
+ if (__builtin_is_within_lifetime (&a[n / 2]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n - 1]))
+ return false;
+ std::construct_at (&a[n / 2]);
+ std::destroy_at (&a[n - 1]);
+ if (!__builtin_is_within_lifetime (&a))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[0]))
+ return false;
+ if (!__builtin_is_within_lifetime (&a[n / 2]))
+ return false;
+ if (__builtin_is_within_lifetime (&a[n - 1]))
+ return false;
+ std::construct_at (&a[n - 1]);
+ _Complex double b = 1.0;
+ if (!__builtin_is_within_lifetime (&b))
+ return false;
+ if (!__builtin_is_within_lifetime (&__real__ b))
+ return false;
+ if (!__builtin_is_within_lifetime (&__imag__ b))
+ return false;
+ std::destroy_at (&__real__ b);
+ if (!__builtin_is_within_lifetime (&b))
+ return false;
+ if (__builtin_is_within_lifetime (&__real__ b))
+ return false;
+ if (!__builtin_is_within_lifetime (&__imag__ b))
+ return false;
+ std::construct_at (&__real__ b);
+ std::destroy_at (&__imag__ b);
+ if (!__builtin_is_within_lifetime (&b))
+ return false;
+ if (!__builtin_is_within_lifetime (&__real__ b))
+ return false;
+ if (__builtin_is_within_lifetime (&__imag__ b))
+ return false;
+ std::construct_at (&__imag__ b);
+ return true;
+}
+
+static_assert (foo (42));
--- a/libstdc++-v3/include/bits/version.def 2026-08-27 17:41:43.926633177 +0200
+++ b/libstdc++-v3/include/bits/version.def 2026-08-31 17:18:06.028272627 +0200
@@ -2497,6 +2497,15 @@ ftms = {
};
ftms = {
+ name = within_lifetime;
+ values = {
+ v = 202306;
+ cxxmin = 26;
+ extra_cond = "__has_builtin(__builtin_is_within_lifetime)";
+ };
+};
+
+ftms = {
name = hardened_array;
values = {
v = 202502;
--- a/libstdc++-v3/include/bits/version.h 2026-08-27 17:41:43.926633177 +0200
+++ b/libstdc++-v3/include/bits/version.h 2026-08-31 17:18:06.028561440 +0200
@@ -2749,6 +2749,16 @@
#endif /* !defined(__cpp_lib_valarray) */
#undef __glibcxx_want_valarray
+#if !defined(__cpp_lib_within_lifetime)
+# if (__cplusplus > 202302L) && (__has_builtin(__builtin_is_within_lifetime))
+# define __glibcxx_within_lifetime 202306L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_within_lifetime)
+# define __cpp_lib_within_lifetime 202306L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_within_lifetime) */
+#undef __glibcxx_want_within_lifetime
+
#if !defined(__cpp_lib_hardened_array)
# if (__cplusplus >= 201103L) && (defined(_GLIBCXX_ASSERTIONS))
# define __glibcxx_hardened_array 202502L
--- a/libstdc++-v3/include/std/type_traits 2026-08-27 17:41:43.927633164 +0200
+++ b/libstdc++-v3/include/std/type_traits 2026-08-31 17:18:06.028956596 +0200
@@ -66,6 +66,7 @@
#define __glibcxx_want_type_trait_variable_templates
#define __glibcxx_want_unwrap_ref
#define __glibcxx_want_void_t
+#define __glibcxx_want_within_lifetime
#include <bits/version.h>
extern "C++"
@@ -4400,6 +4401,13 @@ template<typename _Ret, typename _Fn, ty
};
#endif // C++11
+#if __cpp_lib_within_lifetime >= 202306L // C++ >= 26
+ template<typename _Tp>
+ consteval bool
+ is_within_lifetime(const _Tp* __p) noexcept
+ { return __builtin_is_within_lifetime (__p); }
+#endif
+
/// @} group metaprogramming
_GLIBCXX_END_NAMESPACE_VERSION
--- a/libstdc++-v3/src/c++23/std.cc.in 2026-08-27 17:41:43.927633164 +0200
+++ b/libstdc++-v3/src/c++23/std.cc.in 2026-08-31 17:18:06.029869556 +0200
@@ -3610,6 +3610,9 @@ export namespace std
using std::is_structural;
using std::is_structural_v;
#endif
+#if __cpp_lib_within_lifetime >= 202306L
+ using std::is_within_lifetime;
+#endif
}
// <typeindex>
Jakub
More information about the Libstdc++
mailing list