[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