https://github.com/My-Bad-2 created 
https://github.com/llvm/llvm-project/pull/196502

This PR introduces `__builtin_start_lifetime_as`, the compiler frontend 
primitive required to implement C++23 `std::start_lifetime_as` and 
`std::start_lifetime_as_array` 
([P2590R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2590r2.pdf)).

This builtin acts as a gatekeeper in the AST by validating standard compliance 
by enforcing implicit-lifetime type contraints, intercepting VLAs, and 
rejecting evaluation in `constexpr` contexts. 

The implementation also provides a dual-path approach via an optional boolean 
flag. When provided, this flag instructs `Sema` to bypass the C++23 
implicit-lifetime checks.

Why Reject VLAs? GCC 16 also rejects VLAs from `std::start_lifetime_as`. After 
a bit of research, it appears that because `std::start_lifetime_as` requires a 
fixed-size type where are VLAs have a runtime-determined size.

>From a13a3feb53ea6d5d5c333094239267e71b0663cd Mon Sep 17 00:00:00 2001
From: Yash Verma <[email protected]>
Date: Thu, 7 May 2026 13:22:51 -0400
Subject: [PATCH 1/3] Adds `bool isImplicitLifetimeType() const` to
 `clang::Type` to map the C++20 [basic.types.general] rules to the AST.

This method serves as a common path to determine whether a type is an 
implicit-lifetime type.
---
 clang/include/clang/AST/TypeBase.h |  3 ++
 clang/lib/AST/Type.cpp             | 59 ++++++++++++++++++++++++++++++
 clang/lib/Sema/SemaTypeTraits.cpp  | 43 +---------------------
 3 files changed, 63 insertions(+), 42 deletions(-)

diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index b2887bcc36246..6476d85d46984 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2470,6 +2470,9 @@ class alignas(TypeAlignment) Type : public 
ExtQualsTypeCommonBase {
   /// or QualType::getSingleStepDesugaredType(const ASTContext&).
   QualType getLocallyUnqualifiedSingleStepDesugaredType() const;
 
+  /// Determine whether this type is an implicit-lifetime type.
+  bool isImplicitLifetimeType() const;
+
   /// As an extension, we classify types as one of "sized" or "sizeless";
   /// every type is one or the other.  Standard types are all sized;
   /// sizeless types are purely an extension.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 06023fc088a32..ffb25b4f51350 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5914,3 +5914,62 @@ StringRef PredefinedSugarType::getName(Kind KD) {
   }
   llvm_unreachable("unexpected kind");
 }
+
+bool Type::isImplicitLifetimeType() const {
+  // [basic.types.general] p9
+  // Scalar types, implicit-lifetime class types ([class.prop]), array types,
+  // and cv-qualified versions of these types are collectively called
+  // implicit-lifetime types.
+  QualType UnqualT = getCanonicalTypeUnqualified();
+
+  if (UnqualT->isScalarType())
+    return true;
+
+  // Arrays are implicit Lifetime types.
+  if (UnqualT->isArrayType() || UnqualT->isVectorType() ||
+      UnqualT->isExtVectorType())
+    return true;
+
+  const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
+  if (!RD)
+    return false;
+
+  // [class.prop] p9
+  // A class S is an implicit-lifetime class if
+  //   - it is an aggregate whose destructor is not user-provided or
+  //   - it has atleast one trivial eligible constructor and a trivial,
+  //   non-deleted destructor.
+
+  const CXXDestructorDecl *Dtor = RD->getDestructor();
+  if (UnqualT->isAggregateType() && (!Dtor || !Dtor->isUserProvided()))
+    return true;
+
+  // Type must have a trivial, non-deleted destructor
+  bool HasTrivialNonDeletedDtr =
+      RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted());
+  if (!HasTrivialNonDeletedDtr)
+    return false;
+
+  for (CXXConstructorDecl *Ctor : RD->ctors()) {
+    if (Ctor->isIneligibleOrNotSelected() || Ctor->isDeleted())
+      continue;
+
+    if (Ctor->isTrivial())
+      return true;
+  }
+
+  if (RD->needsImplicitDefaultConstructor() &&
+      RD->hasTrivialDefaultConstructor() &&
+      !RD->hasNonTrivialDefaultConstructor())
+    return true;
+
+  if (RD->needsImplicitCopyConstructor() && RD->hasTrivialCopyConstructor() &&
+      !RD->defaultedCopyConstructorIsDeleted())
+    return true;
+
+  if (RD->needsImplicitMoveConstructor() && RD->hasTrivialMoveConstructor() &&
+      !RD->defaultedMoveConstructorIsDeleted())
+    return true;
+
+  return false;
+}
\ No newline at end of file
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index a94a59e8add7b..7f575fdacf9ae 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1070,48 +1070,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait 
UTT,
     DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
                                  tok::kw___builtin_is_implicit_lifetime);
 
-    // [basic.types.general] p9
-    // Scalar types, implicit-lifetime class types ([class.prop]),
-    // array types, and cv-qualified versions of these types
-    // are collectively called implicit-lifetime types.
-    QualType UnqualT = T->getCanonicalTypeUnqualified();
-    if (UnqualT->isScalarType())
-      return true;
-    if (UnqualT->isArrayType() || UnqualT->isVectorType())
-      return true;
-    const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
-    if (!RD)
-      return false;
-
-    // [class.prop] p9
-    // A class S is an implicit-lifetime class if
-    //   - it is an aggregate whose destructor is not user-provided or
-    //   - it has at least one trivial eligible constructor and a trivial,
-    //     non-deleted destructor.
-    const CXXDestructorDecl *Dtor = RD->getDestructor();
-    if (UnqualT->isAggregateType() && (!Dtor || !Dtor->isUserProvided()))
-      return true;
-    bool HasTrivialNonDeletedDtr =
-        RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted());
-    if (!HasTrivialNonDeletedDtr)
-      return false;
-    for (CXXConstructorDecl *Ctr : RD->ctors()) {
-      if (Ctr->isIneligibleOrNotSelected() || Ctr->isDeleted())
-        continue;
-      if (Ctr->isTrivial())
-        return true;
-    }
-    if (RD->needsImplicitDefaultConstructor() &&
-        RD->hasTrivialDefaultConstructor() &&
-        !RD->hasNonTrivialDefaultConstructor())
-      return true;
-    if (RD->needsImplicitCopyConstructor() && RD->hasTrivialCopyConstructor() 
&&
-        !RD->defaultedCopyConstructorIsDeleted())
-      return true;
-    if (RD->needsImplicitMoveConstructor() && RD->hasTrivialMoveConstructor() 
&&
-        !RD->defaultedMoveConstructorIsDeleted())
-      return true;
-    return false;
+    return T->isImplicitLifetimeType();
   }
   case UTT_IsIntangibleType:
     assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only 
feature");

>From 70c37dcf81c65f0f71f5a641068afe21a4df7a78 Mon Sep 17 00:00:00 2001
From: Yash Verma <[email protected]>
Date: Fri, 8 May 2026 04:33:46 -0400
Subject: [PATCH 2/3] [Clang] Implement __builtin_start_lifetime_as instrinsic

This introduces the compiler frontend primitive required to implement C++23 
`std::start_lifetime_as` and `std::start_lifetime_as_array`.

Key Changes:
  - Builtins.td: Register `__builtin_start_lifetime_as`
  - SemaChecking.cpp: Implement `Sema::BuiltinStartLifetimeAs` to validate 
pointer arguments and intercept VLAs.
  - AST Validation: Reuses `isImplicitLifetimeType()` to enforce C++23 standard 
compliance on the target type.
  - Dual-mode: Supports an optional secondary boolean argument to bypass the 
strict implicit-lifetime trait check.
  - ExprConstant.cpp: Explicitly rejects compile-time evaluation of the 
intrinsic.
---
 clang/include/clang/Basic/Builtins.td         |   6 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   9 ++
 clang/include/clang/Sema/Sema.h               |   6 +
 clang/lib/AST/ExprConstant.cpp                |   7 ++
 clang/lib/Sema/SemaChecking.cpp               |  77 ++++++++++++-
 .../SemaCXX/builtin-start-lifetime-as.cpp     | 108 ++++++++++++++++++
 6 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/SemaCXX/builtin-start-lifetime-as.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 4a7eaeb3d353e..e387c990efdd6 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5883,3 +5883,9 @@ def CountedByRef : Builtin {
   let Attributes = [NoThrow, CustomTypeChecking];
   let Prototype = "int(...)";
 }
+
+def StartLifetimeAs : Builtin {
+  let Spellings = ["__builtin_start_lifetime_as"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void *(...)";
+}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c69b2ce3648f8..a7bb79207c354 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -14238,4 +14238,13 @@ def err_cuda_device_kernel_launch_not_supported
 def err_cuda_device_kernel_launch_require_rdc
     : Error<"kernel launch from __device__ or __global__ function requires "
             "relocatable device code (i.e. requires -fgpu-rdc)">;
+def err_start_lifetime_as_not_implicit : Error<
+  "type %0 is not an implicit-lifetime type; cannot start lifetime">;
+def err_builtin_start_lifetime_as_invalid_arg : Error<
+  "non-pointer argument to '__builtin_start_lifetime_as' is not allowed">;
+def note_constexpr_start_lifetime : Note<
+  "implicitly creating objects and dynamically altering lifetimes is not "
+  "supported during constant evalutaion">;
+def err_builtin_start_lifetime_as_vla : Error<
+  "variable length arrays are not supported in '__builtin_start_lifetime_as'">;
 } // end of sema component.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 72beac7526dc5..c94d2db54e279 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2893,6 +2893,12 @@ class Sema final : public SemaBase {
   bool BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall, unsigned ArgNum,
                                            unsigned ArgBits);
 
+  /// BuiltinStartLifetimeAs - Ensures the argument is a valid pointer to a
+  /// complete type. In strict mode (default), it also enforces C++23
+  /// implicit-lifetime constraints. Returns true on error, false on success or
+  /// if type-dependent.
+  bool BuiltinStartLifetimeAs(CallExpr *TheCall);
+
   /// Checks that a call expression's argument count is at least the desired
   /// number. This is useful when doing custom type-checking on a variadic
   /// function. Returns true on error.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3f3a80f5b77a3..50a02031fb0b2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10549,6 +10549,13 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
     return ZeroInitialization(E);
   }
 
+  case Builtin::BI__builtin_start_lifetime_as: {
+    // C++23 forbids evaluating lifetime-altering abstract machine magic inside
+    // a constant expression.
+    Info.FFDiag(E, diag::note_constexpr_start_lifetime);
+    return false;
+  }
+
   case Builtin::BImemcpy:
   case Builtin::BImemmove:
   case Builtin::BIwmemcpy:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 4706fa5d3cde0..15043defc459a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -41,6 +41,7 @@
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/UnresolvedSet.h"
 #include "clang/Basic/AddressSpaces.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/IdentifierTable.h"
@@ -3125,7 +3126,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     return BuiltinIsWithinLifetime(*this, TheCall);
   case Builtin::BI__builtin_trivially_relocate:
     return BuiltinTriviallyRelocate(*this, TheCall);
-
+  case Builtin::BI__builtin_start_lifetime_as:
+    if (BuiltinStartLifetimeAs(TheCall))
+      return ExprError();
+    break;
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_add_1:
   case Builtin::BI__sync_fetch_and_add_2:
@@ -17298,3 +17302,74 @@ void Sema::CheckTCBEnforcement(const SourceLocation 
CallExprLoc,
     }
   }
 }
+
+bool Sema::BuiltinStartLifetimeAs(CallExpr *Call) {
+  // Argument count: 1 (Strict) or 2 (Pointer, StrictnessFlag)
+  if (checkArgCountRange(Call, 1, 2))
+    return true;
+
+  Expr *PtrArg = Call->getArg(0);
+  QualType PtrType = PtrArg->getType();
+
+  const PointerType *PT = PtrType->getAs<PointerType>();
+  if (!PT) {
+    Diag(PtrArg->getExprLoc(), diag::err_builtin_start_lifetime_as_invalid_arg)
+        << PtrArg->getSourceRange();
+    return true;
+  }
+
+  QualType PointeeType = PT->getPointeeType();
+
+  // Variable Length Arrays are "complete types" in clang, but they lack a
+  // static size. GCC doesn't support variable-sized arrays in
+  // `std::start_lifetime_as`.
+  if (PointeeType->isVariableArrayType()) {
+    // We cannot use `err_vla_unsupported` because of some FormatDiagnostic
+    // issue
+    Diag(PtrArg->getExprLoc(), diag::err_builtin_start_lifetime_as_vla)
+        << PtrArg->getSourceRange();
+    return true;
+  }
+
+  if (Call->isTypeDependent() || Call->isValueDependent())
+    return false;
+
+  // Reject void and function pointers immediately.
+  if (PointeeType->isVoidType() || PointeeType->isFunctionType()) {
+    Diag(PtrArg->getExprLoc(), diag::err_start_lifetime_as_not_implicit)
+        << PointeeType << PtrArg->getSourceRange();
+    return true;
+  }
+
+  // Ensure the type is fully defined
+  if (RequireCompleteType(PtrArg->getExprLoc(), PointeeType,
+                          diag::err_incomplete_type))
+    return true;
+
+  bool IsStrict = true;
+  if (Call->getNumArgs() == 2) {
+    Expr *StrictArg = Call->getArg(1);
+
+    std::optional<llvm::APSInt> OptStrict =
+        StrictArg->getIntegerConstantExpr(Context);
+
+    if (!OptStrict) {
+      Diag(StrictArg->getExprLoc(), diag::err_expr_not_ice)
+          << 0 << StrictArg->getSourceRange();
+      return true;
+    }
+
+    IsStrict = OptStrict->getBoolValue();
+  }
+
+  // Note: If the user called `start_lifetime_as_array<int>`, the PointeeType 
is
+  // simply `int` so no need to unwrap the array.
+  if (IsStrict && !PointeeType->isImplicitLifetimeType()) {
+    Diag(PtrArg->getExprLoc(), diag::err_start_lifetime_as_not_implicit)
+        << PointeeType << PtrArg->getSourceRange();
+    return true;
+  }
+
+  Call->setType(PtrType);
+  return false;
+}
diff --git a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp 
b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
new file mode 100644
index 0000000000000..82286e60914aa
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+
+namespace InvalidArgs {
+    void test_non_pointers(int x) {
+        __builtin_start_lifetime_as(x); // expected-error {{non-pointer 
argument to '__builtin_start_lifetime_as' is not allowed}}
+        __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer 
argument to '__builtin_start_lifetime_as' is not allowed}}
+    }
+
+    void test_void_and_func(void *p, void (*f)()) {
+        __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not 
an implicit-lifetime type; cannot start lifetime}}
+        __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is 
not an implicit-lifetime type; cannot start lifetime}}
+    }
+
+    struct Incomplete; // expected-note {{forward declaration of 
'InvalidArgs::Incomplete'}}
+    void test_incomplete(Incomplete *p) {
+        __builtin_start_lifetime_as(p); // expected-error {{incomplete type 
'Incomplete' where a complete type is required}}
+    }
+
+    void test_vla(int n) { // expected-note {{declared here}}
+        int vla[n]; // expected-warning {{variable length arrays in C++ are a 
Clang extension}} \
+                    // expected-note {{function parameter 'n' with unknown 
value cannot be used in a constant expression}}
+        __builtin_start_lifetime_as(&vla); // expected-error {{variable length 
arrays are not supported in '__builtin_start_lifetime_as'}}
+    }
+} // namespace InvalidArgs
+
+namespace ImplicitLifetimeRules {
+    // Valid types
+    struct Trivial { int x; int y; };
+    struct TrivialArray { int arr[5]; };
+    union TrivialUnion { int a; float b; };
+
+    struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; };
+
+    // Invalid types
+    struct UserDtor { ~UserDtor() {} };
+    struct UserCopy { UserCopy(const UserCopy&); };
+    struct VirtualBase { virtual void f(); };
+
+    void test_valid_types(void* p) {
+        __builtin_start_lifetime_as((int*)p);
+        __builtin_start_lifetime_as((Trivial*)p);
+        __builtin_start_lifetime_as((TrivialArray*)p);
+        __builtin_start_lifetime_as((TrivialUnion*)p);
+        __builtin_start_lifetime_as((AggregateNoDtor*)p);
+
+        // Arrays of implicit-lifetime types are implicitly valid
+        __builtin_start_lifetime_as((int(*)[5])p);
+
+        // Arrays of non-implicit-lifetime types are also valid under C++23
+        __builtin_start_lifetime_as((UserDtor(*)[5])p);
+    }
+
+    void test_invalid_types(void *p) {
+    __builtin_start_lifetime_as((UserDtor*)p); // expected-error {{type 
'UserDtor' is not an implicit-lifetime type; cannot start lifetime}}
+    __builtin_start_lifetime_as((UserCopy*)p); // expected-error {{type 
'UserCopy' is not an implicit-lifetime type; cannot start lifetime}}
+    __builtin_start_lifetime_as((VirtualBase*)p); // expected-error {{type 
'VirtualBase' is not an implicit-lifetime type; cannot start lifetime}}
+  }
+} // namespace ImplicitLifetimeRules
+
+namespace StrictnessFlag {
+    struct NonTrivial { ~NonTrivial() {} };
+
+    void test_flag(NonTrivial* p, int runtime_flag) {
+        // defaults to strict
+        __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' 
is not an implicit-lifetime type}}
+
+        // explicit strict
+        __builtin_start_lifetime_as(p, true); // expected-error {{type 
'NonTrivial' is not an implicit-lifetime type}}
+    
+        // bypasses the implicit-lifetime check
+        __builtin_start_lifetime_as(p, false);
+
+        // Flag must be an ICE (Integer Constant Expression)
+        __builtin_start_lifetime_as(p, runtime_flag); // expected-error 
{{expression is not an integer constant expression}}
+    }
+} // namespace StrictnessFlag
+
+namespace Templates {
+  template <typename T>
+  T* test_dependent_type(void *p) {
+    // Should defer evaluation until instantiation
+    return __builtin_start_lifetime_as((T*)p); // expected-error {{type 
'Templates::std::string' is not an implicit-lifetime type}}
+  }
+
+  namespace std { struct string { ~string() {} }; }
+
+  void instantiate(void *p) {
+    test_dependent_type<int>(p);
+    test_dependent_type<std::string>(p); // expected-note {{in instantiation 
of function template specialization 
'Templates::test_dependent_type<Templates::std::string>' requested here}}
+  }
+} // namespace Templates
+
+namespace ConstexprEvaluation {
+  struct Trivial { int x; };
+
+  constexpr Trivial* test_constexpr_builtin(Trivial* p) {
+    return __builtin_start_lifetime_as(p); // expected-note {{subexpression 
not valid in a constant expression}}
+  }
+
+  constexpr bool test_eval() {
+    Trivial t{0};
+    Trivial* p = test_constexpr_builtin(&t); // expected-note {{in call to 
'test_constexpr_builtin(&t)'}}
+    return p != nullptr;
+  }
+
+  constexpr bool b = test_eval(); // expected-error {{constexpr variable 'b' 
must be initialized by a constant expression}} \
+                                  // expected-note {{in call to 'test_eval()'}}
+}

>From 1de2a8a4777d0d4ad893260e73e0390971a5ff7b Mon Sep 17 00:00:00 2001
From: Yash Verma <[email protected]>
Date: Fri, 8 May 2026 05:41:54 -0400
Subject: [PATCH 3/3] [Clang][CodeGen] Implement IR generation for
 __builtin_start_lifetime_as

This path implements the CodeGen lowering for the C++23 
`std::start_lifetime_as` intrinsic.

Key Changes:
  - CGBuiltin.cpp: Route `BI__builtin_start_lifetime_as` to share the existing 
`__builtin_launder` optimization barrier path.
---
 clang/lib/CodeGen/CGBuiltin.cpp               |   2 +
 .../CodeGenCXX/builtin-start-lifetime-as.cpp  |  42 +++++++
 .../SemaCXX/builtin-start-lifetime-as.cpp     | 114 +++++++++---------
 3 files changed, 101 insertions(+), 57 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 67de2a34f44ea..5d05ecad6a6c8 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -26,6 +26,7 @@
 #include "TargetInfo.h"
 #include "clang/AST/OSLog.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/IR/InlineAsm.h"
@@ -5180,6 +5181,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
 
     return RValue::get(nullptr);
   }
+  case Builtin::BI__builtin_start_lifetime_as:
   case Builtin::BI__builtin_launder: {
     const Expr *Arg = E->getArg(0);
     QualType ArgTy = Arg->getType()->getPointeeType();
diff --git a/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp 
b/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp
new file mode 100644
index 0000000000000..264ae6e4d4e5f
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++23 -emit-llvm -disable-llvm-passes -o - %s | 
FileCheck %s
+
+struct Trivial { int x; float y; };
+
+// CHECK-LABEL: define {{.*}}@_Z12test_trivialPv(
+// CHECK-NOT: call ptr @llvm.launder.invariant.group
+// CHECK: ret ptr
+Trivial* test_trivial(void* p) {
+  return __builtin_start_lifetime_as((Trivial*)p);
+}
+
+// CHECK-LABEL: define {{.*}}@_Z18test_array_trivialPv(
+// CHECK-NOT: call ptr @llvm.launder.invariant.group
+// CHECK: ret ptr
+int (*test_array_trivial(void* p))[5] {
+  return __builtin_start_lifetime_as((int(*)[5])p);
+}
+
+struct WithConst { const int x; };
+struct WithRef { int& x; };
+
+// CHECK-LABEL: define {{.*}}@_Z10test_constPv(
+// CHECK-NOT: call ptr @llvm.launder.invariant.group
+// CHECK: ret ptr
+WithConst* test_const(void* p) {
+  return __builtin_start_lifetime_as((WithConst*)p);
+}
+
+// CHECK-LABEL: define {{.*}}@_Z8test_refPv(
+// CHECK-NOT: call ptr @llvm.launder.invariant.group
+// CHECK: ret ptr
+WithRef* test_ref(void* p) {
+  return __builtin_start_lifetime_as((WithRef*)p);
+}
+
+// CHECK-LABEL: define {{.*}}@_Z20test_strict_flag_laxPv(
+// CHECK-NOT: call ptr @llvm.launder.invariant.group
+// CHECK: ret ptr
+WithConst* test_strict_flag_lax(void* p) {
+  // Pass 'false' to lax mode; CodeGen cleanly drops the second argument
+  return __builtin_start_lifetime_as((WithConst*)p, false);
+}
diff --git a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp 
b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
index 82286e60914aa..fec29a1945b67 100644
--- a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
+++ b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
@@ -1,56 +1,56 @@
 // RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
 
 namespace InvalidArgs {
-    void test_non_pointers(int x) {
-        __builtin_start_lifetime_as(x); // expected-error {{non-pointer 
argument to '__builtin_start_lifetime_as' is not allowed}}
-        __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer 
argument to '__builtin_start_lifetime_as' is not allowed}}
-    }
-
-    void test_void_and_func(void *p, void (*f)()) {
-        __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not 
an implicit-lifetime type; cannot start lifetime}}
-        __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is 
not an implicit-lifetime type; cannot start lifetime}}
-    }
-
-    struct Incomplete; // expected-note {{forward declaration of 
'InvalidArgs::Incomplete'}}
-    void test_incomplete(Incomplete *p) {
-        __builtin_start_lifetime_as(p); // expected-error {{incomplete type 
'Incomplete' where a complete type is required}}
-    }
-
-    void test_vla(int n) { // expected-note {{declared here}}
-        int vla[n]; // expected-warning {{variable length arrays in C++ are a 
Clang extension}} \
-                    // expected-note {{function parameter 'n' with unknown 
value cannot be used in a constant expression}}
-        __builtin_start_lifetime_as(&vla); // expected-error {{variable length 
arrays are not supported in '__builtin_start_lifetime_as'}}
-    }
-} // namespace InvalidArgs
-
-namespace ImplicitLifetimeRules {
-    // Valid types
-    struct Trivial { int x; int y; };
-    struct TrivialArray { int arr[5]; };
-    union TrivialUnion { int a; float b; };
-
-    struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; };
+  void test_non_pointers(int x) {
+    __builtin_start_lifetime_as(x); // expected-error {{non-pointer argument 
to '__builtin_start_lifetime_as' is not allowed}}
+    __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer 
argument to '__builtin_start_lifetime_as' is not allowed}}
+  }
 
-    // Invalid types
-    struct UserDtor { ~UserDtor() {} };
-    struct UserCopy { UserCopy(const UserCopy&); };
-    struct VirtualBase { virtual void f(); };
+  void test_void_and_func(void *p, void (*f)()) {
+    __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not an 
implicit-lifetime type; cannot start lifetime}}
+    __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is not 
an implicit-lifetime type; cannot start lifetime}}
+  }
 
-    void test_valid_types(void* p) {
-        __builtin_start_lifetime_as((int*)p);
-        __builtin_start_lifetime_as((Trivial*)p);
-        __builtin_start_lifetime_as((TrivialArray*)p);
-        __builtin_start_lifetime_as((TrivialUnion*)p);
-        __builtin_start_lifetime_as((AggregateNoDtor*)p);
+  struct Incomplete; // expected-note {{forward declaration of 
'InvalidArgs::Incomplete'}}
+  void test_incomplete(Incomplete *p) {
+    __builtin_start_lifetime_as(p); // expected-error {{incomplete type 
'Incomplete' where a complete type is required}}
+  }
 
-        // Arrays of implicit-lifetime types are implicitly valid
-        __builtin_start_lifetime_as((int(*)[5])p);
+  void test_vla(int n) { // expected-note {{declared here}}
+    int vla[n]; // expected-warning {{variable length arrays in C++ are a 
Clang extension}} \
+                // expected-note {{function parameter 'n' with unknown value 
cannot be used in a constant expression}}
+    __builtin_start_lifetime_as(&vla); // expected-error {{variable length 
arrays are not supported in '__builtin_start_lifetime_as'}}
+  }
+} // namespace InvalidArgs
 
-        // Arrays of non-implicit-lifetime types are also valid under C++23
-        __builtin_start_lifetime_as((UserDtor(*)[5])p);
-    }
+namespace ImplicitLifetimeRules {
+  // Valid types
+  struct Trivial { int x; int y; };
+  struct TrivialArray { int arr[5]; };
+  union TrivialUnion { int a; float b; };
+
+  struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; };
+
+  // Invalid types
+  struct UserDtor { ~UserDtor() {} };
+  struct UserCopy { UserCopy(const UserCopy&); };
+  struct VirtualBase { virtual void f(); };
+
+  void test_valid_types(void* p) {
+    __builtin_start_lifetime_as((int*)p);
+    __builtin_start_lifetime_as((Trivial*)p);
+    __builtin_start_lifetime_as((TrivialArray*)p);
+    __builtin_start_lifetime_as((TrivialUnion*)p);
+    __builtin_start_lifetime_as((AggregateNoDtor*)p);
+
+    // Arrays of implicit-lifetime types are implicitly valid
+    __builtin_start_lifetime_as((int(*)[5])p);
+
+    // Arrays of non-implicit-lifetime types are also valid under C++23
+    __builtin_start_lifetime_as((UserDtor(*)[5])p);
+  }
 
-    void test_invalid_types(void *p) {
+  void test_invalid_types(void *p) {
     __builtin_start_lifetime_as((UserDtor*)p); // expected-error {{type 
'UserDtor' is not an implicit-lifetime type; cannot start lifetime}}
     __builtin_start_lifetime_as((UserCopy*)p); // expected-error {{type 
'UserCopy' is not an implicit-lifetime type; cannot start lifetime}}
     __builtin_start_lifetime_as((VirtualBase*)p); // expected-error {{type 
'VirtualBase' is not an implicit-lifetime type; cannot start lifetime}}
@@ -58,21 +58,21 @@ namespace ImplicitLifetimeRules {
 } // namespace ImplicitLifetimeRules
 
 namespace StrictnessFlag {
-    struct NonTrivial { ~NonTrivial() {} };
+  struct NonTrivial { ~NonTrivial() {} };
 
-    void test_flag(NonTrivial* p, int runtime_flag) {
-        // defaults to strict
-        __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' 
is not an implicit-lifetime type}}
+  void test_flag(NonTrivial* p, int runtime_flag) {
+    // defaults to strict
+    __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' is 
not an implicit-lifetime type}}
 
-        // explicit strict
-        __builtin_start_lifetime_as(p, true); // expected-error {{type 
'NonTrivial' is not an implicit-lifetime type}}
+    // explicit strict
+    __builtin_start_lifetime_as(p, true); // expected-error {{type 
'NonTrivial' is not an implicit-lifetime type}}
     
-        // bypasses the implicit-lifetime check
-        __builtin_start_lifetime_as(p, false);
+    // bypasses the implicit-lifetime check
+    __builtin_start_lifetime_as(p, false);
 
-        // Flag must be an ICE (Integer Constant Expression)
-        __builtin_start_lifetime_as(p, runtime_flag); // expected-error 
{{expression is not an integer constant expression}}
-    }
+    // Flag must be an ICE (Integer Constant Expression)
+    __builtin_start_lifetime_as(p, runtime_flag); // expected-error 
{{expression is not an integer constant expression}}
+  }
 } // namespace StrictnessFlag
 
 namespace Templates {
@@ -105,4 +105,4 @@ namespace ConstexprEvaluation {
 
   constexpr bool b = test_eval(); // expected-error {{constexpr variable 'b' 
must be initialized by a constant expression}} \
                                   // expected-note {{in call to 'test_eval()'}}
-}
+} // namespace ConstexprEvaluation

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to