[PATCH] c++, libstdc++: Implement C++26 P2641R4 - Checking if a union alternative is active

Jakub Jelinek jakub@redhat.com
Wed Aug 26 08:08:49 GMT 2026


Hi!

The following patch attempts to implement this paper (though for now just
using what the FE prooovides instead of introducing new stuff, see below).
There is a new builtin which returns true if what the argument points to
is within lifetime, false if it is not and results in non-constant
expression if it points to something non-accessible in constant expression,
or the complete object is not in lifetime.  Most of the tests work
identically in clang++ which implements this paper for some time and g++
with this patch.

There are a few differences/problems, some of those I'd like to address
incrementally:
1) unlike in clang++, the builtin itself is not consteval, just usable
   in constant expressions; people shouldn't be using the builtin directly
   and when it is called from consteval std::is_within_lifetime (or any
   other consteval function or consteval block), it is guaranteed to be
   immediately evaluated.  The builtin if not immediately
   evaluated just quickly checks for some errors (number of arguments and
   that the argument is a pointer) and optimizes itself out.  Having
   a consteval builtin would be a novel thing and I'm not convinced it
   is necessary.
2) this patch implements just P2641R4, not the subsequent P3450R1 paper.
   The plan is to use
  template<class _Up = void, typename _Tp>
    consteval bool
    is_within_lifetime(const _Tp* __p) noexcept
    { return __builtin_is_within_lifetime (__p)
	     && __builtin_constant_p (static_cast <const volatile _Up *> (__p)
				      && true); }
   afterwards but we need
   https://gcc.gnu.org/pipermail/gcc-patches/2026-August/thread.html#726464
   finished for that; I'll restart work on that soon
3) we don't implement std::allocator<T>::allocate correctly at constant
   evaluation time, in particular
   https://eel.is/c++draft/memory#allocator.members-5.sentence-2
   That is pretty much the same thing as in P3726R2 std::start_lifetime
   does, therefore my current plan is once this is in, to restart working
   on P3726R2 and add a new CONSTRUCTOR bit next to CONSTRUCTOR_NO_CLEARING
   which would mean the left out elements are not in lifetime even when the
   whole CONSTRUCTOR is, add support for that in
   cxx_eval_is_within_lifetime and implicitly pretend the builtin is
   used in std::allocator<T>::allocate; this is covered in the testsuite
   in within-lifetime3.C but is guarded with #if 0
4) the standard says in https://eel.is/c++draft/type.traits#meta.const.eval-5
   that "p points to an object that is usable in constant expressions
   or whose complete object's lifetime began within E".
   The patch handles const vars outside of current evaluation (and in that
   case diagnoses/makes non-constant accesses to their mutable members if
   any since those subobjects aren't usable in constant expressions), but
   also diagnoses/making non-constant if the complete object is gone
   (use after scope, use after deallocation, that all seems to me like
   clear UB that should not be constexpr but am not sure that the current
   standard wording is clear about that).
5) for complete objects created during E evaluation, the patch uses
   a new get_value_ptr method (the 2 argument one is too much store
   specific, but I can't just use get_value, because I need to differentiate
   between values hash map doesn't have an entry for this var yet, it
   clearly has not started lifetime yet, and values hash map has a NULL_TREE
   entry for that, that e.g. for scalar means it has started lifetime but
   is uninitialized) and heap_deleted_identifier (for use after delete)
   and is_outside_lifetime (which is use after destruction).  The patch
   doesn't track yet though whether the complete subobject actually began
   its lifetime vs. just started being constructed, so there are 3 xfails
   in within-lifetime3.C test which clang++ handles right; for
   "complete object's lifetime began" I think we need some bit somewhere
   set when the evaluation of constructor for the complete object finishes
   (but is unclear whether we should clear it again when we start destructor
   though because we don't have consteval destructors yet I'm not sure this
   is actually observable).  Also if we can only track it for complete
   objects and not also subobjects.  In any case, the problem of not
   implementing this yet means that std::is_within_lifetime uses in ctors
   on the currently constructed subobjects (where complete object is
   not in lifetime yet) won't be non-constant but will return something
   (false/true).  I'd like to address this incrementally, but am not yet
   sure where to record it without slowing stuff significantly down and
   causing extra compile time memory use
6) thinking about it more, most of the std::is_within_lifetime uses in
   ctors are supposed to be non-constant because the complete object is
   not constructed yet.  But if this is a construction done later on
   after the complete subobject is in lifetime already, like in
  // P3450R1 - Extend std::is_within_lifetime
  // { dg-do compile { target c++26 } }

  #include <memory>
  #include <type_traits>

  struct A {
    consteval A () : a (0), b (0), c (0) {}
    consteval A (int x, int y, int z)
      : a (x), b (y + std::is_within_lifetime (&this->a) * 32
		  + std::is_within_lifetime (&this->c) * 64), c (z) {}
    constexpr ~A () {}
    int a, b, c;
  };

  consteval int
  foo ()
  {
    A 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;
  }

  static_assert (foo () == 4 +  5 + 6 + 32);
  then clang++ IMHO also handles this correctly but we'd need to
  track in detail in consteval ctors (perhaps for this builtin
  don't need to care about other ctors) which members have been
  constructed already (including vacuous initialization) and which
  haven't been yet

In any case, despite the above limitations I think this patch works
for most of the intended uses of the new function template and I hope
the rest can be addressed incrementally.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-08-26  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.
	(check_builtin_is_within_lifetime): Declare.
	* 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.
	* cp-gimplify.cc (cp_gimplify_expr): Handle
	CP_BUILT_IN_IS_WITHIN_LIFETIME.
	* semantics.cc (check_builtin_is_within_lifetime): New function.
	* 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-24 23:45:29.129830790 +0200
+++ b/gcc/cp/cp-tree.h	2026-08-25 12:05:47.373676908 +0200
@@ -7262,6 +7262,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
 };
 
@@ -8686,6 +8687,7 @@ extern tree fold_builtin_is_correspondin
 extern bool pointer_interconvertible_base_of_p	(tree, tree, bool = false);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *);
 extern tree fold_builtin_is_string_literal	(location_t, int, tree *);
+extern bool check_builtin_is_within_lifetime	(location_t, int, tree *, tsubst_flags_t);
 extern tree finish_structured_binding_size	(location_t, tree, tsubst_flags_t);
 extern tree finish_trait_expr			(location_t, enum cp_trait_kind,
 						 tree, tree,
--- a/gcc/cp/tree.cc	2026-08-24 23:44:49.827085609 +0200
+++ b/gcc/cp/tree.cc	2026-08-25 12:05:47.375673007 +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-24 23:44:57.188987689 +0200
+++ b/gcc/cp/decl.cc	2026-08-25 12:05:47.377064416 +0200
@@ -5673,6 +5673,11 @@ 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);
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
--- a/gcc/cp/cp-gimplify.cc	2026-08-24 23:44:49.788086129 +0200
+++ b/gcc/cp/cp-gimplify.cc	2026-08-25 12:05:47.378434745 +0200
@@ -1038,6 +1038,15 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 	      case CP_BUILT_IN_CONSTEXPR_DIAG:
 		*expr_p = void_node;
 		break;
+	      case CP_BUILT_IN_IS_WITHIN_LIFETIME:
+		check_builtin_is_within_lifetime (EXPR_LOCATION (*expr_p),
+						  call_expr_nargs (*expr_p),
+						  call_expr_nargs (*expr_p)
+						  ? &CALL_EXPR_ARG (*expr_p, 0)
+						  : NULL,
+						  tf_warning_or_error);
+		*expr_p = boolean_false_node;
+		break;
 	      default:
 		break;
 	      }
--- a/gcc/cp/semantics.cc	2026-08-24 23:44:49.826085623 +0200
+++ b/gcc/cp/semantics.cc	2026-08-25 14:16:59.977404766 +0200
@@ -14051,6 +14051,36 @@ fold_builtin_is_string_literal (location
   return boolean_true_node;
 }
 
+/* Perform __builtin_is_within_lifetime call checking, return false
+   when errors are reported.  */
+
+bool
+check_builtin_is_within_lifetime (location_t loc, int nargs, tree *args,
+				  tsubst_flags_t complain)
+{
+  /* Unless users call the builtin directly, the following 2 checks should be
+     ensured from std::is_within_lifetime template.  */
+  if (nargs != 1)
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_is_within_lifetime%> needs a single "
+		  "argument");
+      return false;
+    }
+  tree arg = args[0];
+  if (error_operand_p (arg))
+    return false;
+  tree ptype = TREE_TYPE (arg);
+  if (!POINTER_TYPE_P (ptype))
+    {
+      if (complain & tf_error)
+	error_at (loc, "%<__builtin_is_within_lifetime%> argument type %qT "
+		  "is not pointer type", ptype);
+      return false;
+    }
+  return true;
+}
+
 /* [basic.types] 8.  True iff TYPE is an object type.  */
 
 static bool
--- a/gcc/cp/constexpr.cc	2026-08-24 23:44:49.788086129 +0200
+++ b/gcc/cp/constexpr.cc	2026-08-25 19:26:24.647145859 +0200
@@ -1253,6 +1253,13 @@ public:
 	return *p;
     return NULL_TREE;
   }
+  tree *get_value_ptr (tree t)
+  {
+    if (tree *p = values.get (t))
+      if (*p != void_node)
+	return p;
+    return nullptr;
+  }
   tree *get_value_ptr (tree t, bool initializing)
   {
     if (modifiable && !modifiable->contains (t))
@@ -2533,6 +2540,256 @@ 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);
+  if (!check_builtin_is_within_lifetime (loc, call_expr_nargs (t),
+					 call_expr_nargs (t)
+					 ? &CALL_EXPR_ARG (t, 0) : NULL,
+					 ctx->quiet ? tf_none
+					 : tf_warning_or_error))
+    {
+      *non_constant_p = true;
+      return 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_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_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_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)
+    {
+      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_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_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_within_lifetime", obj);
+	}
+      *non_constant_p = true;
+      return t;
+    }
+  tree val = *valp;
+  unsigned i;
+  tree ref;
+  /* In the loop below, val contains the value of the current
+     container (usually a CONSTRUCTOR), or NULL_TREE if we are in zero
+     initialized container (i.e. first union member is in lifetime),
+     or void_list_node if we are in uninitialized container
+     (non-union is still within lifetime, none of the union members are
+     within lifetime.  */
+  if (val == NULL_TREE)
+    val = void_list_node;
+  FOR_EACH_VEC_ELT_REVERSE (refs, i, ref)
+    switch (TREE_CODE (ref))
+      {
+      case REALPART_EXPR:
+      case IMAGPART_EXPR:
+	if (val
+	    && 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_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 == NULL_TREE)
+	      {
+		if (TREE_OPERAND (ref, 1)
+		    != next_aggregate_field (TYPE_FIELDS (union_type)))
+		  return boolean_false_node;
+		continue;
+	      }
+	    else
+	      {
+		if (TREE_CODE (val) != CONSTRUCTOR)
+		  return boolean_false_node;
+		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 = NULL_TREE;
+		    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 == NULL_TREE || val == void_list_node)
+	  continue;
+	if (TREE_CODE (val) != CONSTRUCTOR)
+	  return boolean_false_node;
+	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 = void_list_node;
+	else
+	  val = NULL_TREE;
+	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 == NULL_TREE || val == void_list_node)
+	  continue;
+	if (TREE_CODE (val) == STRING_CST)
+	  return boolean_true_node;
+	if (TREE_CODE (val) != CONSTRUCTOR)
+	  return boolean_false_node;
+	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 = void_list_node;
+	else
+	  val = NULL_TREE;
+	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 +2863,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;
       }
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C	2026-08-25 12:05:47.381287872 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C	2026-08-25 16:58:17.075473131 +0200
@@ -0,0 +1,341 @@
+// 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 1;
+  if (!std::is_within_lifetime (&b))
+    return 2;
+  if (!std::is_within_lifetime (&b.b))
+    return 3;
+  if (!std::is_within_lifetime (&c))
+    return 4;
+  if (!std::is_within_lifetime (&c.b))
+    return 5;
+  if (!std::is_within_lifetime (&c.c[1]))
+    return 6;
+  if (!std::is_within_lifetime (&c.f))
+    return 7;
+  if (!std::is_within_lifetime (&c.f.d))
+    return 8;
+  if (std::is_within_lifetime (&c.f.e))
+    return 9;
+  if (!std::is_within_lifetime (&d))
+    return 10;
+  if (!std::is_within_lifetime (&e))
+    return 11;
+  if (!std::is_within_lifetime (&e.a))
+    return 12;
+  if (!std::is_within_lifetime (&e.g))
+    return 13;
+  if (!std::is_within_lifetime (&e.g.b))
+    return 14;
+  if (std::is_within_lifetime (&e.g.c))
+    return 15;
+  if (std::is_within_lifetime (&e.g.d))
+    return 16;
+  if (std::is_within_lifetime (&e.g.f))
+    return 17;
+  if (!std::is_within_lifetime (&f))
+    return 18;
+  if (!std::is_within_lifetime (&f.a))
+    return 19;
+  if (!std::is_within_lifetime (&f.g))
+    return 20;
+  if (std::is_within_lifetime (&f.g.b))
+    return 21;
+  if (std::is_within_lifetime (&f.g.c))
+    return 22;
+  if (std::is_within_lifetime (&f.g.d))
+    return 23;
+  if (!std::is_within_lifetime (&f.g.f))
+    return 24;
+  if (!std::is_within_lifetime (&f.g.f[0]))
+    return 25;
+  if (!std::is_within_lifetime (&f.g.f[0].e))
+    return 26;
+  if (!std::is_within_lifetime (&f.g.f[1].e))
+    return 27;
+  if (!std::is_within_lifetime (&g))
+    return 28;
+  if (!std::is_within_lifetime (&g.e))
+    return 29;
+  if (std::is_within_lifetime (&g.f))
+    return 30;
+  if (!std::is_within_lifetime (&g.e.a))
+    return 31;
+  if (std::is_within_lifetime (&g.e.d))
+    return 32;
+  g.e.d.c = 1;
+  if (!std::is_within_lifetime (&g))
+    return 33;
+  if (!std::is_within_lifetime (&g.e))
+    return 34;
+  if (std::is_within_lifetime (&g.f))
+    return 35;
+  if (std::is_within_lifetime (&g.e.a))
+    return 36;
+  if (!std::is_within_lifetime (&g.e.d))
+    return 37;
+  if (std::is_within_lifetime (&g.e.d.b))
+    return 38;
+  if (!std::is_within_lifetime (&g.e.d.c))
+    return 39;
+  g.f = 42;
+  if (!std::is_within_lifetime (&g))
+    return 40;
+  if (std::is_within_lifetime (&g.e))
+    return 41;
+  if (!std::is_within_lifetime (&g.f))
+    return 42;
+  if (!std::is_within_lifetime (&h))
+    return 43;
+  if (std::is_within_lifetime (&h.e))
+    return 44;
+  if (std::is_within_lifetime (&h.f))
+    return 45;
+  if (!std::is_within_lifetime (&i))
+    return 46;
+  if (!std::is_within_lifetime (&i.b))
+    return 47;
+  if (!std::is_within_lifetime (&i.c[1]))
+    return 48;
+  if (!std::is_within_lifetime (&i.f))
+    return 49;
+  if (std::is_within_lifetime (&i.f.d))
+    return 50;
+  if (std::is_within_lifetime (&i.f.e))
+    return 51;
+  if (!std::is_within_lifetime (&j))
+    return 52;
+  if (!std::is_within_lifetime (&k))
+    return 53;
+  unsigned char l = std::bit_cast <unsigned char> (k);
+  if (!std::is_within_lifetime (&l))
+    return 54;
+  struct {} m;
+  if (!std::is_within_lifetime (&m))
+    return 55;
+  unsigned char n = std::bit_cast <unsigned char> (m);
+  if (!std::is_within_lifetime (&n))
+    return 56;
+  int o;
+  if (!std::is_within_lifetime (static_cast <volatile int *> (&o)))
+    return 57;
+  if (!std::is_within_lifetime (static_cast <void *> (&o)))
+    return 58;
+  volatile int p;
+  if (!std::is_within_lifetime (const_cast <int *> (&p)))
+    return 59;
+  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 60;
+  if (!std::is_within_lifetime (&q.g))
+    return 61;
+  if (!std::is_within_lifetime (&q.h))
+    return 62;
+  if (std::is_within_lifetime (&q.g.e))
+    return 63;
+  if (std::is_within_lifetime (&q.g.f))
+    return 64;
+  if (std::is_within_lifetime (&q.g.e.c))
+    return 65;
+  if (std::is_within_lifetime (&q.g.e.d))
+    return 66;
+  if (std::is_within_lifetime (&q.g.e.c.a))
+    return 67;
+  if (std::is_within_lifetime (&q.g.e.c.b))
+    return 68;
+  if (!std::is_within_lifetime (static_cast <void *> (&q)))
+    return 69;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+    return 70;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+    return 71;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+    return 72;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+    return 73;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+    return 74;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+    return 75;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+    return 76;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+    return 77;
+  q.g.f = 0;
+  if (!std::is_within_lifetime (&q))
+    return 78;
+  if (!std::is_within_lifetime (&q.g))
+    return 79;
+  if (!std::is_within_lifetime (&q.h))
+    return 80;
+  if (std::is_within_lifetime (&q.g.e))
+    return 81;
+  if (!std::is_within_lifetime (&q.g.f))
+    return 82;
+  if (std::is_within_lifetime (&q.g.e.c))
+    return 83;
+  if (std::is_within_lifetime (&q.g.e.d))
+    return 84;
+  if (std::is_within_lifetime (&q.g.e.c.a))
+    return 85;
+  if (std::is_within_lifetime (&q.g.e.c.b))
+    return 86;
+  if (!std::is_within_lifetime (static_cast <void *> (&q)))
+    return 87;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+    return 88;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+    return 89;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+    return 90;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+    return 91;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+    return 92;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+    return 93;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+    return 94;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+    return 95;
+  q.g.e.d = 0;
+  if (!std::is_within_lifetime (&q))
+    return 96;
+  if (!std::is_within_lifetime (&q.g))
+    return 97;
+  if (!std::is_within_lifetime (&q.h))
+    return 98;
+  if (!std::is_within_lifetime (&q.g.e))
+    return 99;
+  if (std::is_within_lifetime (&q.g.f))
+    return 100;
+  if (std::is_within_lifetime (&q.g.e.c))
+    return 101;
+  if (!std::is_within_lifetime (&q.g.e.d))
+    return 102;
+  if (std::is_within_lifetime (&q.g.e.c.a))
+    return 103;
+  if (std::is_within_lifetime (&q.g.e.c.b))
+    return 104;
+  if (!std::is_within_lifetime (static_cast <void *> (&q)))
+    return 105;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+    return 106;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+    return 107;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+    return 108;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+    return 109;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+    return 110;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+    return 111;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+    return 112;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+    return 113;
+  q.g.e.c.a = 0;
+  if (!std::is_within_lifetime (&q))
+    return 114;
+  if (!std::is_within_lifetime (&q.g))
+    return 115;
+  if (!std::is_within_lifetime (&q.h))
+    return 116;
+  if (!std::is_within_lifetime (&q.g.e))
+    return 117;
+  if (std::is_within_lifetime (&q.g.f))
+    return 118;
+  if (!std::is_within_lifetime (&q.g.e.c))
+    return 119;
+  if (std::is_within_lifetime (&q.g.e.d))
+    return 120;
+  if (!std::is_within_lifetime (&q.g.e.c.a))
+    return 121;
+  if (!std::is_within_lifetime (&q.g.e.c.b))
+    return 122;
+  if (!std::is_within_lifetime (static_cast <void *> (&q)))
+    return 123;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g)))
+    return 124;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.h)))
+    return 125;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e)))
+    return 126;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.f)))
+    return 127;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c)))
+    return 128;
+  if (std::is_within_lifetime (static_cast <void *> (&q.g.e.d)))
+    return 129;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c.a)))
+    return 130;
+  if (!std::is_within_lifetime (static_cast <void *> (&q.g.e.c.b)))
+    return 131;
+  struct { union { int a; short b; }; mutable int c; } r = { .b = 42 };
+  if (!std::is_within_lifetime (&r))
+    return 132;
+  if (std::is_within_lifetime (&r.a))
+    return 133;
+  if (!std::is_within_lifetime (&r.b))
+    return 134;
+  if (!std::is_within_lifetime (&r.c))
+    return 135;
+  if (!std::is_within_lifetime (&s))
+    return 136;
+  if (!std::is_within_lifetime (const_cast <int *> (&s)))
+    return 137;
+  if (!std::is_within_lifetime (const_cast <volatile int *> (&s)))
+    return 138;
+  if (!std::is_within_lifetime (static_cast <const void *> (&s)))
+    return 139;
+  if (!std::is_within_lifetime (t))
+    return 140;
+  if (!std::is_within_lifetime (t + 0))
+    return 141;
+  if (!std::is_within_lifetime (t + 1))
+    return 142;
+  return 0;
+}
+
+static_assert (foo () == 0);
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C	2026-08-25 12:05:47.381392859 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C	2026-08-25 12:05:47.381392859 +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-25 12:05:47.381492104 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C	2026-08-25 17:45:30.229298154 +0200
@@ -0,0 +1,195 @@
+// 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_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_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+  }
+  if (x == 2)
+    __builtin_is_within_lifetime (a);	// { dg-error "'__builtin_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_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_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+  }
+  if (x == 5)
+    __builtin_is_within_lifetime (b);	// { dg-error "'__builtin_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_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_within_lifetime' on 'c' outside its lifetime is not a constant expression" }
+  }
+  if (x == 8)
+    __builtin_is_within_lifetime (a);	// { dg-error "'__builtin_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_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_within_lifetime' on 'd' outside its lifetime is not a constant expression" }
+  }
+  if (x == 11)
+    __builtin_is_within_lifetime (b);	// { dg-error "'__builtin_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_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); };	// { dg-error "" "" { xfail *-*-* } }
+constexpr A n;
+
+struct B { consteval B () : a (42), b (std::is_within_lifetime (&a)) {} int a; bool b; };
+constexpr B o;				// { dg-error "" "" { xfail *-*-* } }
+
+struct C { consteval C () { __builtin_is_within_lifetime (this); } } p;	// { dg-error "" "" { xfail *-*-* } }
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C	2026-08-25 15:27:19.687843572 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C	2026-08-25 19:11:05.217203640 +0200
@@ -0,0 +1,156 @@
+// 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_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);
+  __builtin_is_within_lifetime (static_cast <int *> (nullptr));
+  __builtin_is_within_lifetime (&foo);
+  __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);
+}
+
+extern int v;
+
+consteval bool
+bar (int x)
+{
+  switch (x)
+    {
+    case 0: __builtin_is_within_lifetime (&b); break;
+    case 1: __builtin_is_within_lifetime (nullptr); break; // { dg-error "'__builtin_within_lifetime' called with a null pointer" }
+    case 2: __builtin_is_within_lifetime (static_cast <int *> (nullptr)); break; // { dg-error "'__builtin_within_lifetime' called with a null pointer" }
+    case 3: __builtin_is_within_lifetime (&foo); break;	// { dg-error "'__builtin_within_lifetime' called with pointer to function" }
+    case 4: __builtin_is_within_lifetime (&v); break; // { dg-error "'__builtin_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" }
+bool f = bar (4);				// { 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_within_lifetime' on 'mutable' sub-object '<unnamed struct>::c'" }
+  else if (x == 2)
+    __builtin_is_within_lifetime (&s + 1);	// { dg-error "'__builtin_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 (&corge (0));
+  static_assert (&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_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_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-25 17:08:36.082281771 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C	2026-08-25 17:21:49.881858232 +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-25 17:54:50.544931689 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C	2026-08-25 17:55:34.156362767 +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-25 19:13:45.210104915 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C	2026-08-25 19:28:32.714466636 +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-20 08:39:02.424319866 +0200
+++ b/libstdc++-v3/include/bits/version.def	2026-08-25 13:23:45.637499510 +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-20 08:39:02.424319866 +0200
+++ b/libstdc++-v3/include/bits/version.h	2026-08-25 13:23:50.821020242 +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-07-24 18:38:54.338025248 +0200
+++ b/libstdc++-v3/include/std/type_traits	2026-08-25 13:30:00.826519412 +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-17 10:00:21.952853341 +0200
+++ b/libstdc++-v3/src/c++23/std.cc.in	2026-08-25 13:33:02.423108969 +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