https://gcc.gnu.org/g:eccc490c21fc7c9dadf8d13566963bce151c902d

commit r17-3846-geccc490c21fc7c9dadf8d13566963bce151c902d
Author: Jakub Jelinek <[email protected]>
Date:   Wed Sep 2 10:07:18 2026 +0200

    c++, libstdc++: Implement C++26 P2641R4 - Checking if a union alternative 
is active
    
    The following patch attempts to implement this paper (though for now just
    using what the FE provides 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) 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
    2) 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
    3) 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".
       This patch assumes Jason's proposed change to this sentence to change
       lifetime to initialization.
    4) for complete objects created during E evaluation, the patch uses
       a new get_raw_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) and also needs
       to differentiate between the case where pointer points to a complete 
object
       after its storage has been released (that is an error/non-constant) vs.
       when say std::destroy_at has been called on it (it is outside of 
lifetime,
       but should just return false in that case)
    5) we don't handle the builtin correctly inside of constructors where we
       need to keep track of what mem-initializers have been processed already
       (and thus those members are within lifetime) and what have not yet
    6) I fear there might be issues with calling is_within_lifetime on pointers
       to empty class subobjects, I think e.g. reduced_constant_expression_p
       actively removes initializers for those
    
    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.
    
    2026-09-02  Jakub Jelinek  <[email protected]>
    
            P2641R4
    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.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/constexpr.cc                           | 273 +++++++++++++++++++-
 gcc/cp/cp-tree.h                              |   1 +
 gcc/cp/decl.cc                                |   6 +
 gcc/cp/semantics.cc                           |  26 ++
 gcc/cp/tree.cc                                |   1 +
 gcc/testsuite/g++.dg/cpp26/within-lifetime1.C | 353 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/within-lifetime2.C |  65 +++++
 gcc/testsuite/g++.dg/cpp26/within-lifetime3.C | 244 ++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/within-lifetime4.C | 157 ++++++++++++
 gcc/testsuite/g++.dg/cpp26/within-lifetime5.C |  46 ++++
 gcc/testsuite/g++.dg/cpp26/within-lifetime6.C |  41 +++
 gcc/testsuite/g++.dg/cpp26/within-lifetime7.C |  75 ++++++
 libstdc++-v3/include/bits/version.def         |   9 +
 libstdc++-v3/include/bits/version.h           |  10 +
 libstdc++-v3/include/std/type_traits          |   8 +
 libstdc++-v3/src/c++23/std.cc.in              |   3 +
 16 files changed, 1310 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index dbfc460d04f3..a78150786c9a 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -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_ctx *ctx, tree 
t, bool *non_constant_p,
   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 after its storage has been released "
+                       "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 constexpr_ctx 
*ctx, tree t, tree fun,
        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 constexpr_ctx *ctx, tree 
t,
       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;
        }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 75c00f88a083..6b9c8c1ba3f5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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
 };
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 55badc9eed97..27183cd98852 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -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.  */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7907668da36e..469f71dcab6d 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3540,6 +3540,32 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool 
disallow_virtual,
              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);
        }
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index a4aec230a340..1d88d6c574ba 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -578,6 +578,7 @@ builtin_valid_in_constant_expr_p (const_tree decl)
          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;
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C
new file mode 100644
index 000000000000..e5ce7bdb830c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C
new file mode 100644
index 000000000000..dc0544848f7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C
@@ -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 ());
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C
new file mode 100644
index 000000000000..416a25438b28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C
@@ -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' after its storage has been released is 
not a constant expression" }
+  }
+  if (x == 2)
+    __builtin_is_within_lifetime (a);  // { dg-error 
"'__builtin_is_within_lifetime' on 'c' after its storage has been released 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' after its storage has been released 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' after its storage has been released is 
not a constant expression" }
+  }
+  if (x == 5)
+    __builtin_is_within_lifetime (b);  // { dg-error 
"'__builtin_is_within_lifetime' on 'd' after its storage has been released 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' after its storage has been released 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' after its storage has been released is 
not a constant expression" }
+  }
+  if (x == 8)
+    __builtin_is_within_lifetime (a);  // { dg-error 
"'__builtin_is_within_lifetime' on 'c' after its storage has been released 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' after its storage has been released 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' after its storage has been released is 
not a constant expression" }
+  }
+  if (x == 11)
+    __builtin_is_within_lifetime (b);  // { dg-error 
"'__builtin_is_within_lifetime' on 'd' after its storage has been released 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' after its storage has been released 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' after its storage has been released 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" }
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C
new file mode 100644
index 000000000000..b420dea5fe2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C
@@ -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' after its storage has been 
released is not a constant expression" }
+constexpr bool m = __builtin_is_within_lifetime (waldo <int [2]> ());  // { 
dg-error "'__builtin_is_within_lifetime' on 't' after its storage has been 
released 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; }>;
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C
new file mode 100644
index 000000000000..1f780cf13d58
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C
new file mode 100644
index 000000000000..6e9ddf917bdc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C
new file mode 100644
index 000000000000..d920f5aee0cd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C
@@ -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));
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 81bbf0ea63ab..30cc3de51b31 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2496,6 +2496,15 @@ ftms = {
   };
 };
 
+ftms = {
+  name = within_lifetime;
+  values = {
+    v = 202306;
+    cxxmin = 26;
+    extra_cond = "__has_builtin(__builtin_is_within_lifetime)";
+  };
+};
+
 ftms = {
   name = hardened_array;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 786db34d6be7..08e0f1a4b3bc 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -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
diff --git a/libstdc++-v3/include/std/type_traits 
b/libstdc++-v3/include/std/type_traits
index 2b3f10016a29..921ef287f839 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -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, typename... _Args>
     };
 #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
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index ddc1f04ba354..2449af2e8f05 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -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>

Reply via email to