[libcxx-commits] [clang] [libcxx] [Clang][libc++] Implement C++23 std::start_lifetime_as (P2590R2, P2679R2) (PR #196286)

Yash Verma via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 7 04:12:11 PDT 2026


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

This PR implements the compiler built-in and standard library interfaces for C++23 Explicit Lifetime Management as proposed in [P2590R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2590r2.pdf) and fixed for arrays in [P2679R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2679r2.pdf).

This PR implements a `__builtin_start_lifetime_as((T*)ptr)` builtin which is used by `std::start_lifetime_as<T>(ptr)` and `std::start_lifetime_as_array<T>(ptr, n)`.

I'm sorry if there are any mistakes in the pull request as this my first PR ever. Also, the commit timestamp is a bit messed up because I forgot to run `git clang-format` hook before pushing a few commits to the remote host.

>From 40693914c62d5f86556fccf60fb2bd2f84dec800 Mon Sep 17 00:00:00 2001
From: Yash Verma <yashverma056 at proton.me>
Date: Thu, 7 May 2026 01:36:11 -0400
Subject: [PATCH 1/4] [Clang][AST] Implement isImplicitLifetimetype()

Adds `bool isImplicitLifetimeType() const` to `clang::Type` to map the C++20 [basic.types.general] rules to the AST. This methods recursively unwraps arrays and validates CXXRecordDecls for trivial constructors and destructors.
---
 clang/include/clang/AST/TypeBase.h | 689 +++++++++++++----------------
 clang/lib/AST/Type.cpp             |  51 +++
 2 files changed, 355 insertions(+), 385 deletions(-)

diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index b2887bcc36246..2e20cecd8ae1d 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -71,43 +71,37 @@ class TemplateParameterList;
 class Type;
 class Attr;
 
-enum {
-  TypeAlignmentInBits = 4,
-  TypeAlignment = 1 << TypeAlignmentInBits
-};
+enum { TypeAlignmentInBits = 4, TypeAlignment = 1 << TypeAlignmentInBits };
 
 namespace serialization {
-  template <class T> class AbstractTypeReader;
-  template <class T> class AbstractTypeWriter;
-}
+template <class T> class AbstractTypeReader;
+template <class T> class AbstractTypeWriter;
+} // namespace serialization
 
 } // namespace clang
 
 namespace llvm {
 
-  template <typename T>
-  struct PointerLikeTypeTraits;
-  template<>
-  struct PointerLikeTypeTraits< ::clang::Type*> {
-    static inline void *getAsVoidPointer(::clang::Type *P) { return P; }
+template <typename T> struct PointerLikeTypeTraits;
+template <> struct PointerLikeTypeTraits<::clang::Type *> {
+  static inline void *getAsVoidPointer(::clang::Type *P) { return P; }
 
-    static inline ::clang::Type *getFromVoidPointer(void *P) {
-      return static_cast< ::clang::Type*>(P);
-    }
+  static inline ::clang::Type *getFromVoidPointer(void *P) {
+    return static_cast<::clang::Type *>(P);
+  }
 
-    static constexpr int NumLowBitsAvailable = clang::TypeAlignmentInBits;
-  };
+  static constexpr int NumLowBitsAvailable = clang::TypeAlignmentInBits;
+};
 
-  template<>
-  struct PointerLikeTypeTraits< ::clang::ExtQuals*> {
-    static inline void *getAsVoidPointer(::clang::ExtQuals *P) { return P; }
+template <> struct PointerLikeTypeTraits<::clang::ExtQuals *> {
+  static inline void *getAsVoidPointer(::clang::ExtQuals *P) { return P; }
 
-    static inline ::clang::ExtQuals *getFromVoidPointer(void *P) {
-      return static_cast< ::clang::ExtQuals*>(P);
-    }
+  static inline ::clang::ExtQuals *getFromVoidPointer(void *P) {
+    return static_cast<::clang::ExtQuals *>(P);
+  }
 
-    static constexpr int NumLowBitsAvailable = clang::TypeAlignmentInBits;
-  };
+  static constexpr int NumLowBitsAvailable = clang::TypeAlignmentInBits;
+};
 
 } // namespace llvm
 
@@ -339,11 +333,7 @@ class Qualifiers {
     CVRMask = Const | Volatile | Restrict
   };
 
-  enum GC {
-    GCNone = 0,
-    Weak,
-    Strong
-  };
+  enum GC { GCNone = 0, Weak, Strong };
 
   enum ObjCLifetime {
     /// There is no lifetime qualification on this type.
@@ -496,9 +486,7 @@ class Qualifiers {
     assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits");
     Mask &= ~static_cast<uint64_t>(mask);
   }
-  void removeCVRQualifiers() {
-    removeCVRQualifiers(CVRMask);
-  }
+  void removeCVRQualifiers() { removeCVRQualifiers(CVRMask); }
   void addCVRQualifiers(unsigned mask) {
     assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits");
     Mask |= mask;
@@ -509,9 +497,7 @@ class Qualifiers {
   }
 
   bool hasUnaligned() const { return Mask & UMask; }
-  void setUnaligned(bool flag) {
-    Mask = (Mask & ~UMask) | (flag ? UMask : 0);
-  }
+  void setUnaligned(bool flag) { Mask = (Mask & ~UMask) | (flag ? UMask : 0); }
   void removeUnaligned() { Mask &= ~UMask; }
   void addUnaligned() { Mask |= UMask; }
 
@@ -590,8 +576,8 @@ class Qualifiers {
   }
   void setAddressSpace(LangAS space) {
     assert((unsigned)space <= MaxAddressSpace);
-    Mask = (Mask & ~AddressSpaceMask)
-         | (((uint32_t) space) << AddressSpaceShift);
+    Mask =
+        (Mask & ~AddressSpaceMask) | (((uint32_t)space) << AddressSpaceShift);
   }
   void removeAddressSpace() { setAddressSpace(LangAS::Default); }
   void addAddressSpace(LangAS space) {
@@ -625,9 +611,7 @@ class Qualifiers {
     assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits");
     Mask &= ~static_cast<uint64_t>(mask);
   }
-  void removeFastQualifiers() {
-    removeFastQualifiers(FastMask);
-  }
+  void removeFastQualifiers() { removeFastQualifiers(FastMask); }
   void addFastQualifiers(unsigned mask) {
     assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits");
     Mask |= mask;
@@ -687,12 +671,12 @@ class Qualifiers {
   /// Add the qualifiers from the given set to this set, given that
   /// they don't conflict.
   void addConsistentQualifiers(Qualifiers qs) {
-    assert(getAddressSpace() == qs.getAddressSpace() ||
-           !hasAddressSpace() || !qs.hasAddressSpace());
-    assert(getObjCGCAttr() == qs.getObjCGCAttr() ||
-           !hasObjCGCAttr() || !qs.hasObjCGCAttr());
-    assert(getObjCLifetime() == qs.getObjCLifetime() ||
-           !hasObjCLifetime() || !qs.hasObjCLifetime());
+    assert(getAddressSpace() == qs.getAddressSpace() || !hasAddressSpace() ||
+           !qs.hasAddressSpace());
+    assert(getObjCGCAttr() == qs.getObjCGCAttr() || !hasObjCGCAttr() ||
+           !qs.hasObjCGCAttr());
+    assert(getObjCLifetime() == qs.getObjCLifetime() || !hasObjCLifetime() ||
+           !qs.hasObjCLifetime());
     assert(!hasPointerAuth() || !qs.hasPointerAuth() ||
            getPointerAuth() == qs.getPointerAuth());
     Mask |= qs.Mask;
@@ -880,7 +864,7 @@ struct SplitQualType {
   SplitQualType getSingleStepDesugaredType() const; // end of this file
 
   // Make std::tie work.
-  std::pair<const Type *,Qualifiers> asPair() const {
+  std::pair<const Type *, Qualifiers> asPair() const {
     return std::pair<const Type *, Qualifiers>(Ty, Quals);
   }
 
@@ -939,7 +923,8 @@ class QualType {
 
   // Thankfully, these are efficiently composable.
   llvm::PointerIntPair<llvm::PointerUnion<const Type *, const ExtQuals *>,
-                       Qualifiers::FastWidth> Value;
+                       Qualifiers::FastWidth>
+      Value;
 
   const ExtQuals *getExtQualsUnsafe() const {
     return cast<const ExtQuals *>(Value.getPointer());
@@ -953,7 +938,7 @@ class QualType {
     assert(!isNull() && "Cannot retrieve a NULL type pointer");
     auto CommonPtrVal = reinterpret_cast<uintptr_t>(Value.getOpaqueValue());
     CommonPtrVal &= ~(uintptr_t)((1 << TypeAlignmentInBits) - 1);
-    return reinterpret_cast<ExtQualsTypeCommonBase*>(CommonPtrVal);
+    return reinterpret_cast<ExtQualsTypeCommonBase *>(CommonPtrVal);
   }
 
 public:
@@ -985,25 +970,19 @@ class QualType {
 
   static QualType getFromOpaquePtr(const void *Ptr) {
     QualType T;
-    T.Value.setFromOpaqueValue(const_cast<void*>(Ptr));
+    T.Value.setFromOpaqueValue(const_cast<void *>(Ptr));
     return T;
   }
 
-  const Type &operator*() const {
-    return *getTypePtr();
-  }
+  const Type &operator*() const { return *getTypePtr(); }
 
-  const Type *operator->() const {
-    return getTypePtr();
-  }
+  const Type *operator->() const { return getTypePtr(); }
 
   bool isCanonical() const;
   bool isCanonicalAsParam() const;
 
   /// Return true if this QualType doesn't point to a type yet.
-  bool isNull() const {
-    return Value.getPointer().isNull();
-  }
+  bool isNull() const { return Value.getPointer().isNull(); }
 
   // Determines if a type can form `T&`.
   bool isReferenceable() const;
@@ -1086,15 +1065,13 @@ class QualType {
   /// Retrieve the set of CVR (const-volatile-restrict) qualifiers
   /// local to this particular QualType instance, not including any qualifiers
   /// acquired through typedefs or other sugar.
-  unsigned getLocalCVRQualifiers() const {
-    return getLocalFastQualifiers();
-  }
+  unsigned getLocalCVRQualifiers() const { return getLocalFastQualifiers(); }
 
   /// Retrieve the set of CVR (const-volatile-restrict) qualifiers
   /// applied to this type.
   unsigned getCVRQualifiers() const;
 
-  bool isConstant(const ASTContext& Ctx) const {
+  bool isConstant(const ASTContext &Ctx) const {
     return QualType::isConstant(*this, Ctx);
   }
 
@@ -1164,25 +1141,17 @@ class QualType {
   // easily added.
 
   /// Add the `const` type qualifier to this QualType.
-  void addConst() {
-    addFastQualifiers(Qualifiers::Const);
-  }
-  QualType withConst() const {
-    return withFastQualifiers(Qualifiers::Const);
-  }
+  void addConst() { addFastQualifiers(Qualifiers::Const); }
+  QualType withConst() const { return withFastQualifiers(Qualifiers::Const); }
 
   /// Add the `volatile` type qualifier to this QualType.
-  void addVolatile() {
-    addFastQualifiers(Qualifiers::Volatile);
-  }
+  void addVolatile() { addFastQualifiers(Qualifiers::Volatile); }
   QualType withVolatile() const {
     return withFastQualifiers(Qualifiers::Volatile);
   }
 
   /// Add the `restrict` qualifier to this QualType.
-  void addRestrict() {
-    addFastQualifiers(Qualifiers::Restrict);
-  }
+  void addRestrict() { addFastQualifiers(Qualifiers::Restrict); }
   QualType withRestrict() const {
     return withFastQualifiers(Qualifiers::Restrict);
   }
@@ -1192,8 +1161,8 @@ class QualType {
   }
 
   void addFastQualifiers(unsigned TQs) {
-    assert(!(TQs & ~Qualifiers::FastMask)
-           && "non-fast qualifier bits set in mask!");
+    assert(!(TQs & ~Qualifiers::FastMask) &&
+           "non-fast qualifier bits set in mask!");
     Value.setInt(Value.getInt() | TQs);
   }
 
@@ -1360,9 +1329,8 @@ class QualType {
     return print(split.Ty, split.Quals, OS, policy, PlaceHolder, Indentation);
   }
 
-  static void print(const Type *ty, Qualifiers qs,
-                    raw_ostream &OS, const PrintingPolicy &policy,
-                    const Twine &PlaceHolder,
+  static void print(const Type *ty, Qualifiers qs, raw_ostream &OS,
+                    const PrintingPolicy &policy, const Twine &PlaceHolder,
                     unsigned Indentation = 0);
 
   void getAsStringInternal(std::string &Str,
@@ -1436,14 +1404,10 @@ class QualType {
   inline Qualifiers::GC getObjCGCAttr() const;
 
   /// true when Type is objc's weak.
-  bool isObjCGCWeak() const {
-    return getObjCGCAttr() == Qualifiers::Weak;
-  }
+  bool isObjCGCWeak() const { return getObjCGCAttr() == Qualifiers::Weak; }
 
   /// true when Type is objc's strong.
-  bool isObjCGCStrong() const {
-    return getObjCGCAttr() == Qualifiers::Strong;
-  }
+  bool isObjCGCStrong() const { return getObjCGCAttr() == Qualifiers::Strong; }
 
   /// Returns lifetime attribute of this type.
   Qualifiers::ObjCLifetime getObjCLifetime() const {
@@ -1599,8 +1563,7 @@ class QualType {
   /// \param context The context in which the subject type was written.
   ///
   /// \returns the resulting type.
-  QualType substObjCTypeArgs(ASTContext &ctx,
-                             ArrayRef<QualType> typeArgs,
+  QualType substObjCTypeArgs(ASTContext &ctx, ArrayRef<QualType> typeArgs,
                              ObjCSubstitutionContext context) const;
 
   /// Substitute type arguments from an object type for the Objective-C type
@@ -1623,8 +1586,7 @@ class QualType {
   ///
   /// \returns the subject type after replacing all of the Objective-C type
   /// parameters with their corresponding arguments.
-  QualType substObjCMemberType(QualType objectType,
-                               const DeclContext *dc,
+  QualType substObjCMemberType(QualType objectType, const DeclContext *dc,
                                ObjCSubstitutionContext context) const;
 
   /// Strip Objective-C "__kindof" types from the given type.
@@ -1641,7 +1603,7 @@ class QualType {
   // These methods are implemented in a separate translation unit;
   // "static"-ize them to avoid creating temporary QualTypes in the
   // caller.
-  static bool isConstant(QualType T, const ASTContext& Ctx);
+  static bool isConstant(QualType T, const ASTContext &Ctx);
   static QualType getDesugaredType(QualType T, const ASTContext &Context);
   static SplitQualType getSplitDesugaredType(QualType T);
   static SplitQualType getSplitUnqualifiedTypeImpl(QualType type);
@@ -1651,7 +1613,8 @@ class QualType {
   static DestructionKind isDestructedTypeImpl(QualType type);
 
   /// Check if \param RD is or contains a non-trivial C union.
-  static bool hasNonTrivialToPrimitiveDefaultInitializeCUnion(const RecordDecl *RD);
+  static bool
+  hasNonTrivialToPrimitiveDefaultInitializeCUnion(const RecordDecl *RD);
   static bool hasNonTrivialToPrimitiveDestructCUnion(const RecordDecl *RD);
   static bool hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD);
 };
@@ -1664,7 +1627,7 @@ namespace llvm {
 
 /// Implement simplify_type for QualType, so that we can dyn_cast from QualType
 /// to a specific Type class.
-template<> struct simplify_type< ::clang::QualType> {
+template <> struct simplify_type<::clang::QualType> {
   using SimpleType = const ::clang::Type *;
 
   static SimpleType getSimplifiedValue(::clang::QualType Val) {
@@ -1673,8 +1636,7 @@ template<> struct simplify_type< ::clang::QualType> {
 };
 
 // Teach SmallPtrSet that QualType is "basically a pointer".
-template<>
-struct PointerLikeTypeTraits<clang::QualType> {
+template <> struct PointerLikeTypeTraits<clang::QualType> {
   static inline void *getAsVoidPointer(clang::QualType P) {
     return P.getAsOpaquePtr();
   }
@@ -1750,10 +1712,10 @@ class alignas(TypeAlignment) ExtQuals : public ExtQualsTypeCommonBase,
       : ExtQualsTypeCommonBase(baseType,
                                canon.isNull() ? QualType(this_(), 0) : canon),
         Quals(quals) {
-    assert(Quals.hasNonFastQualifiers()
-           && "ExtQuals created with no fast qualifiers");
-    assert(!Quals.hasFastQualifiers()
-           && "ExtQuals created with fast qualifiers");
+    assert(Quals.hasNonFastQualifiers() &&
+           "ExtQuals created with no fast qualifiers");
+    assert(!Quals.hasFastQualifiers() &&
+           "ExtQuals created with fast qualifiers");
   }
 
   Qualifiers getQualifiers() const { return Quals; }
@@ -1776,8 +1738,7 @@ class alignas(TypeAlignment) ExtQuals : public ExtQualsTypeCommonBase,
     Profile(ID, getBaseType(), Quals);
   }
 
-  static void Profile(llvm::FoldingSetNodeID &ID,
-                      const Type *BaseType,
+  static void Profile(llvm::FoldingSetNodeID &ID, const Type *BaseType,
                       Qualifiers Quals) {
     assert(!Quals.hasFastQualifiers() && "fast qualifiers in ExtQuals hash!");
     ID.AddPointer(BaseType);
@@ -1908,9 +1869,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     LLVM_PREFERRED_TYPE(bool)
     mutable unsigned FromAST : 1;
 
-    bool isCacheValid() const {
-      return CacheValid;
-    }
+    bool isCacheValid() const { return CacheValid; }
 
     Linkage getLinkage() const {
       assert(isCacheValid() && "getting linkage from invalid cache");
@@ -2392,9 +2351,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   template <class T> friend class TypePropertyCache;
 
   /// Set whether this type comes from an AST file.
-  void setFromAST(bool V = true) const {
-    TypeBits.FromAST = V;
-  }
+  void setFromAST(bool V = true) const { TypeBits.FromAST = V; }
 
 protected:
   friend class ASTContext;
@@ -2533,11 +2490,13 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   /// class), will be set to the declaration.
   bool isIncompleteType(NamedDecl **Def = nullptr) const;
 
+  /// Determines if this type is an implicit-lifetime type as defined in C++20
+  /// [basic.types.general]. Returns true if the type is implicit lifetime type.
+  bool isImplicitLifetimeType() const;
+
   /// Return true if this is an incomplete or object
   /// type, in other words, not a function type.
-  bool isIncompleteOrObjectType() const {
-    return !isFunctionType();
-  }
+  bool isIncompleteOrObjectType() const { return !isFunctionType(); }
 
   /// \returns True if the type is incomplete and it is also a type that
   /// cannot be completed by a later type definition.
@@ -2602,7 +2561,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
 
   /// isIntegerType() does *not* include complex integers (a GCC extension).
   /// isComplexIntegerType() can be used to test for complex integers.
-  bool isIntegerType() const;     // C99 6.2.5p17 (int, char, bool, enum)
+  bool isIntegerType() const; // C99 6.2.5p17 (int, char, bool, enum)
   bool isEnumeralType() const;
 
   /// Determine whether this type is a scoped enumeration type.
@@ -2628,21 +2587,21 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isRealFloatingType() const; // C99 6.2.5p10 (float, double, long double)
   /// isComplexType() does *not* include complex integers (a GCC extension).
   /// isComplexIntegerType() can be used to test for complex integers.
-  bool isComplexType() const;      // C99 6.2.5p11 (complex)
-  bool isAnyComplexType() const;   // C99 6.2.5p11 (complex) + Complex Int.
-  bool isFloatingType() const;     // C99 6.2.5p11 (real floating + complex)
-  bool isHalfType() const;         // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half)
-  bool isFloat16Type() const;      // C11 extension ISO/IEC TS 18661
+  bool isComplexType() const;    // C99 6.2.5p11 (complex)
+  bool isAnyComplexType() const; // C99 6.2.5p11 (complex) + Complex Int.
+  bool isFloatingType() const;   // C99 6.2.5p11 (real floating + complex)
+  bool isHalfType() const;       // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half)
+  bool isFloat16Type() const;    // C11 extension ISO/IEC TS 18661
   bool isFloat32Type() const;
   bool isDoubleType() const;
   bool isBFloat16Type() const;
   bool isMFloat8Type() const;
   bool isFloat128Type() const;
   bool isIbm128Type() const;
-  bool isRealType() const;         // C99 6.2.5p17 (real floating + integer)
-  bool isArithmeticType() const;   // C99 6.2.5p18 (integer + floating)
-  bool isVoidType() const;         // C99 6.2.5p19
-  bool isScalarType() const;       // C99 6.2.5p21 (arithmetic + pointers)
+  bool isRealType() const;       // C99 6.2.5p17 (real floating + integer)
+  bool isArithmeticType() const; // C99 6.2.5p18 (integer + floating)
+  bool isVoidType() const;       // C99 6.2.5p19
+  bool isScalarType() const;     // C99 6.2.5p21 (arithmetic + pointers)
   bool isAggregateType() const;
   bool isFundamentalType() const;
   bool isCompoundType() const;
@@ -2657,7 +2616,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isSignableType(const ASTContext &Ctx) const;
   bool isSignablePointerType() const;
   bool isSignableIntegerType(const ASTContext &Ctx) const;
-  bool isAnyPointerType() const;   // Any C pointer or ObjC object pointer
+  bool isAnyPointerType() const; // Any C pointer or ObjC object pointer
   bool isCountAttributedType() const;
   bool isCFIUncheckedCalleeFunctionType() const;
   bool hasPointeeToCFIUncheckedCalleeFunctionType() const;
@@ -2686,33 +2645,35 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isInterfaceType() const;
   bool isStructureOrClassType() const;
   bool isUnionType() const;
-  bool isComplexIntegerType() const;            // GCC _Complex integer type.
-  bool isVectorType() const;                    // GCC vector type.
-  bool isExtVectorType() const;                 // Extended vector type.
-  bool isExtVectorBoolType() const;             // Extended vector type with bool element.
+  bool isComplexIntegerType() const; // GCC _Complex integer type.
+  bool isVectorType() const;         // GCC vector type.
+  bool isExtVectorType() const;      // Extended vector type.
+  bool isExtVectorBoolType() const;  // Extended vector type with bool element.
   bool isConstantMatrixBoolType() const; // Matrix type with bool element.
   // Extended vector type with bool element that is packed. HLSL doesn't pack
   // its bool vectors.
   bool isPackedVectorBoolType(const ASTContext &ctx) const;
   bool isSubscriptableVectorType() const;
-  bool isMatrixType() const;                    // Matrix type.
-  bool isConstantMatrixType() const;            // Constant matrix type.
-  bool isOverflowBehaviorType() const;          // Overflow behavior type.
-  bool isDependentAddressSpaceType() const;     // value-dependent address space qualifier
-  bool isObjCObjectPointerType() const;         // pointer to ObjC object
-  bool isObjCRetainableType() const;            // ObjC object or block pointer
-  bool isObjCLifetimeType() const;              // (array of)* retainable type
-  bool isObjCIndirectLifetimeType() const;      // (pointer to)* lifetime type
-  bool isObjCNSObjectType() const;              // __attribute__((NSObject))
-  bool isObjCIndependentClassType() const;      // __attribute__((objc_independent_class))
+  bool isMatrixType() const;           // Matrix type.
+  bool isConstantMatrixType() const;   // Constant matrix type.
+  bool isOverflowBehaviorType() const; // Overflow behavior type.
+  bool isDependentAddressSpaceType()
+      const; // value-dependent address space qualifier
+  bool isObjCObjectPointerType() const;    // pointer to ObjC object
+  bool isObjCRetainableType() const;       // ObjC object or block pointer
+  bool isObjCLifetimeType() const;         // (array of)* retainable type
+  bool isObjCIndirectLifetimeType() const; // (pointer to)* lifetime type
+  bool isObjCNSObjectType() const;         // __attribute__((NSObject))
+  bool
+  isObjCIndependentClassType() const; // __attribute__((objc_independent_class))
   // FIXME: change this to 'raw' interface type, so we can used 'interface' type
   // for the common case.
-  bool isObjCObjectType() const;                // NSString or typeof(*(id)0)
-  bool isObjCQualifiedInterfaceType() const;    // NSString<foo>
-  bool isObjCQualifiedIdType() const;           // id<foo>
-  bool isObjCQualifiedClassType() const;        // Class<foo>
+  bool isObjCObjectType() const;             // NSString or typeof(*(id)0)
+  bool isObjCQualifiedInterfaceType() const; // NSString<foo>
+  bool isObjCQualifiedIdType() const;        // id<foo>
+  bool isObjCQualifiedClassType() const;     // Class<foo>
   bool isObjCObjectOrInterfaceType() const;
-  bool isObjCIdType() const;                    // id
+  bool isObjCIdType() const; // id
   bool isDecltypeType() const;
   /// Was this type written with the special inert-in-ARC __unsafe_unretained
   /// qualifier?
@@ -2734,7 +2695,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isObjCIdOrObjectKindOfType(const ASTContext &ctx,
                                   const ObjCObjectType *&bound) const;
 
-  bool isObjCClassType() const;                 // Class
+  bool isObjCClassType() const; // Class
 
   /// Whether the type is Objective-C 'Class' or a __kindof type of an
   /// Class type, e.g., __kindof Class <NSCopying>.
@@ -2745,47 +2706,46 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isObjCClassOrClassKindOfType() const;
 
   bool isBlockCompatibleObjCPointerType(ASTContext &ctx) const;
-  bool isObjCSelType() const;                 // Class
-  bool isObjCBuiltinType() const;               // 'id' or 'Class'
+  bool isObjCSelType() const;     // Class
+  bool isObjCBuiltinType() const; // 'id' or 'Class'
   bool isObjCARCBridgableType() const;
   bool isCARCBridgableType() const;
-  bool isTemplateTypeParmType() const;          // C++ template type parameter
-  bool isNullPtrType() const;                   // C++11 std::nullptr_t or
-                                                // C23   nullptr_t
-  bool isNothrowT() const;                      // C++   std::nothrow_t
-  bool isAlignValT() const;                     // C++17 std::align_val_t
-  bool isStdByteType() const;                   // C++17 std::byte
-  bool isAtomicType() const;                    // C11 _Atomic()
-  bool isUndeducedAutoType() const;             // C++11 auto or
-                                                // C++14 decltype(auto)
-  bool isTypedefNameType() const;               // typedef or alias template
-
-#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
+  bool isTemplateTypeParmType() const; // C++ template type parameter
+  bool isNullPtrType() const;          // C++11 std::nullptr_t or
+                                       // C23   nullptr_t
+  bool isNothrowT() const;             // C++   std::nothrow_t
+  bool isAlignValT() const;            // C++17 std::align_val_t
+  bool isStdByteType() const;          // C++17 std::byte
+  bool isAtomicType() const;           // C11 _Atomic()
+  bool isUndeducedAutoType() const;    // C++11 auto or
+                                       // C++14 decltype(auto)
+  bool isTypedefNameType() const;      // typedef or alias template
+
+#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   bool is##Id##Type() const;
 #include "clang/Basic/OpenCLImageTypes.def"
 
-  bool isImageType() const;                     // Any OpenCL image type
+  bool isImageType() const; // Any OpenCL image type
 
-  bool isSamplerT() const;                      // OpenCL sampler_t
-  bool isEventT() const;                        // OpenCL event_t
-  bool isClkEventT() const;                     // OpenCL clk_event_t
-  bool isQueueT() const;                        // OpenCL queue_t
-  bool isReserveIDT() const;                    // OpenCL reserve_id_t
+  bool isSamplerT() const;   // OpenCL sampler_t
+  bool isEventT() const;     // OpenCL event_t
+  bool isClkEventT() const;  // OpenCL clk_event_t
+  bool isQueueT() const;     // OpenCL queue_t
+  bool isReserveIDT() const; // OpenCL reserve_id_t
 
-#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
-  bool is##Id##Type() const;
+#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) bool is##Id##Type() const;
 #include "clang/Basic/OpenCLExtensionTypes.def"
   // Type defined in cl_intel_device_side_avc_motion_estimation OpenCL extension
   bool isOCLIntelSubgroupAVCType() const;
-  bool isOCLExtOpaqueType() const;              // Any OpenCL extension type
+  bool isOCLExtOpaqueType() const; // Any OpenCL extension type
 
-  bool isPipeType() const;                      // OpenCL pipe type
-  bool isBitIntType() const;                    // Bit-precise integer type
-  bool isOpenCLSpecificType() const;            // Any OpenCL specific type
+  bool isPipeType() const;           // OpenCL pipe type
+  bool isBitIntType() const;         // Bit-precise integer type
+  bool isOpenCLSpecificType() const; // Any OpenCL specific type
 
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) bool is##Id##Type() const;
 #include "clang/Basic/HLSLIntangibleTypes.def"
-  bool isHLSLSpecificType() const; // Any HLSL specific type
+  bool isHLSLSpecificType() const;          // Any HLSL specific type
   bool isHLSLBuiltinIntangibleType() const; // Any HLSL builtin intangible type
   bool isHLSLAttributedResourceType() const;
   bool isHLSLInlineSpirvType() const;
@@ -3171,9 +3131,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
 
   const char *getTypeClassName() const;
 
-  QualType getCanonicalTypeInternal() const {
-    return CanonicalType;
-  }
+  QualType getCanonicalTypeInternal() const { return CanonicalType; }
 
   CanQualType getCanonicalTypeUnqualified() const; // in CanonicalType.h
   void dump() const;
@@ -3277,9 +3235,7 @@ class BuiltinType : public Type {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  bool isInteger() const {
-    return getKind() >= Bool && getKind() <= Int128;
-  }
+  bool isInteger() const { return getKind() >= Bool && getKind() <= Int128; }
 
   bool isSignedInteger() const {
     return getKind() >= Char_S && getKind() <= Int128;
@@ -3298,16 +3254,12 @@ class BuiltinType : public Type {
   bool isSVECount() const { return getKind() == Kind::SveCount; }
 
   /// Determines whether the given kind corresponds to a placeholder type.
-  static bool isPlaceholderTypeKind(Kind K) {
-    return K >= Overload;
-  }
+  static bool isPlaceholderTypeKind(Kind K) { return K >= Overload; }
 
   /// Determines whether this type is a placeholder type, i.e. a type
   /// which cannot appear in arbitrary positions in a fully-formed
   /// expression.
-  bool isPlaceholderType() const {
-    return isPlaceholderTypeKind(getKind());
-  }
+  bool isPlaceholderType() const { return isPlaceholderTypeKind(getKind()); }
 
   /// Determines whether this type is a placeholder type other than
   /// Overload.  Most placeholder types require only syntactic
@@ -3318,9 +3270,7 @@ class BuiltinType : public Type {
   /// from their context, like whether the context expects a
   /// specific function-pointer type, and so frequently need
   /// special treatment.
-  bool isNonOverloadPlaceholderType() const {
-    return getKind() > Overload;
-  }
+  bool isNonOverloadPlaceholderType() const { return getKind() > Overload; }
 
   static bool classof(const Type *T) { return T->getTypeClass() == Builtin; }
 };
@@ -3342,9 +3292,7 @@ class ComplexType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getElementType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getElementType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType Element) {
     ID.AddPointer(Element.getAsOpaquePtr());
@@ -3368,9 +3316,7 @@ class ParenType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return true; }
   QualType desugar() const { return getInnerType(); }
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getInnerType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getInnerType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType Inner) {
     Inner.Profile(ID);
@@ -3395,9 +3341,7 @@ class PointerType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getPointeeType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getPointeeType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee) {
     ID.AddPointer(Pointee.getAsOpaquePtr());
@@ -3580,8 +3524,8 @@ class AdjustedType : public Type, public llvm::FoldingSetNode {
 class DecayedType : public AdjustedType {
   friend class ASTContext; // ASTContext creates these.
 
-  inline
-  DecayedType(QualType OriginalType, QualType Decayed, QualType Canonical);
+  inline DecayedType(QualType OriginalType, QualType Decayed,
+                     QualType Canonical);
 
 public:
   QualType getDecayedType() const { return getAdjustedType(); }
@@ -3611,12 +3555,10 @@ class BlockPointerType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-      Profile(ID, getPointeeType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getPointeeType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee) {
-      ID.AddPointer(Pointee.getAsOpaquePtr());
+    ID.AddPointer(Pointee.getAsOpaquePtr());
   }
 
   static bool classof(const Type *T) {
@@ -3655,8 +3597,7 @@ class ReferenceType : public Type, public llvm::FoldingSetNode {
     Profile(ID, PointeeType, isSpelledAsLValue());
   }
 
-  static void Profile(llvm::FoldingSetNodeID &ID,
-                      QualType Referencee,
+  static void Profile(llvm::FoldingSetNodeID &ID, QualType Referencee,
                       bool SpelledAsLValue) {
     ID.AddPointer(Referencee.getAsOpaquePtr());
     ID.AddBoolean(SpelledAsLValue);
@@ -3691,7 +3632,7 @@ class RValueReferenceType : public ReferenceType {
   friend class ASTContext; // ASTContext creates these
 
   RValueReferenceType(QualType Referencee, QualType CanonicalRef)
-       : ReferenceType(RValueReference, Referencee, CanonicalRef, false) {}
+      : ReferenceType(RValueReference, Referencee, CanonicalRef, false) {}
 
 public:
   bool isSugared() const { return false; }
@@ -3964,8 +3905,8 @@ class ArrayParameterType : public ConstantArrayType {
 class IncompleteArrayType : public ArrayType {
   friend class ASTContext; // ASTContext creates these.
 
-  IncompleteArrayType(QualType et, QualType can,
-                      ArraySizeModifier sm, unsigned tq)
+  IncompleteArrayType(QualType et, QualType can, ArraySizeModifier sm,
+                      unsigned tq)
       : ArrayType(IncompleteArray, et, can, sm, tq) {}
 
 public:
@@ -4035,7 +3976,7 @@ class VariableArrayType : public ArrayType {
   Expr *getSizeExpr() const {
     // We use C-style casts instead of cast<> here because we do not wish
     // to have a dependency of Type.h on Stmt.h/Expr.h.
-    return (Expr*) SizeExpr;
+    return (Expr *)SizeExpr;
   }
 
   bool isSugared() const { return false; }
@@ -4082,7 +4023,7 @@ class DependentSizedArrayType : public ArrayType {
   Expr *getSizeExpr() const {
     // We use C-style casts instead of cast<> here because we do not wish
     // to have a dependency of Type.h on Stmt.h/Expr.h.
-    return (Expr*) SizeExpr;
+    return (Expr *)SizeExpr;
   }
 
   bool isSugared() const { return false; }
@@ -4093,8 +4034,8 @@ class DependentSizedArrayType : public ArrayType {
   }
 
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
-    Profile(ID, Context, getElementType(),
-            getSizeModifier(), getIndexTypeCVRQualifiers(), getSizeExpr());
+    Profile(ID, Context, getElementType(), getSizeModifier(),
+            getIndexTypeCVRQualifiers(), getSizeExpr());
   }
 
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
@@ -4104,7 +4045,8 @@ class DependentSizedArrayType : public ArrayType {
 
 /// Represents an extended address space qualifier where the input address space
 /// value is dependent. Non-dependent address spaces are not represented with a
-/// special Type subclass; they are stored on an ExtQuals node as part of a QualType.
+/// special Type subclass; they are stored on an ExtQuals node as part of a
+/// QualType.
 ///
 /// For example:
 /// \code
@@ -4252,8 +4194,8 @@ class VectorType : public Type, public llvm::FoldingSetNode {
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getElementType(), getNumElements(),
-            getTypeClass(), getVectorKind());
+    Profile(ID, getElementType(), getNumElements(), getTypeClass(),
+            getVectorKind());
   }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType ElementType,
@@ -4329,39 +4271,65 @@ class ExtVectorType : public VectorType {
 public:
   static int getPointAccessorIdx(char c) {
     switch (c) {
-    default: return -1;
-    case 'x': case 'r': return 0;
-    case 'y': case 'g': return 1;
-    case 'z': case 'b': return 2;
-    case 'w': case 'a': return 3;
+    default:
+      return -1;
+    case 'x':
+    case 'r':
+      return 0;
+    case 'y':
+    case 'g':
+      return 1;
+    case 'z':
+    case 'b':
+      return 2;
+    case 'w':
+    case 'a':
+      return 3;
     }
   }
 
   static int getNumericAccessorIdx(char c) {
     switch (c) {
-      default: return -1;
-      case '0': return 0;
-      case '1': return 1;
-      case '2': return 2;
-      case '3': return 3;
-      case '4': return 4;
-      case '5': return 5;
-      case '6': return 6;
-      case '7': return 7;
-      case '8': return 8;
-      case '9': return 9;
-      case 'A':
-      case 'a': return 10;
-      case 'B':
-      case 'b': return 11;
-      case 'C':
-      case 'c': return 12;
-      case 'D':
-      case 'd': return 13;
-      case 'E':
-      case 'e': return 14;
-      case 'F':
-      case 'f': return 15;
+    default:
+      return -1;
+    case '0':
+      return 0;
+    case '1':
+      return 1;
+    case '2':
+      return 2;
+    case '3':
+      return 3;
+    case '4':
+      return 4;
+    case '5':
+      return 5;
+    case '6':
+      return 6;
+    case '7':
+      return 7;
+    case '8':
+      return 8;
+    case '9':
+      return 9;
+    case 'A':
+    case 'a':
+      return 10;
+    case 'B':
+    case 'b':
+      return 11;
+    case 'C':
+    case 'c':
+      return 12;
+    case 'D':
+    case 'd':
+      return 13;
+    case 'E':
+    case 'e':
+      return 14;
+    case 'F':
+    case 'f':
+      return 15;
     }
   }
 
@@ -4373,17 +4341,15 @@ class ExtVectorType : public VectorType {
   }
 
   bool isAccessorWithinNumElements(char c, bool isNumericAccessor) const {
-    if (int idx = getAccessorIdx(c, isNumericAccessor)+1)
-      return unsigned(idx-1) < getNumElements();
+    if (int idx = getAccessorIdx(c, isNumericAccessor) + 1)
+      return unsigned(idx - 1) < getNumElements();
     return false;
   }
 
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  static bool classof(const Type *T) {
-    return T->getTypeClass() == ExtVector;
-  }
+  static bool classof(const Type *T) { return T->getTypeClass() == ExtVector; }
 };
 
 /// Represents a matrix type, as defined in the Matrix Types clang extensions.
@@ -4716,7 +4682,9 @@ class FunctionType : public Type {
     bool getCmseNSCall() const { return Bits & CmseNSCallMask; }
     bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
     bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
-    bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; }
+    bool getHasRegParm() const {
+      return ((Bits & RegParmMask) >> RegParmOffset) != 0;
+    }
 
     unsigned getRegParm() const {
       unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset;
@@ -4727,12 +4695,8 @@ class FunctionType : public Type {
 
     CallingConv getCC() const { return CallingConv(Bits & CallConvMask); }
 
-    bool operator==(ExtInfo Other) const {
-      return Bits == Other.Bits;
-    }
-    bool operator!=(ExtInfo Other) const {
-      return Bits != Other.Bits;
-    }
+    bool operator==(ExtInfo Other) const { return Bits == Other.Bits; }
+    bool operator!=(ExtInfo Other) const { return Bits != Other.Bits; }
 
     // Note that we don't have setters. That is by design, use
     // the following with methods instead of mutating these objects.
@@ -4774,23 +4738,22 @@ class FunctionType : public Type {
 
     ExtInfo withRegParm(unsigned RegParm) const {
       assert(RegParm < 7 && "Invalid regparm value");
-      return ExtInfo((Bits & ~RegParmMask) |
-                     ((RegParm + 1) << RegParmOffset));
+      return ExtInfo((Bits & ~RegParmMask) | ((RegParm + 1) << RegParmOffset));
     }
 
     ExtInfo withCallingConv(CallingConv cc) const {
-      return ExtInfo((Bits & ~CallConvMask) | (unsigned) cc);
+      return ExtInfo((Bits & ~CallConvMask) | (unsigned)cc);
     }
 
-    void Profile(llvm::FoldingSetNodeID &ID) const {
-      ID.AddInteger(Bits);
-    }
+    void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Bits); }
   };
 
   /// A simple holder for a QualType representing a type in an
   /// exception specification. Unfortunately needed by FunctionProtoType
   /// because TrailingObjects cannot handle repeated types.
-  struct ExceptionType { QualType Type; };
+  struct ExceptionType {
+    QualType Type;
+  };
 
   /// A simple holder for various uncommon bits which do not fit in
   /// FunctionTypeBitfields. Aligned to alignof(void *) to maintain the
@@ -5617,7 +5580,6 @@ class FunctionProtoType final
             FunctionTypeBits.HasExtraBitfields) &&
            "ExtraBitfields are required for given ExceptionSpecType");
     return FunctionTypeBits.HasExtraBitfields;
-
   }
 
   bool hasExtraAttributeInfo() const {
@@ -5632,9 +5594,7 @@ class FunctionProtoType final
                ->HasArmTypeAttributes;
   }
 
-  bool hasExtQualifiers() const {
-    return FunctionTypeBits.HasExtQuals;
-  }
+  bool hasExtQualifiers() const { return FunctionTypeBits.HasExtQuals; }
 
 public:
   unsigned getNumParams() const { return FunctionTypeBits.NumParams; }
@@ -6747,9 +6707,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
     ID.AddPointer(attr);
   }
 
-  static bool classof(const Type *T) {
-    return T->getTypeClass() == Attributed;
-  }
+  static bool classof(const Type *T) { return T->getTypeClass() == Attributed; }
 };
 
 class BTFTagAttributedType : public Type, public llvm::FoldingSetNode {
@@ -7329,9 +7287,7 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode {
     return TypeConstraintConcept;
   }
 
-  bool isConstrained() const {
-    return TypeConstraintConcept != nullptr;
-  }
+  bool isConstrained() const { return TypeConstraintConcept != nullptr; }
 
   bool isDecltypeAuto() const {
     return getKeyword() == AutoTypeKeyword::DecltypeAuto;
@@ -7350,9 +7306,7 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode {
                       DeducedKind DK, QualType Deduced, AutoTypeKeyword Keyword,
                       TemplateDecl *CD, ArrayRef<TemplateArgument> Arguments);
 
-  static bool classof(const Type *T) {
-    return T->getTypeClass() == Auto;
-  }
+  static bool classof(const Type *T) { return T->getTypeClass() == Auto; }
 };
 
 /// Represents a C++17 deduced template specialization type.
@@ -7510,8 +7464,7 @@ class TemplateSpecializationType : public TypeWithKeyword,
 
 /// Print a template argument list, including the '<' and '>'
 /// enclosing the template arguments.
-void printTemplateArgumentList(raw_ostream &OS,
-                               ArrayRef<TemplateArgument> Args,
+void printTemplateArgumentList(raw_ostream &OS, ArrayRef<TemplateArgument> Args,
                                const PrintingPolicy &Policy,
                                const TemplateParameterList *TPL = nullptr);
 
@@ -7569,9 +7522,7 @@ class DependentNameType : public TypeWithKeyword, public llvm::FoldingSetNode {
 
   /// Retrieve the identifier that terminates this type name.
   /// For example, "type" in "typename T::type".
-  const IdentifierInfo *getIdentifier() const {
-    return Name;
-  }
+  const IdentifierInfo *getIdentifier() const { return Name; }
 
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
@@ -7665,21 +7616,20 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode {
 
 /// This class wraps the list of protocol qualifiers. For types that can
 /// take ObjC protocol qualifers, they can subclass this class.
-template <class T>
-class ObjCProtocolQualifiers {
+template <class T> class ObjCProtocolQualifiers {
 protected:
   ObjCProtocolQualifiers() = default;
 
-  ObjCProtocolDecl * const *getProtocolStorage() const {
-    return const_cast<ObjCProtocolQualifiers*>(this)->getProtocolStorage();
+  ObjCProtocolDecl *const *getProtocolStorage() const {
+    return const_cast<ObjCProtocolQualifiers *>(this)->getProtocolStorage();
   }
 
   ObjCProtocolDecl **getProtocolStorage() {
-    return static_cast<T*>(this)->getProtocolStorageImpl();
+    return static_cast<T *>(this)->getProtocolStorageImpl();
   }
 
   void setNumProtocols(unsigned N) {
-    static_cast<T*>(this)->setNumProtocolsImpl(N);
+    static_cast<T *>(this)->setNumProtocolsImpl(N);
   }
 
   void initialize(ArrayRef<ObjCProtocolDecl *> protocols) {
@@ -7688,11 +7638,11 @@ class ObjCProtocolQualifiers {
            "bitfield overflow in protocol count");
     if (!protocols.empty())
       memcpy(getProtocolStorage(), protocols.data(),
-             protocols.size() * sizeof(ObjCProtocolDecl*));
+             protocols.size() * sizeof(ObjCProtocolDecl *));
   }
 
 public:
-  using qual_iterator = ObjCProtocolDecl * const *;
+  using qual_iterator = ObjCProtocolDecl *const *;
   using qual_range = llvm::iterator_range<qual_iterator>;
 
   qual_range quals() const { return qual_range(qual_begin(), qual_end()); }
@@ -7704,7 +7654,7 @@ class ObjCProtocolQualifiers {
   /// Return the number of qualifying protocols in this type, or 0 if
   /// there are none.
   unsigned getNumProtocols() const {
-    return static_cast<const T*>(this)->getNumProtocolsImpl();
+    return static_cast<const T *>(this)->getNumProtocolsImpl();
   }
 
   /// Fetch a protocol by index.
@@ -7739,16 +7689,11 @@ class ObjCTypeParamType : public Type,
 
   /// Return the number of qualifying protocols in this interface type,
   /// or 0 if there are none.
-  unsigned getNumProtocolsImpl() const {
-    return NumProtocols;
-  }
+  unsigned getNumProtocolsImpl() const { return NumProtocols; }
 
-  void setNumProtocolsImpl(unsigned N) {
-    NumProtocols = N;
-  }
+  void setNumProtocolsImpl(unsigned N) { NumProtocols = N; }
 
-  ObjCTypeParamType(const ObjCTypeParamDecl *D,
-                    QualType can,
+  ObjCTypeParamType(const ObjCTypeParamDecl *D, QualType can,
                     ArrayRef<ObjCProtocolDecl *> protocols);
 
 public:
@@ -7761,8 +7706,7 @@ class ObjCTypeParamType : public Type,
 
   void Profile(llvm::FoldingSetNodeID &ID);
   static void Profile(llvm::FoldingSetNodeID &ID,
-                      const ObjCTypeParamDecl *OTPDecl,
-                      QualType CanonicalType,
+                      const ObjCTypeParamDecl *OTPDecl, QualType CanonicalType,
                       ArrayRef<ObjCProtocolDecl *> protocols);
 
   ObjCTypeParamDecl *getDecl() const { return OTPDecl; }
@@ -7817,7 +7761,7 @@ class ObjCObjectType : public Type,
 
   /// Cached superclass type.
   mutable llvm::PointerIntPair<const ObjCObjectType *, 1, bool>
-    CachedSuperClassType;
+      CachedSuperClassType;
 
   QualType *getTypeArgStorage();
   const QualType *getTypeArgStorage() const {
@@ -7830,17 +7774,13 @@ class ObjCObjectType : public Type,
   unsigned getNumProtocolsImpl() const {
     return ObjCObjectTypeBits.NumProtocols;
   }
-  void setNumProtocolsImpl(unsigned N) {
-    ObjCObjectTypeBits.NumProtocols = N;
-  }
+  void setNumProtocolsImpl(unsigned N) { ObjCObjectTypeBits.NumProtocols = N; }
 
 protected:
   enum Nonce_ObjCInterface { Nonce_ObjCInterface };
 
-  ObjCObjectType(QualType Canonical, QualType Base,
-                 ArrayRef<QualType> typeArgs,
-                 ArrayRef<ObjCProtocolDecl *> protocols,
-                 bool isKindOf);
+  ObjCObjectType(QualType Canonical, QualType Base, ArrayRef<QualType> typeArgs,
+                 ArrayRef<ObjCProtocolDecl *> protocols, bool isKindOf);
 
   ObjCObjectType(enum Nonce_ObjCInterface)
       : Type(ObjCInterface, QualType(), TypeDependence::None),
@@ -7872,7 +7812,8 @@ class ObjCObjectType : public Type,
   bool isObjCUnqualifiedId() const { return qual_empty() && isObjCId(); }
   bool isObjCUnqualifiedClass() const { return qual_empty() && isObjCClass(); }
   bool isObjCUnqualifiedIdOrClass() const {
-    if (!qual_empty()) return false;
+    if (!qual_empty())
+      return false;
     if (const BuiltinType *T = getBaseType()->getAs<BuiltinType>())
       return T->getKind() == BuiltinType::ObjCId ||
              T->getKind() == BuiltinType::ObjCClass;
@@ -7956,31 +7897,29 @@ class ObjCObjectTypeImpl : public ObjCObjectType, public llvm::FoldingSetNode {
 
   ObjCObjectTypeImpl(QualType Canonical, QualType Base,
                      ArrayRef<QualType> typeArgs,
-                     ArrayRef<ObjCProtocolDecl *> protocols,
-                     bool isKindOf)
+                     ArrayRef<ObjCProtocolDecl *> protocols, bool isKindOf)
       : ObjCObjectType(Canonical, Base, typeArgs, protocols, isKindOf) {}
 
 public:
   void Profile(llvm::FoldingSetNodeID &ID);
-  static void Profile(llvm::FoldingSetNodeID &ID,
-                      QualType Base,
+  static void Profile(llvm::FoldingSetNodeID &ID, QualType Base,
                       ArrayRef<QualType> typeArgs,
-                      ArrayRef<ObjCProtocolDecl *> protocols,
-                      bool isKindOf);
+                      ArrayRef<ObjCProtocolDecl *> protocols, bool isKindOf);
 };
 
 inline QualType *ObjCObjectType::getTypeArgStorage() {
-  return reinterpret_cast<QualType *>(static_cast<ObjCObjectTypeImpl*>(this)+1);
+  return reinterpret_cast<QualType *>(static_cast<ObjCObjectTypeImpl *>(this) +
+                                      1);
 }
 
 inline ObjCProtocolDecl **ObjCObjectType::getProtocolStorageImpl() {
-    return reinterpret_cast<ObjCProtocolDecl**>(
-             getTypeArgStorage() + ObjCObjectTypeBits.NumTypeArgs);
+  return reinterpret_cast<ObjCProtocolDecl **>(getTypeArgStorage() +
+                                               ObjCObjectTypeBits.NumTypeArgs);
 }
 
 inline ObjCProtocolDecl **ObjCTypeParamType::getProtocolStorageImpl() {
-    return reinterpret_cast<ObjCProtocolDecl**>(
-             static_cast<ObjCTypeParamType*>(this)+1);
+  return reinterpret_cast<ObjCProtocolDecl **>(
+      static_cast<ObjCTypeParamType *>(this) + 1);
 }
 
 /// Interfaces are the core concept in Objective-C for object oriented design.
@@ -8004,7 +7943,7 @@ class ObjCInterfaceType : public ObjCObjectType {
 
   ObjCInterfaceType(const ObjCInterfaceDecl *D)
       : ObjCObjectType(Nonce_ObjCInterface),
-        Decl(const_cast<ObjCInterfaceDecl*>(D)) {}
+        Decl(const_cast<ObjCInterfaceDecl *>(D)) {}
 
 public:
   /// Get the declaration of this interface.
@@ -8021,13 +7960,7 @@ class ObjCInterfaceType : public ObjCObjectType {
   // class.  People asking for protocols on an ObjCInterfaceType are
   // not going to get what they want: ObjCInterfaceTypes are
   // guaranteed to have no protocols.
-  enum {
-    qual_iterator,
-    qual_begin,
-    qual_end,
-    getNumProtocols,
-    getProtocol
-  };
+  enum { qual_iterator, qual_begin, qual_end, getNumProtocols, getProtocol };
 };
 
 inline ObjCInterfaceDecl *ObjCObjectType::getInterface() const {
@@ -8109,9 +8042,7 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
 
   /// True if this is equivalent to the 'id' type, i.e. if
   /// its object type is the primitive 'id' type with no protocols.
-  bool isObjCIdType() const {
-    return getObjectType()->isObjCUnqualifiedId();
-  }
+  bool isObjCIdType() const { return getObjectType()->isObjCUnqualifiedId(); }
 
   /// True if this is equivalent to the 'Class' type,
   /// i.e. if its object tive is the primitive 'Class' type with no protocols.
@@ -8172,13 +8103,9 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
 
   qual_range quals() const { return qual_range(qual_begin(), qual_end()); }
 
-  qual_iterator qual_begin() const {
-    return getObjectType()->qual_begin();
-  }
+  qual_iterator qual_begin() const { return getObjectType()->qual_begin(); }
 
-  qual_iterator qual_end() const {
-    return getObjectType()->qual_end();
-  }
+  qual_iterator qual_end() const { return getObjectType()->qual_end(); }
 
   bool qual_empty() const { return getObjectType()->qual_empty(); }
 
@@ -8205,12 +8132,10 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
 
   /// Strip off the Objective-C "kindof" type and (with it) any
   /// protocol qualifiers.
-  const ObjCObjectPointerType *stripObjCKindOfTypeAndQuals(
-                                 const ASTContext &ctx) const;
+  const ObjCObjectPointerType *
+  stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const;
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getPointeeType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getPointeeType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType T) {
     ID.AddPointer(T.getAsOpaquePtr());
@@ -8237,17 +8162,13 @@ class AtomicType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
-  void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getValueType());
-  }
+  void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getValueType()); }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType T) {
     ID.AddPointer(T.getAsOpaquePtr());
   }
 
-  static bool classof(const Type *T) {
-    return T->getTypeClass() == Atomic;
-  }
+  static bool classof(const Type *T) { return T->getTypeClass() == Atomic; }
 };
 
 /// PipeType - OpenCL20.
@@ -8277,9 +8198,7 @@ class PipeType : public Type, public llvm::FoldingSetNode {
     ID.AddBoolean(isRead);
   }
 
-  static bool classof(const Type *T) {
-    return T->getTypeClass() == Pipe;
-  }
+  static bool classof(const Type *T) { return T->getTypeClass() == Pipe; }
 
   bool isReadOnly() const { return isRead; }
 };
@@ -8317,7 +8236,7 @@ class BitIntType final : public Type, public llvm::FoldingSetNode {
 
 class DependentBitIntType final : public Type, public llvm::FoldingSetNode {
   friend class ASTContext;
-  llvm::PointerIntPair<Expr*, 1, bool> ExprAndUnsigned;
+  llvm::PointerIntPair<Expr *, 1, bool> ExprAndUnsigned;
 
 protected:
   DependentBitIntType(bool IsUnsigned, Expr *NumBits);
@@ -8394,7 +8313,7 @@ class QualifierCollector : public Qualifiers {
   QualType apply(const ASTContext &Context, QualType QT) const;
 
   /// Apply the collected qualifiers to the given type.
-  QualType apply(const ASTContext &Context, const Type* T) const;
+  QualType apply(const ASTContext &Context, const Type *T) const;
 };
 
 /// A container of type source information.
@@ -8428,7 +8347,7 @@ class alignas(8) TypeSourceInfo {
 
 inline SplitQualType SplitQualType::getSingleStepDesugaredType() const {
   SplitQualType desugar =
-    Ty->getLocallyUnqualifiedSingleStepDesugaredType().split();
+      Ty->getLocallyUnqualifiedSingleStepDesugaredType().split();
   desugar.Quals.addConsistentQualifiers(Quals);
   return desugar;
 }
@@ -8495,8 +8414,10 @@ inline bool QualType::isCanonical() const {
 }
 
 inline bool QualType::isCanonicalAsParam() const {
-  if (!isCanonical()) return false;
-  if (hasLocalQualifiers()) return false;
+  if (!isCanonical())
+    return false;
+  if (hasLocalQualifiers())
+    return false;
 
   const Type *T = getTypePtr();
   if (T->isVariablyModifiedType() && T->hasSizedVLAType())
@@ -8516,7 +8437,6 @@ inline bool QualType::isRestrictQualified() const {
          getCommonPtr()->CanonicalType.isLocalRestrictQualified();
 }
 
-
 inline bool QualType::isVolatileQualified() const {
   return isLocalVolatileQualified() ||
          getCommonPtr()->CanonicalType.isLocalVolatileQualified();
@@ -8634,8 +8554,7 @@ inline bool QualType::isCForbiddenLValueType() const {
 ///
 /// \returns True for types specified in C++0x [basic.fundamental].
 inline bool Type::isFundamentalType() const {
-  return isVoidType() ||
-         isNullPtrType() ||
+  return isVoidType() || isNullPtrType() ||
          // FIXME: It's really annoying that we don't have an
          // 'isArithmeticType()' which agrees with the standard definition.
          (isArithmeticType() && !isEnumeralType());
@@ -8649,20 +8568,23 @@ inline bool Type::isCompoundType() const {
   //   Compound types can be constructed in the following ways:
   //    -- arrays of objects of a given type [...];
   return isArrayType() ||
-  //    -- functions, which have parameters of given types [...];
+         //    -- functions, which have parameters of given types [...];
          isFunctionType() ||
-  //    -- pointers to void or objects or functions [...];
+         //    -- pointers to void or objects or functions [...];
          isPointerType() ||
-  //    -- references to objects or functions of a given type. [...]
+         //    -- references to objects or functions of a given type. [...]
          isReferenceType() ||
-  //    -- classes containing a sequence of objects of various types, [...];
+         //    -- classes containing a sequence of objects of various types,
+         //    [...];
          isRecordType() ||
-  //    -- unions, which are classes capable of containing objects of different
-  //               types at different times;
+         //    -- unions, which are classes capable of containing objects of
+         //    different
+         //               types at different times;
          isUnionType() ||
-  //    -- enumerations, which comprise a set of named constant values. [...];
+         //    -- enumerations, which comprise a set of named constant values.
+         //    [...];
          isEnumeralType() ||
-  //    -- pointers to non-static class members, [...].
+         //    -- pointers to non-static class members, [...].
          isMemberPointerType();
 }
 
@@ -8769,9 +8691,7 @@ inline bool Type::isMemberDataPointerType() const {
     return false;
 }
 
-inline bool Type::isArrayType() const {
-  return isa<ArrayType>(CanonicalType);
-}
+inline bool Type::isArrayType() const { return isa<ArrayType>(CanonicalType); }
 
 inline bool Type::isConstantArrayType() const {
   return isa<ConstantArrayType>(CanonicalType);
@@ -8859,7 +8779,7 @@ inline bool Type::isObjCObjectType() const {
 
 inline bool Type::isObjCObjectOrInterfaceType() const {
   return isa<ObjCInterfaceType>(CanonicalType) ||
-    isa<ObjCObjectType>(CanonicalType);
+         isa<ObjCObjectType>(CanonicalType);
 }
 
 inline bool Type::isAtomicType() const {
@@ -8904,13 +8824,11 @@ inline bool Type::isObjCBuiltinType() const {
   return isObjCIdType() || isObjCClassType() || isObjCSelType();
 }
 
-inline bool Type::isDecltypeType() const {
-  return isa<DecltypeType>(this);
-}
+inline bool Type::isDecltypeType() const { return isa<DecltypeType>(this); }
 
-#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
-  inline bool Type::is##Id##Type() const { \
-    return isSpecificBuiltinType(BuiltinType::Id); \
+#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
+  inline bool Type::is##Id##Type() const {                                     \
+    return isSpecificBuiltinType(BuiltinType::Id);                             \
   }
 #include "clang/Basic/OpenCLImageTypes.def"
 
@@ -8941,33 +8859,31 @@ inline bool Type::isImageType() const {
       false; // end boolean or operation
 }
 
-inline bool Type::isPipeType() const {
-  return isa<PipeType>(CanonicalType);
-}
+inline bool Type::isPipeType() const { return isa<PipeType>(CanonicalType); }
 
 inline bool Type::isBitIntType() const {
   return isa<BitIntType>(CanonicalType);
 }
 
-#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
-  inline bool Type::is##Id##Type() const { \
-    return isSpecificBuiltinType(BuiltinType::Id); \
+#define EXT_OPAQUE_TYPE(ExtType, Id, Ext)                                      \
+  inline bool Type::is##Id##Type() const {                                     \
+    return isSpecificBuiltinType(BuiltinType::Id);                             \
   }
 #include "clang/Basic/OpenCLExtensionTypes.def"
 
 inline bool Type::isOCLIntelSubgroupAVCType() const {
-#define INTEL_SUBGROUP_AVC_TYPE(ExtType, Id) \
+#define INTEL_SUBGROUP_AVC_TYPE(ExtType, Id)                                   \
   isOCLIntelSubgroupAVC##Id##Type() ||
   return
 #include "clang/Basic/OpenCLExtensionTypes.def"
-    false; // end of boolean or operation
+      false; // end of boolean or operation
 }
 
 inline bool Type::isOCLExtOpaqueType() const {
 #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) is##Id##Type() ||
   return
 #include "clang/Basic/OpenCLExtensionTypes.def"
-    false; // end of boolean or operation
+      false; // end of boolean or operation
 }
 
 inline bool Type::isOpenCLSpecificType() const {
@@ -9026,7 +8942,7 @@ inline const BuiltinType *Type::getAsPlaceholderType() const {
 }
 
 inline bool Type::isSpecificPlaceholderType(unsigned K) const {
-  assert(BuiltinType::isPlaceholderTypeKind((BuiltinType::Kind) K));
+  assert(BuiltinType::isPlaceholderTypeKind((BuiltinType::Kind)K));
   return isSpecificBuiltinType(K);
 }
 
@@ -9281,7 +9197,8 @@ template <typename T> const T *Type::getAs() const {
 }
 
 template <typename T> const T *Type::getAsAdjusted() const {
-  static_assert(!TypeIsArrayType<T>::value, "ArrayType cannot be used with getAsAdjusted!");
+  static_assert(!TypeIsArrayType<T>::value,
+                "ArrayType cannot be used with getAsAdjusted!");
 
   // If this is directly a T type, return it.
   if (const auto *Ty = dyn_cast<T>(this))
@@ -9334,14 +9251,16 @@ template <typename T> const T *Type::castAs() const {
   static_assert(!TypeIsArrayType<T>::value,
                 "ArrayType cannot be used with castAs!");
 
-  if (const auto *ty = dyn_cast<T>(this)) return ty;
+  if (const auto *ty = dyn_cast<T>(this))
+    return ty;
   assert(isa<T>(CanonicalType));
   return cast<T>(getUnqualifiedDesugaredType());
 }
 
 inline const ArrayType *Type::castAsArrayTypeUnsafe() const {
   assert(isa<ArrayType>(CanonicalType));
-  if (const auto *arr = dyn_cast<ArrayType>(this)) return arr;
+  if (const auto *arr = dyn_cast<ArrayType>(this))
+    return arr;
   return cast<ArrayType>(getUnqualifiedDesugaredType());
 }
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 06023fc088a32..e6b316c0d90a2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5914,3 +5914,54 @@ StringRef PredefinedSugarType::getName(Kind KD) {
   }
   llvm_unreachable("unexpected kind");
 }
+
+bool Type::isImplicitLifetimeType() const {
+  // Scalar types (such as pointers, ints, floats, enums, etc.) asre always
+  // implicit-lifetime types.
+  if (isScalarType())
+    return true;
+
+  // Vector types act like scalars for lifetimes.
+  if (isVectorType() || isExtVectorType())
+    return true;
+
+  // Array types are implicit-lifetime if their base elemnt is also
+  // implicit-lifetime.
+  if (isArrayType())
+    return getBaseElementTypeUnsafe()->isImplicitLifetimeType();
+
+  if (const RecordType *RT = getAs<RecordType>()) {
+    const RecordDecl *RD = RT->getDecl();
+
+    // dyn_cast returns nullptr if RD is not a CXXRecordDecl. If it's not a C++
+    // record, it's a standard C struct/union, which are always
+    // implicit-lifetime.
+    const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
+    if (!CXXRD)
+      return true;
+
+    // We cannot determine lifetime if the class is incomplete.
+    if (!CXXRD->isCompleteDefinition())
+      return false;
+
+    // C++20 [class.prop]: A class S is an implicit-lifetime class if
+    // - a trivial, non-deleted destructor, AND
+    if (!CXXRD->hasTrivialDestructor())
+      return false;
+
+    // - it is an aggregate, OR
+    if (CXXRD->isAggregate())
+      return true;
+
+    // - has at least one trivial eligible constructor.
+    if (CXXRD->hasTrivialDefaultConstructor() ||
+        CXXRD->hasTrivialCopyConstructor() ||
+        CXXRD->hasTrivialMoveConstructor())
+      return true;
+
+    return false;
+  }
+
+  // References, function types and void are not implicit-lifetime.
+  return false;
+}

>From b899636f65b3116cc2829e744744f074a2399985 Mon Sep 17 00:00:00 2001
From: Yash Verma <yashverma056 at proton.me>
Date: Thu, 7 May 2026 03:19:37 -0400
Subject: [PATCH 2/4] [Clang][Sema] Implement __builtin_start_lifetime_as
 intrinsic

This patch introduces the `__builtin_start_lifetime_as` instrinsic to support C++23 explicit lifetime management (P2590R2) and its subsequent array-handling refinements (P2679R2).

This instrinsic is designed to take a single-argument. The target type is deduced directly from the pointer cast (e.g., `__builtin_start_lifetime_as((T*)p)`). Thus avoiding the need for a custom AST node.

Key additions:
  - Registers `__builtin_start_lifetime_as` in `Builtins.td` using CustomTypeChecking.
  - Implements `Sema::BuiltinStartLifetimeAs` to strictly enforce C++23 implicit-lifetime constraints.
  - Recursively unwraps array types to evaluate the base element type's lifetime properties (as required by P2679R2).
  - Hooks into `ExprConstant.cpp` to explicitly reject the intrinsic inside constant expressions.
  - Adds `lit` test coverage in `SemaCXX/` validating diagnostic paths
---
 clang/include/clang/Basic/Builtins.td         |   6 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Sema/Sema.h               |   7 +
 clang/lib/AST/ExprConstant.cpp                |   6 +
 clang/lib/Sema/SemaChecking.cpp               | 859 ++++++++++--------
 .../SemaCXX/builtin-start-lifetime-as.cpp     |  56 ++
 6 files changed, 541 insertions(+), 396 deletions(-)
 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..f53a19c9d85af 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1251,6 +1251,12 @@ def Launder : Builtin {
   let Prototype = "void*(void*)";
 }
 
+def StartLifetimeAs : Builtin {
+  let Spellings = ["__builtin_start_lifetime_as"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void*(...)";
+}
+
 def IsConstantEvaluated : LangBuiltin<"CXX_LANG"> {
   let Spellings = ["__builtin_is_constant_evaluated"];
   let Attributes = [NoThrow, Constexpr];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6646b37262d92..0ae9804285153 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -172,6 +172,9 @@ def err_expr_not_string_literal : Error<"expression is not a string literal">;
 def note_constexpr_assert_failed : Note<
   "assertion failed during evaluation of constant expression">;
 
+def err_start_lifetime_as_not_implicit : Error<
+  "type %0 is not an implicit-lifetime type, cannot start lifetime">;
+
 // Semantic analysis of constant literals.
 def ext_predef_outside_function : Warning<
   "predefined identifier is only valid inside function">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 72beac7526dc5..785ff84ad1e46 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2893,6 +2893,13 @@ class Sema final : public SemaBase {
   bool BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall, unsigned ArgNum,
                                            unsigned ArgBits);
 
+  /// BuiltinStartLifetimeAs - Check if the argument of the
+  /// __builtin_start_lifetime_as instrinsic is a valid pointer to an
+  /// implicit-lifetime type. Defers validation if the call is type-dependent.
+  /// Returns true if the argument is invalid and emits a diagnostic. Returns
+  /// false if the call is well-formed or its evaluation is deferred.
+  bool BuiltinStartLifetimeAs(CallExpr *Call);
+
   /// 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..33fc6e0309f47 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10549,6 +10549,12 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     return ZeroInitialization(E);
   }
 
+  case Builtin::BI__builtin_start_lifetime_as:
+    // C++20/C++23 forbids evaluating lifetime-altering abstract machine magic
+    // inside a constant expression.
+    Info.FFDiag(E, diag::note_constexpr_invalid_function) << /*IsContexpr=*/0;
+    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..a922c00d42cb8 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"
@@ -408,35 +409,37 @@ static bool BuiltinOverflow(Sema &S, CallExpr *TheCall, unsigned BuiltinID) {
     return true;
 
   std::pair<unsigned, const char *> Builtins[] = {
-    { Builtin::BI__builtin_add_overflow, "ckd_add" },
-    { Builtin::BI__builtin_sub_overflow, "ckd_sub" },
-    { Builtin::BI__builtin_mul_overflow, "ckd_mul" },
+      {Builtin::BI__builtin_add_overflow, "ckd_add"},
+      {Builtin::BI__builtin_sub_overflow, "ckd_sub"},
+      {Builtin::BI__builtin_mul_overflow, "ckd_mul"},
   };
 
-  bool CkdOperation = llvm::any_of(Builtins, [&](const std::pair<unsigned,
-    const char *> &P) {
-    return BuiltinID == P.first && TheCall->getExprLoc().isMacroID() &&
-         Lexer::getImmediateMacroName(TheCall->getExprLoc(),
-         S.getSourceManager(), S.getLangOpts()) == P.second;
-  });
+  bool CkdOperation =
+      llvm::any_of(Builtins, [&](const std::pair<unsigned, const char *> &P) {
+        return BuiltinID == P.first && TheCall->getExprLoc().isMacroID() &&
+               Lexer::getImmediateMacroName(TheCall->getExprLoc(),
+                                            S.getSourceManager(),
+                                            S.getLangOpts()) == P.second;
+      });
 
   auto ValidCkdIntType = [](QualType QT) {
     // A valid checked integer type is an integer type other than a plain char,
     // bool, a bit-precise type, or an enumeration type.
     if (const auto *BT = QT.getCanonicalType()->getAs<BuiltinType>())
       return (BT->getKind() >= BuiltinType::Short &&
-           BT->getKind() <= BuiltinType::Int128) || (
-           BT->getKind() >= BuiltinType::UShort &&
-           BT->getKind() <= BuiltinType::UInt128) ||
-           BT->getKind() == BuiltinType::UChar ||
-           BT->getKind() == BuiltinType::SChar;
+              BT->getKind() <= BuiltinType::Int128) ||
+             (BT->getKind() >= BuiltinType::UShort &&
+              BT->getKind() <= BuiltinType::UInt128) ||
+             BT->getKind() == BuiltinType::UChar ||
+             BT->getKind() == BuiltinType::SChar;
     return false;
   };
 
   // First two arguments should be integers.
   for (unsigned I = 0; I < 2; ++I) {
     ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(I));
-    if (Arg.isInvalid()) return true;
+    if (Arg.isInvalid())
+      return true;
     TheCall->setArg(I, Arg.get());
 
     QualType Ty = Arg.get()->getType();
@@ -453,18 +456,18 @@ static bool BuiltinOverflow(Sema &S, CallExpr *TheCall, unsigned BuiltinID) {
   // the other qualifiers aren't possible.
   {
     ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(2));
-    if (Arg.isInvalid()) return true;
+    if (Arg.isInvalid())
+      return true;
     TheCall->setArg(2, Arg.get());
 
     QualType Ty = Arg.get()->getType();
     const auto *PtrTy = Ty->getAs<PointerType>();
-    if (!PtrTy ||
-        !PtrTy->getPointeeType()->isIntegerType() ||
+    if (!PtrTy || !PtrTy->getPointeeType()->isIntegerType() ||
         (!ValidCkdIntType(PtrTy->getPointeeType()) && CkdOperation) ||
         PtrTy->getPointeeType().isConstQualified()) {
       S.Diag(Arg.get()->getBeginLoc(),
              diag::err_overflow_builtin_must_be_ptr_int)
-        << CkdOperation << Ty << Arg.get()->getSourceRange();
+          << CkdOperation << Ty << Arg.get()->getSourceRange();
       return true;
     }
   }
@@ -583,7 +586,8 @@ struct BuiltinDumpStructGenerator {
     }
 
     analyze_printf::PrintfSpecifier Specifier;
-    if (Specifier.fixType(T, S.getLangOpts(), S.Context, /*IsObjCLiteral=*/false)) {
+    if (Specifier.fixType(T, S.getLangOpts(), S.Context,
+                          /*IsObjCLiteral=*/false)) {
       // We were able to guess how to format this.
       if (Specifier.getConversionSpecifier().getKind() ==
           analyze_printf::PrintfConversionSpecifier::sArg) {
@@ -844,7 +848,7 @@ static bool BuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) {
   }
 
   QualType ReturnTy = CE->getCallReturnType(S.Context);
-  QualType ArgTys[2] = { ReturnTy, ChainResult.get()->getType() };
+  QualType ArgTys[2] = {ReturnTy, ChainResult.get()->getType()};
   QualType BuiltinTy = S.Context.getFunctionType(
       ReturnTy, ArgTys, FunctionProtoType::ExtProtoInfo());
   QualType BuiltinPtrTy = S.Context.getPointerType(BuiltinTy);
@@ -2887,9 +2891,9 @@ static QualType getVectorElementType(ASTContext &Context, QualType VecTy) {
   return QualType();
 }
 
-ExprResult
-Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
-                               CallExpr *TheCall) {
+ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
+                                          unsigned BuiltinID,
+                                          CallExpr *TheCall) {
   ExprResult TheCallResult(TheCall);
 
   // Find out if any arguments are required to be integer constant expressions.
@@ -2897,12 +2901,13 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   ASTContext::GetBuiltinTypeError Error;
   Context.GetBuiltinType(BuiltinID, Error, &ICEArguments);
   if (Error != ASTContext::GE_None)
-    ICEArguments = 0;  // Don't diagnose previously diagnosed errors.
+    ICEArguments = 0; // Don't diagnose previously diagnosed errors.
 
   // If any arguments are required to be ICE's, check and diagnose.
   for (unsigned ArgNo = 0; ICEArguments != 0; ++ArgNo) {
     // Skip arguments not required to be ICE's.
-    if ((ICEArguments & (1 << ArgNo)) == 0) continue;
+    if ((ICEArguments & (1 << ArgNo)) == 0)
+      continue;
 
     llvm::APSInt Result;
     // If we don't have enough arguments, continue so we can issue better
@@ -3114,7 +3119,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
     if (checkArgCount(TheCall, 1))
       return true;
     ExprResult Arg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
-    if (Arg.isInvalid()) return true;
+    if (Arg.isInvalid())
+      return true;
     TheCall->setArg(0, Arg.get());
     TheCall->setType(Context.IntTy);
     break;
@@ -3125,6 +3131,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:
@@ -4019,7 +4029,7 @@ bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
   // of member functions is counted. However, it doesn't appear in our own
   // lists, so decrement format_idx in that case.
   if (HasImplicitThisParam) {
-    if(FSI->FormatIdx == 0)
+    if (FSI->FormatIdx == 0)
       return false;
     --FSI->FormatIdx;
     if (FSI->FirstDataArg != 0)
@@ -4058,12 +4068,10 @@ static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
 
   bool Result;
   return (!Expr->isValueDependent() &&
-          Expr->EvaluateAsBooleanCondition(Result, S.Context) &&
-          !Result);
+          Expr->EvaluateAsBooleanCondition(Result, S.Context) && !Result);
 }
 
-static void CheckNonNullArgument(Sema &S,
-                                 const Expr *ArgExpr,
+static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr,
                                  SourceLocation CallSiteLoc) {
   if (CheckNonNullExpr(S, ArgExpr))
     S.DiagRuntimeBehavior(CallSiteLoc, ArgExpr,
@@ -4079,8 +4087,7 @@ static bool isNonNullType(QualType type) {
   return false;
 }
 
-static void CheckNonNullArguments(Sema &S,
-                                  const NamedDecl *FDecl,
+static void CheckNonNullArguments(Sema &S, const NamedDecl *FDecl,
                                   const FunctionProtoType *Proto,
                                   ArrayRef<const Expr *> Args,
                                   SourceLocation CallSiteLoc) {
@@ -4116,14 +4123,14 @@ static void CheckNonNullArguments(Sema &S,
   if (FDecl && (isa<FunctionDecl>(FDecl) || isa<ObjCMethodDecl>(FDecl))) {
     // Handle the nonnull attribute on the parameters of the
     // function/method.
-    ArrayRef<ParmVarDecl*> parms;
+    ArrayRef<ParmVarDecl *> parms;
     if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl))
       parms = FD->parameters();
     else
       parms = cast<ObjCMethodDecl>(FDecl)->parameters();
 
     unsigned ParamIndex = 0;
-    for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
+    for (ArrayRef<ParmVarDecl *>::iterator I = parms.begin(), E = parms.end();
          I != E; ++I, ++ParamIndex) {
       const ParmVarDecl *PVD = *I;
       if (PVD->hasAttr<NonNullAttr>() || isNonNullType(PVD->getType())) {
@@ -4501,13 +4508,13 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType,
 
 bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
                              const FunctionProtoType *Proto) {
-  bool IsMemberOperatorCall = isa<CXXOperatorCallExpr>(TheCall) &&
-                              isa<CXXMethodDecl>(FDecl);
-  bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall) ||
-                          IsMemberOperatorCall;
-  VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
-                                                  TheCall->getCallee());
-  Expr** Args = TheCall->getArgs();
+  bool IsMemberOperatorCall =
+      isa<CXXOperatorCallExpr>(TheCall) && isa<CXXMethodDecl>(FDecl);
+  bool IsMemberFunction =
+      isa<CXXMemberCallExpr>(TheCall) || IsMemberOperatorCall;
+  VariadicCallType CallType =
+      getVariadicCallType(FDecl, Proto, TheCall->getCallee());
+  Expr **Args = TheCall->getArgs();
   unsigned NumArgs = TheCall->getNumArgs();
 
   Expr *ImplicitThis = nullptr;
@@ -4615,8 +4622,8 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
 }
 
 bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) {
-  VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto,
-                                                  TheCall->getCallee());
+  VariadicCallType CallType =
+      getVariadicCallType(/*FDecl=*/nullptr, Proto, TheCall->getCallee());
   checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr,
             llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
             /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
@@ -4665,7 +4672,8 @@ static bool isValidOrderingForOp(int64_t Ordering, AtomicExpr::AtomicOp Op) {
 ExprResult Sema::AtomicOpsOverloaded(ExprResult TheCallResult,
                                      AtomicExpr::AtomicOp Op) {
   CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
-  DeclRefExpr *DRE =cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+  DeclRefExpr *DRE =
+      cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
   MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()};
   return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()},
                          DRE->getSourceRange(), TheCall->getRParenLoc(), Args,
@@ -4809,9 +4817,9 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
   //   M is C if C is an integer, and ptrdiff_t if C is a pointer, and
   //   the int parameters are for orderings.
 
-  static_assert(sizeof(NumArgs)/sizeof(NumArgs[0]) == NumForm
-      && sizeof(NumVals)/sizeof(NumVals[0]) == NumForm,
-      "need to update code for modified forms");
+  static_assert(sizeof(NumArgs) / sizeof(NumArgs[0]) == NumForm &&
+                    sizeof(NumVals) / sizeof(NumVals[0]) == NumForm,
+                "need to update code for modified forms");
   static_assert(AtomicExpr::AO__atomic_add_fetch == 0 &&
                     AtomicExpr::AO__atomic_xor_fetch + 1 ==
                         AtomicExpr::AO__c11_atomic_compare_exchange_strong,
@@ -5016,7 +5024,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
 
   // For a __c11 builtin, this should be a pointer to an _Atomic type.
   QualType AtomTy = pointerType->getPointeeType(); // 'A'
-  QualType ValType = AtomTy; // 'C'
+  QualType ValType = AtomTy;                       // 'C'
   if (IsC11) {
     if (!AtomTy->isAtomicType()) {
       Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic)
@@ -5275,7 +5283,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
   }
 
   // Permute the arguments into a 'consistent' order.
-  SmallVector<Expr*, 5> SubExprs;
+  SmallVector<Expr *, 5> SubExprs;
   SubExprs.push_back(Ptr);
   switch (Form) {
   case Init:
@@ -5394,7 +5402,7 @@ static bool checkBuiltinArgument(Sema &S, CallExpr *E, unsigned ArgIndex) {
 
   ParmVarDecl *Param = Fn->getParamDecl(ArgIndex);
   InitializedEntity Entity =
-    InitializedEntity::InitializeParameter(S.Context, Param);
+      InitializedEntity::InitializeParameter(S.Context, Param);
 
   ExprResult Arg = E->getArg(ArgIndex);
   Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg);
@@ -5483,41 +5491,52 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
   // We need to figure out which concrete builtin this maps onto.  For example,
   // __sync_fetch_and_add with a 2 byte object turns into
   // __sync_fetch_and_add_2.
-#define BUILTIN_ROW(x) \
-  { Builtin::BI##x##_1, Builtin::BI##x##_2, Builtin::BI##x##_4, \
-    Builtin::BI##x##_8, Builtin::BI##x##_16 }
+#define BUILTIN_ROW(x)                                                         \
+  {                                                                            \
+    Builtin::BI##x##_1, Builtin::BI##x##_2, Builtin::BI##x##_4,                \
+        Builtin::BI##x##_8, Builtin::BI##x##_16                                \
+  }
 
   static const unsigned BuiltinIndices[][5] = {
-    BUILTIN_ROW(__sync_fetch_and_add),
-    BUILTIN_ROW(__sync_fetch_and_sub),
-    BUILTIN_ROW(__sync_fetch_and_or),
-    BUILTIN_ROW(__sync_fetch_and_and),
-    BUILTIN_ROW(__sync_fetch_and_xor),
-    BUILTIN_ROW(__sync_fetch_and_nand),
-
-    BUILTIN_ROW(__sync_add_and_fetch),
-    BUILTIN_ROW(__sync_sub_and_fetch),
-    BUILTIN_ROW(__sync_and_and_fetch),
-    BUILTIN_ROW(__sync_or_and_fetch),
-    BUILTIN_ROW(__sync_xor_and_fetch),
-    BUILTIN_ROW(__sync_nand_and_fetch),
-
-    BUILTIN_ROW(__sync_val_compare_and_swap),
-    BUILTIN_ROW(__sync_bool_compare_and_swap),
-    BUILTIN_ROW(__sync_lock_test_and_set),
-    BUILTIN_ROW(__sync_lock_release),
-    BUILTIN_ROW(__sync_swap)
-  };
+      BUILTIN_ROW(__sync_fetch_and_add),
+      BUILTIN_ROW(__sync_fetch_and_sub),
+      BUILTIN_ROW(__sync_fetch_and_or),
+      BUILTIN_ROW(__sync_fetch_and_and),
+      BUILTIN_ROW(__sync_fetch_and_xor),
+      BUILTIN_ROW(__sync_fetch_and_nand),
+
+      BUILTIN_ROW(__sync_add_and_fetch),
+      BUILTIN_ROW(__sync_sub_and_fetch),
+      BUILTIN_ROW(__sync_and_and_fetch),
+      BUILTIN_ROW(__sync_or_and_fetch),
+      BUILTIN_ROW(__sync_xor_and_fetch),
+      BUILTIN_ROW(__sync_nand_and_fetch),
+
+      BUILTIN_ROW(__sync_val_compare_and_swap),
+      BUILTIN_ROW(__sync_bool_compare_and_swap),
+      BUILTIN_ROW(__sync_lock_test_and_set),
+      BUILTIN_ROW(__sync_lock_release),
+      BUILTIN_ROW(__sync_swap)};
 #undef BUILTIN_ROW
 
   // Determine the index of the size.
   unsigned SizeIndex;
   switch (Context.getTypeSizeInChars(ValType).getQuantity()) {
-  case 1: SizeIndex = 0; break;
-  case 2: SizeIndex = 1; break;
-  case 4: SizeIndex = 2; break;
-  case 8: SizeIndex = 3; break;
-  case 16: SizeIndex = 4; break;
+  case 1:
+    SizeIndex = 0;
+    break;
+  case 2:
+    SizeIndex = 1;
+    break;
+  case 4:
+    SizeIndex = 2;
+    break;
+  case 8:
+    SizeIndex = 3;
+    break;
+  case 16:
+    SizeIndex = 4;
+    break;
   default:
     Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_pointer_size)
         << FirstArg->getType() << FirstArg->getSourceRange();
@@ -5532,7 +5551,8 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
   unsigned BuiltinIndex, NumFixed = 1;
   bool WarnAboutSemanticsChange = false;
   switch (BuiltinID) {
-  default: llvm_unreachable("Unknown overloaded atomic builtin!");
+  default:
+    llvm_unreachable("Unknown overloaded atomic builtin!");
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_add_1:
   case Builtin::BI__sync_fetch_and_add_2:
@@ -5696,7 +5716,7 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
 
   // Now that we know how many fixed arguments we expect, first check that we
   // have at least that many.
-  if (TheCall->getNumArgs() < 1+NumFixed) {
+  if (TheCall->getNumArgs() < 1 + NumFixed) {
     Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
         << 0 << 1 + NumFixed << TheCall->getNumArgs() << /*is non object*/ 0
         << Callee->getSourceRange();
@@ -5733,13 +5753,13 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
   // deduce the types of the rest of the arguments accordingly.  Walk
   // the remaining arguments, converting them to the deduced value type.
   for (unsigned i = 0; i != NumFixed; ++i) {
-    ExprResult Arg = TheCall->getArg(i+1);
+    ExprResult Arg = TheCall->getArg(i + 1);
 
     // GCC does an implicit conversion to the pointer or integer ValType.  This
     // can fail in some cases (1i -> int**), check for this error case now.
     // Initialize the argument.
-    InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
-                                                   ValType, /*consume*/ false);
+    InitializedEntity Entity = InitializedEntity::InitializeParameter(
+        Context, ValType, /*consume*/ false);
     Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
     if (Arg.isInvalid())
       return ExprError();
@@ -5750,7 +5770,7 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
     // pass in 42.  The 42 gets converted to char.  This is even more strange
     // for things like 45.123 -> char, etc.
     // FIXME: Do this check.
-    TheCall->setArg(i+1, Arg.get());
+    TheCall->setArg(i + 1, Arg.get());
   }
 
   // Create a new DeclRefExpr to refer to the new decl.
@@ -5762,8 +5782,8 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
   // Set the callee in the CallExpr.
   // FIXME: This loses syntactic information.
   QualType CalleePtrTy = Context.getPointerType(NewBuiltinDecl->getType());
-  ExprResult PromotedCall = ImpCastExprToType(NewDRE, CalleePtrTy,
-                                              CK_BuiltinFnToFnPtr);
+  ExprResult PromotedCall =
+      ImpCastExprToType(NewDRE, CalleePtrTy, CK_BuiltinFnToFnPtr);
   TheCall->setCallee(PromotedCall.get());
 
   // Change the result type of the call to match the original value type. This
@@ -5911,8 +5931,9 @@ static bool checkVAStartABI(Sema &S, unsigned BuiltinID, Expr *Fn) {
   return false;
 }
 
-static bool checkVAStartIsInVariadicFunction(Sema &S, Expr *Fn,
-                                             ParmVarDecl **LastParam = nullptr) {
+static bool
+checkVAStartIsInVariadicFunction(Sema &S, Expr *Fn,
+                                 ParmVarDecl **LastParam = nullptr) {
   // Determine whether the current function, block, or obj-c method is variadic
   // and get its parameter list.
   bool IsVariadic = false;
@@ -6036,8 +6057,10 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) {
              return !Context.typesAreCompatible(ED->getPromotionType(), Type);
            }()) {
     unsigned Reason = 0;
-    if (Type->isReferenceType())  Reason = 1;
-    else if (IsCRegister)         Reason = 2;
+    if (Type->isReferenceType())
+      Reason = 1;
+    else if (IsCRegister)
+      Reason = 2;
     Diag(Arg->getBeginLoc(), diag::warn_va_start_type_is_undefined) << Reason;
     Diag(ParamLoc, diag::note_parameter_type) << Type;
   }
@@ -6281,8 +6304,8 @@ bool Sema::BuiltinComplex(CallExpr *TheCall) {
   if (!Context.hasSameType(Real->getType(), Imag->getType())) {
     return Diag(Real->getBeginLoc(),
                 diag::err_typecheck_call_different_arg_types)
-           << Real->getType() << Imag->getType()
-           << Real->getSourceRange() << Imag->getSourceRange();
+           << Real->getType() << Imag->getType() << Real->getSourceRange()
+           << Imag->getSourceRange();
   }
 
   TheCall->setType(Context.getComplexType(Real->getType()));
@@ -6389,8 +6412,7 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
   QualType SrcTy = E->getType();
 
   if (!SrcTy->isVectorType() && !SrcTy->isDependentType())
-    return ExprError(Diag(BuiltinLoc,
-                          diag::err_convertvector_non_vector)
+    return ExprError(Diag(BuiltinLoc, diag::err_convertvector_non_vector)
                      << E->getSourceRange());
   if (!DstTy->isVectorType() && !DstTy->isDependentType())
     return ExprError(Diag(BuiltinLoc, diag::err_builtin_non_vector_type)
@@ -6401,9 +6423,9 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
     unsigned SrcElts = SrcTy->castAs<VectorType>()->getNumElements();
     unsigned DstElts = DstTy->castAs<VectorType>()->getNumElements();
     if (SrcElts != DstElts)
-      return ExprError(Diag(BuiltinLoc,
-                            diag::err_convertvector_incompatible_vector)
-                       << E->getSourceRange());
+      return ExprError(
+          Diag(BuiltinLoc, diag::err_convertvector_incompatible_vector)
+          << E->getSourceRange());
   }
 
   return ConvertVectorExpr::Create(Context, E, TInfo, DstTy, VK, OK, BuiltinLoc,
@@ -6452,7 +6474,8 @@ bool Sema::BuiltinArithmeticFence(CallExpr *TheCall) {
 
 bool Sema::BuiltinAssume(CallExpr *TheCall) {
   Expr *Arg = TheCall->getArg(0);
-  if (Arg->isInstantiationDependent()) return false;
+  if (Arg->isInstantiationDependent())
+    return false;
 
   if (Arg->HasSideEffects(Context))
     Diag(Arg->getBeginLoc(), diag::warn_assume_side_effects)
@@ -6501,8 +6524,7 @@ bool Sema::BuiltinAssumeAligned(CallExpr *TheCall) {
   Expr *FirstArg = TheCall->getArg(0);
 
   {
-    ExprResult FirstArgResult =
-        DefaultFunctionArrayLvalueConversion(FirstArg);
+    ExprResult FirstArgResult = DefaultFunctionArrayLvalueConversion(FirstArg);
     if (!FirstArgResult.get()->getType()->isPointerType()) {
       Diag(TheCall->getBeginLoc(), diag::err_builtin_assume_aligned_invalid_arg)
           << TheCall->getSourceRange();
@@ -6913,9 +6935,7 @@ class UncoveredArgHandler {
 public:
   UncoveredArgHandler() = default;
 
-  bool hasUncoveredArg() const {
-    return (FirstUncoveredArg >= 0);
-  }
+  bool hasUncoveredArg() const { return (FirstUncoveredArg >= 0); }
 
   unsigned getUncoveredArg() const {
     assert(hasUncoveredArg() && "no uncovered argument");
@@ -6959,8 +6979,7 @@ enum StringLiteralCheckType {
 } // namespace
 
 static void sumOffsets(llvm::APSInt &Offset, llvm::APSInt Addend,
-                                     BinaryOperatorKind BinOpKind,
-                                     bool AddendIsRight) {
+                       BinaryOperatorKind BinOpKind, bool AddendIsRight) {
   unsigned BitWidth = Offset.getBitWidth();
   unsigned AddendBitWidth = Addend.getBitWidth();
   // There might be negative interim results.
@@ -7034,10 +7053,11 @@ class FormatStringLiteral {
   bool isUTF32() const { return FExpr->isUTF32(); }
   bool isPascal() const { return FExpr->isPascal(); }
 
-  SourceLocation getLocationOfByte(
-      unsigned ByteNo, const SourceManager &SM, const LangOptions &Features,
-      const TargetInfo &Target, unsigned *StartToken = nullptr,
-      unsigned *StartTokenByteOffset = nullptr) const {
+  SourceLocation
+  getLocationOfByte(unsigned ByteNo, const SourceManager &SM,
+                    const LangOptions &Features, const TargetInfo &Target,
+                    unsigned *StartToken = nullptr,
+                    unsigned *StartTokenByteOffset = nullptr) const {
     return FExpr->getLocationOfByte(ByteNo + Offset, SM, Features, Target,
                                     StartToken, StartTokenByteOffset);
   }
@@ -7109,8 +7129,7 @@ checkFormatStringExpr(Sema &S, const StringLiteral *ReferenceFormatString,
   case Stmt::ConditionalOperatorClass: {
     // The expression is a literal if both sub-expressions were, and it was
     // completely checked only if both sub-expressions were checked.
-    const AbstractConditionalOperator *C =
-        cast<AbstractConditionalOperator>(E);
+    const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(E);
 
     // Determine whether it is necessary to check both sub-expressions, for
     // example, because the condition expression is a constant that can be
@@ -7326,7 +7345,8 @@ checkFormatStringExpr(Sema &S, const StringLiteral *ReferenceFormatString,
   case Stmt::CallExprClass:
   case Stmt::CXXMemberCallExprClass: {
     const CallExpr *CE = cast<CallExpr>(E);
-    if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
+    if (const NamedDecl *ND =
+            dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
       bool IsFirst = true;
       StringLiteralCheckType CommonResult;
       for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
@@ -7740,7 +7760,7 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
   if (UncoveredArg.hasUncoveredArg()) {
     unsigned ArgIdx = UncoveredArg.getUncoveredArg() + firstDataArg;
     assert(ArgIdx < Args.size() && "ArgIdx outside bounds");
-    UncoveredArg.Diagnose(*this, /*IsFunctionCall*/true, Args[ArgIdx]);
+    UncoveredArg.Diagnose(*this, /*IsFunctionCall*/ true, Args[ArgIdx]);
   }
 
   if (CT != SLCT_NotALiteral)
@@ -7770,7 +7790,7 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
   // warn only with -Wformat-nonliteral.
   if (Args.size() == firstDataArg) {
     Diag(FormatLoc, diag::warn_format_nonliteral_noargs)
-      << OrigFormatExpr->getSourceRange();
+        << OrigFormatExpr->getSourceRange();
     switch (Type) {
     default:
       break;
@@ -7778,16 +7798,16 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
     case FormatStringType::FreeBSDKPrintf:
     case FormatStringType::Printf:
       Diag(FormatLoc, diag::note_format_security_fixit)
-        << FixItHint::CreateInsertion(FormatLoc, "\"%s\", ");
+          << FixItHint::CreateInsertion(FormatLoc, "\"%s\", ");
       break;
     case FormatStringType::NSString:
       Diag(FormatLoc, diag::note_format_security_fixit)
-        << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", ");
+          << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", ");
       break;
     }
   } else {
     Diag(FormatLoc, diag::warn_format_nonliteral)
-      << OrigFormatExpr->getSourceRange();
+        << OrigFormatExpr->getSourceRange();
   }
   return false;
 }
@@ -8774,11 +8794,11 @@ bool DecomposePrintfHandler::HandlePrintfSpecifier(
 // Determines if the specified is a C++ class or struct containing
 // a member with the specified name and kind (e.g. a CXXMethodDecl named
 // "c_str()").
-template<typename MemberKind>
-static llvm::SmallPtrSet<MemberKind*, 1>
+template <typename MemberKind>
+static llvm::SmallPtrSet<MemberKind *, 1>
 CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
   auto *RD = Ty->getAsCXXRecordDecl();
-  llvm::SmallPtrSet<MemberKind*, 1> Results;
+  llvm::SmallPtrSet<MemberKind *, 1> Results;
 
   if (!RD || !(RD->isBeingDefined() || RD->isCompleteDefinition()))
     return Results;
@@ -8807,8 +8827,8 @@ bool Sema::hasCStrMethod(const Expr *E) {
 
   MethodSet Results =
       CXXRecordMembersNamed<CXXMethodDecl>("c_str", *this, E->getType());
-  for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
-       MI != ME; ++MI)
+  for (MethodSet::iterator MI = Results.begin(), ME = Results.end(); MI != ME;
+       ++MI)
     if ((*MI)->getMinRequiredArguments() == 0)
       return true;
   return false;
@@ -8817,15 +8837,15 @@ bool Sema::hasCStrMethod(const Expr *E) {
 // Check if a (w)string was passed when a (w)char* was needed, and offer a
 // better diagnostic if so. AT is assumed to be valid.
 // Returns true when a c_str() conversion method is found.
-bool CheckPrintfHandler::checkForCStrMembers(
-    const analyze_printf::ArgType &AT, const Expr *E) {
+bool CheckPrintfHandler::checkForCStrMembers(const analyze_printf::ArgType &AT,
+                                             const Expr *E) {
   using MethodSet = llvm::SmallPtrSet<CXXMethodDecl *, 1>;
 
   MethodSet Results =
       CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType());
 
-  for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
-       MI != ME; ++MI) {
+  for (MethodSet::iterator MI = Results.begin(), ME = Results.end(); MI != ME;
+       ++MI) {
     const CXXMethodDecl *Method = *MI;
     if (Method->getMinRequiredArguments() == 0 &&
         AT.matchesType(S.Context, Method->getReturnType())) {
@@ -10039,7 +10059,7 @@ static unsigned getLargerAbsoluteValueFunction(unsigned AbsFunction) {
     return 0;
 
   case Builtin::BIcabsf:
-   return Builtin::BIcabs;
+    return Builtin::BIcabs;
   case Builtin::BIcabs:
     return Builtin::BIcabsl;
   case Builtin::BIcabsl:
@@ -10089,11 +10109,7 @@ static unsigned getBestAbsFunction(ASTContext &Context, QualType ArgType,
   return BestKind;
 }
 
-enum AbsoluteValueKind {
-  AVK_Integer,
-  AVK_Floating,
-  AVK_Complex
-};
+enum AbsoluteValueKind { AVK_Integer, AVK_Floating, AVK_Complex };
 
 static AbsoluteValueKind getAbsoluteValueKind(QualType T) {
   if (T->isIntegralOrEnumerationType())
@@ -10283,8 +10299,8 @@ static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range,
   if (!EmitHeaderHint)
     return;
 
-  S.Diag(Loc, diag::note_include_header_or_declare) << HeaderName
-                                                    << FunctionName;
+  S.Diag(Loc, diag::note_include_header_or_declare)
+      << HeaderName << FunctionName;
 }
 
 template <std::size_t StrLen>
@@ -10439,32 +10455,44 @@ void Sema::CheckAbsoluteValueFunction(const CallExpr *Call,
 //===--- CHECK: Warn on use of std::max and unsigned zero. r---------------===//
 void Sema::CheckMaxUnsignedZero(const CallExpr *Call,
                                 const FunctionDecl *FDecl) {
-  if (!Call || !FDecl) return;
+  if (!Call || !FDecl)
+    return;
 
   // Ignore template specializations and macros.
-  if (inTemplateInstantiation()) return;
-  if (Call->getExprLoc().isMacroID()) return;
+  if (inTemplateInstantiation())
+    return;
+  if (Call->getExprLoc().isMacroID())
+    return;
 
   // Only care about the one template argument, two function parameter std::max
-  if (Call->getNumArgs() != 2) return;
-  if (!IsStdFunction(FDecl, "max")) return;
-  const auto * ArgList = FDecl->getTemplateSpecializationArgs();
-  if (!ArgList) return;
-  if (ArgList->size() != 1) return;
+  if (Call->getNumArgs() != 2)
+    return;
+  if (!IsStdFunction(FDecl, "max"))
+    return;
+  const auto *ArgList = FDecl->getTemplateSpecializationArgs();
+  if (!ArgList)
+    return;
+  if (ArgList->size() != 1)
+    return;
 
   // Check that template type argument is unsigned integer.
-  const auto& TA = ArgList->get(0);
-  if (TA.getKind() != TemplateArgument::Type) return;
+  const auto &TA = ArgList->get(0);
+  if (TA.getKind() != TemplateArgument::Type)
+    return;
   QualType ArgType = TA.getAsType();
-  if (!ArgType->isUnsignedIntegerType()) return;
+  if (!ArgType->isUnsignedIntegerType())
+    return;
 
   // See if either argument is a literal zero.
-  auto IsLiteralZeroArg = [](const Expr* E) -> bool {
+  auto IsLiteralZeroArg = [](const Expr *E) -> bool {
     const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
-    if (!MTE) return false;
+    if (!MTE)
+      return false;
     const auto *Num = dyn_cast<IntegerLiteral>(MTE->getSubExpr());
-    if (!Num) return false;
-    if (Num->getValue() != 0) return false;
+    if (!Num)
+      return false;
+    if (Num->getValue() != 0)
+      return false;
     return true;
   };
 
@@ -10474,7 +10502,8 @@ void Sema::CheckMaxUnsignedZero(const CallExpr *Call,
   const bool IsSecondArgZero = IsLiteralZeroArg(SecondArg);
 
   // Only warn when exactly one argument is zero.
-  if (IsFirstArgZero == IsSecondArgZero) return;
+  if (IsFirstArgZero == IsSecondArgZero)
+    return;
 
   SourceRange FirstRange = FirstArg->getSourceRange();
   SourceRange SecondRange = SecondArg->getSourceRange();
@@ -10495,8 +10524,8 @@ void Sema::CheckMaxUnsignedZero(const CallExpr *Call,
   }
 
   Diag(Call->getExprLoc(), diag::note_remove_max_call)
-        << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange())
-        << FixItHint::CreateRemoval(RemovalRange);
+      << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange())
+      << FixItHint::CreateRemoval(RemovalRange);
 }
 
 //===--- CHECK: Standard memory functions ---------------------------------===//
@@ -10680,7 +10709,7 @@ struct SearchNonTrivialToCopyField
   Sema &S;
 };
 
-}
+} // namespace
 
 /// Detect if \c SizeofExpr is likely to calculate the sizeof an object.
 static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) {
@@ -10724,7 +10753,7 @@ static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr *Call) {
     return;
 
   const Expr *SizeArg =
-    Call->getArg(BId == Builtin::BImemset ? 2 : 1)->IgnoreImpCasts();
+      Call->getArg(BId == Builtin::BImemset ? 2 : 1)->IgnoreImpCasts();
 
   auto isLiteralZero = [](const Expr *E) {
     return (isa<IntegerLiteral>(E) &&
@@ -10758,8 +10787,7 @@ static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr *Call) {
   // If the second argument to a memset is a sizeof expression and the third
   // isn't, this is also likely an error. This should catch
   // 'memset(buf, sizeof(buf), 0xff)'.
-  if (BId == Builtin::BImemset &&
-      doesExprLikelyComputeSize(Call->getArg(1)) &&
+  if (BId == Builtin::BImemset && doesExprLikelyComputeSize(Call->getArg(1)) &&
       !doesExprLikelyComputeSize(Call->getArg(2))) {
     SourceLocation DiagLoc = Call->getArg(1)->getExprLoc();
     S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 1;
@@ -10768,8 +10796,7 @@ static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr *Call) {
   }
 }
 
-void Sema::CheckMemaccessArguments(const CallExpr *Call,
-                                   unsigned BId,
+void Sema::CheckMemaccessArguments(const CallExpr *Call, unsigned BId,
                                    IdentifierInfo *FnName) {
   assert(BId != 0);
 
@@ -10781,7 +10808,9 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
     return;
 
   unsigned LastArg = (BId == Builtin::BImemset || BId == Builtin::BIbzero ||
-                      BId == Builtin::BIstrndup ? 1 : 2);
+                              BId == Builtin::BIstrndup
+                          ? 1
+                          : 2);
   unsigned LenArg =
       (BId == Builtin::BIbzero || BId == Builtin::BIstrndup ? 1 : 2);
   const Expr *LenExpr = Call->getArg(LenArg)->IgnoreParenImpCasts();
@@ -10832,9 +10861,9 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
             Context.typesAreCompatible(SizeOfArgTy, DestTy)) {
           DiagRuntimeBehavior(LenExpr->getExprLoc(), Dest,
                               PDiag(diag::warn_sizeof_pointer_type_memaccess)
-                                << FnName << SizeOfArgTy << ArgIdx
-                                << PointeeTy << Dest->getSourceRange()
-                                << LenExpr->getSourceRange());
+                                  << FnName << SizeOfArgTy << ArgIdx
+                                  << PointeeTy << Dest->getSourceRange()
+                                  << LenExpr->getSourceRange());
           break;
         }
       }
@@ -10857,7 +10886,7 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
       if (ArgIdx != 0 || IsCmp) {
         if (BId == Builtin::BImemcpy)
           OperationType = 1;
-        else if(BId == Builtin::BImemmove)
+        else if (BId == Builtin::BImemmove)
           OperationType = 2;
         else if (IsCmp)
           OperationType = 3;
@@ -10869,12 +10898,11 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
                               << IsContained << ContainedRD << OperationType
                               << Call->getCallee()->getSourceRange());
     } else if (PointeeTy.hasNonTrivialObjCLifetime() &&
-             BId != Builtin::BImemset)
-      DiagRuntimeBehavior(
-        Dest->getExprLoc(), Dest,
-        PDiag(diag::warn_arc_object_memaccess)
-          << ArgIdx << FnName << PointeeTy
-          << Call->getCallee()->getSourceRange());
+               BId != Builtin::BImemset)
+      DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+                          PDiag(diag::warn_arc_object_memaccess)
+                              << ArgIdx << FnName << PointeeTy
+                              << Call->getCallee()->getSourceRange());
     else if (const auto *RD = PointeeTy->getAsRecordDecl()) {
 
       // FIXME: Do not consider incomplete types even though they may be
@@ -10918,9 +10946,9 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
       continue;
 
     DiagRuntimeBehavior(
-      Dest->getExprLoc(), Dest,
-      PDiag(diag::note_bad_memaccess_silence)
-        << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
+        Dest->getExprLoc(), Dest,
+        PDiag(diag::note_bad_memaccess_silence)
+            << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
     break;
   }
 }
@@ -10997,7 +11025,7 @@ static const Expr *ignoreLiteralAdditions(const Expr *Ex, ASTContext &Ctx) {
   Ex = Ex->IgnoreParenCasts();
 
   while (true) {
-    const BinaryOperator * BO = dyn_cast<BinaryOperator>(Ex);
+    const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex);
     if (!BO || !BO->isAdditiveOp())
       break;
 
@@ -11167,8 +11195,8 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
 
   // Check if the destination is an array (rather than a pointer to an array).
   QualType DstTy = DstArg->getType();
-  bool isKnownSizeArray = isConstantSizeArrayWithMoreThanOneElement(DstTy,
-                                                                    Context);
+  bool isKnownSizeArray =
+      isConstantSizeArrayWithMoreThanOneElement(DstTy, Context);
   if (!isKnownSizeArray) {
     if (PatternType == 1)
       Diag(SL, diag::warn_strncat_wrong_size) << SR;
@@ -11192,7 +11220,7 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
   OS << ") - 1";
 
   Diag(SL, diag::note_strncat_wrong_size)
-    << FixItHint::CreateReplacement(SR, OS.str());
+      << FixItHint::CreateReplacement(SR, OS.str());
 }
 
 namespace {
@@ -11309,18 +11337,15 @@ void Sema::CheckFreeArguments(const CallExpr *E) {
     return CheckFreeArgumentsCast(*this, CalleeName, Cast);
 }
 
-void
-Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
-                         SourceLocation ReturnLoc,
-                         bool isObjCMethod,
-                         const AttrVec *Attrs,
-                         const FunctionDecl *FD) {
+void Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
+                              SourceLocation ReturnLoc, bool isObjCMethod,
+                              const AttrVec *Attrs, const FunctionDecl *FD) {
   // Check if the return value is null but should not be.
   if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
        (!isObjCMethod && isNonNullType(lhsType))) &&
       CheckNonNullExpr(*this, RetValExp))
     Diag(ReturnLoc, diag::warn_null_ret)
-      << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();
+        << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();
 
   // C++11 [basic.stc.dynamic.allocation]p4:
   //   If an allocation function declared with a non-throwing
@@ -11330,12 +11355,12 @@ Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
   if (FD) {
     OverloadedOperatorKind Op = FD->getOverloadedOperator();
     if (Op == OO_New || Op == OO_Array_New) {
-      const FunctionProtoType *Proto
-        = FD->getType()->castAs<FunctionProtoType>();
-      if (!Proto->isNothrow(/*ResultIfDependent*/true) &&
+      const FunctionProtoType *Proto =
+          FD->getType()->castAs<FunctionProtoType>();
+      if (!Proto->isNothrow(/*ResultIfDependent*/ true) &&
           CheckNonNullExpr(*this, RetValExp))
         Diag(ReturnLoc, diag::warn_operator_new_returns_null)
-          << FD << getLangOpts().CPlusPlus11;
+            << FD << getLangOpts().CPlusPlus11;
     }
   }
 
@@ -11417,7 +11442,7 @@ void Sema::CheckFloatComparison(SourceLocation Loc, const Expr *LHS,
 
   // Emit the diagnostic.
   Diag(Loc, diag::warn_floatingpoint_eq)
-    << LHS->getSourceRange() << RHS->getSourceRange();
+      << LHS->getSourceRange() << RHS->getSourceRange();
 }
 
 //===--- CHECK: Integer mixed-sign comparisons (-Wsign-compare) --------===//
@@ -11441,19 +11466,15 @@ struct IntRange {
       : Width(Width), NonNegative(NonNegative) {}
 
   /// Number of bits excluding the sign bit.
-  unsigned valueBits() const {
-    return NonNegative ? Width : Width - 1;
-  }
+  unsigned valueBits() const { return NonNegative ? Width : Width - 1; }
 
   /// Returns the range of the bool type.
-  static IntRange forBoolType() {
-    return IntRange(1, true);
-  }
+  static IntRange forBoolType() { return IntRange(1, true); }
 
   /// Returns the range of an opaque value of the given integral type.
   static IntRange forValueOfType(ASTContext &C, QualType T) {
     return forValueOfCanonicalType(C,
-                          T->getCanonicalTypeInternal().getTypePtr());
+                                   T->getCanonicalTypeInternal().getTypePtr());
   }
 
   /// Returns the range of an opaque value of a canonical integral type.
@@ -11488,10 +11509,10 @@ struct IntRange {
       unsigned NumNegative = Enum->getNumNegativeBits();
 
       if (NumNegative == 0)
-        return IntRange(NumPositive, true/*NonNegative*/);
+        return IntRange(NumPositive, true /*NonNegative*/);
       else
         return IntRange(std::max(NumPositive + 1, NumNegative),
-                        false/*NonNegative*/);
+                        false /*NonNegative*/);
     }
 
     if (const auto *EIT = dyn_cast<BitIntType>(T))
@@ -11785,8 +11806,8 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
     case BO_Shl:
       // ...except that we want to treat '1 << (blah)' as logically
       // positive.  It's an important idiom.
-      if (IntegerLiteral *I
-            = dyn_cast<IntegerLiteral>(BO->getLHS()->IgnoreParenCasts())) {
+      if (IntegerLiteral *I =
+              dyn_cast<IntegerLiteral>(BO->getLHS()->IgnoreParenCasts())) {
         if (I->getValue() == 1) {
           IntRange R = IntRange::forValueOfType(C, GetExprType(E));
           return IntRange(R.Width, /*NonNegative*/ true);
@@ -12040,8 +12061,8 @@ static bool IsEnumConstOrFromMacro(Sema &S, const Expr *E) {
   if (BeginLoc.isMacroID()) {
     StringRef MacroName = Lexer::getImmediateMacroName(
         BeginLoc, S.getSourceManager(), S.getLangOpts());
-    return MacroName != "YES" && MacroName != "NO" &&
-           MacroName != "true" && MacroName != "false";
+    return MacroName != "YES" && MacroName != "NO" && MacroName != "true" &&
+           MacroName != "false";
   }
 
   return false;
@@ -12118,21 +12139,30 @@ struct PromotedRange {
            Value.isUnsigned() == PromotedMin.isUnsigned());
     if (!isContiguous()) {
       assert(Value.isUnsigned() && "discontiguous range for signed compare");
-      if (Value.isMinValue()) return Min;
-      if (Value.isMaxValue()) return Max;
-      if (Value >= PromotedMin) return InRange;
-      if (Value <= PromotedMax) return InRange;
+      if (Value.isMinValue())
+        return Min;
+      if (Value.isMaxValue())
+        return Max;
+      if (Value >= PromotedMin)
+        return InRange;
+      if (Value <= PromotedMax)
+        return InRange;
       return InHole;
     }
 
     switch (llvm::APSInt::compareValues(Value, PromotedMin)) {
-    case -1: return Less;
-    case 0: return PromotedMin == PromotedMax ? OnlyValue : Min;
+    case -1:
+      return Less;
+    case 0:
+      return PromotedMin == PromotedMax ? OnlyValue : Min;
     case 1:
       switch (llvm::APSInt::compareValues(Value, PromotedMax)) {
-      case -1: return InRange;
-      case 0: return Max;
-      case 1: return Greater;
+      case -1:
+        return InRange;
+      case 0:
+        return Max;
+      case 1:
+        return Greater;
       }
     }
 
@@ -12143,11 +12173,15 @@ struct PromotedRange {
   constantValue(BinaryOperatorKind Op, ComparisonResult R, bool ConstantOnRHS) {
     if (Op == BO_Cmp) {
       ComparisonResult LTFlag = LT, GTFlag = GT;
-      if (ConstantOnRHS) std::swap(LTFlag, GTFlag);
-
-      if (R & EQ) return StringRef("'std::strong_ordering::equal'");
-      if (R & LTFlag) return StringRef("'std::strong_ordering::less'");
-      if (R & GTFlag) return StringRef("'std::strong_ordering::greater'");
+      if (ConstantOnRHS)
+        std::swap(LTFlag, GTFlag);
+
+      if (R & EQ)
+        return StringRef("'std::strong_ordering::equal'");
+      if (R & LTFlag)
+        return StringRef("'std::strong_ordering::less'");
+      if (R & GTFlag)
+        return StringRef("'std::strong_ordering::greater'");
       return std::nullopt;
     }
 
@@ -12176,13 +12210,12 @@ struct PromotedRange {
     return std::nullopt;
   }
 };
-}
+} // namespace
 
 static bool HasEnumType(const Expr *E) {
   // Strip off implicit integral promotions.
   while (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
-    if (ICE->getCastKind() != CK_IntegralCast &&
-        ICE->getCastKind() != CK_NoOp)
+    if (ICE->getCastKind() != CK_IntegralCast && ICE->getCastKind() != CK_NoOp)
       break;
     E = ICE->getSubExpr();
   }
@@ -12193,11 +12226,7 @@ static bool HasEnumType(const Expr *E) {
 static int classifyConstantValue(Expr *Constant) {
   // The values of this enumeration are used in the diagnostics
   // diag::warn_out_of_range_compare and diag::warn_tautological_bool_compare.
-  enum ConstantValueKind {
-    Miscellaneous = 0,
-    LiteralTrue,
-    LiteralFalse
-  };
+  enum ConstantValueKind { Miscellaneous = 0, LiteralTrue, LiteralFalse };
   if (auto *BL = dyn_cast<CXXBoolLiteralExpr>(Constant))
     return BL->getValue() ? ConstantValueKind::LiteralTrue
                           : ConstantValueKind::LiteralFalse;
@@ -12307,7 +12336,7 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
   if (ED) {
     OS << '\'' << *ED << "' (" << Value << ")";
   } else if (auto *BL = dyn_cast<ObjCBoolLiteralExpr>(
-               Constant->IgnoreParenImpCasts())) {
+                 Constant->IgnoreParenImpCasts())) {
     OS << (BL->getValue() ? "YES" : "NO");
   } else {
     OS << Value;
@@ -12346,8 +12375,8 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
         (isKnownToHaveUnsignedValue(OriginalOther) && Value == 0)
             ? (HasEnumType(OriginalOther)
                    ? diag::warn_unsigned_enum_always_true_comparison
-                   : IsCharTy ? diag::warn_unsigned_char_always_true_comparison
-                              : diag::warn_unsigned_always_true_comparison)
+               : IsCharTy ? diag::warn_unsigned_char_always_true_comparison
+                          : diag::warn_unsigned_always_true_comparison)
             : diag::warn_tautological_constant_compare;
 
     S.Diag(E->getOperatorLoc(), Diag)
@@ -12498,7 +12527,7 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
   // White-list bool bitfields.
   QualType BitfieldType = Bitfield->getType();
   if (BitfieldType->isBooleanType())
-     return false;
+    return false;
 
   if (auto *BitfieldEnumDecl = BitfieldType->getAsEnumDecl()) {
     // If the underlying enum type was not explicitly specified as an unsigned
@@ -12515,8 +12544,7 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
 
   // Ignore value- or type-dependent expressions.
   if (Bitfield->getBitWidth()->isValueDependent() ||
-      Bitfield->getBitWidth()->isTypeDependent() ||
-      Init->isValueDependent() ||
+      Bitfield->getBitWidth()->isTypeDependent() || Init->isValueDependent() ||
       Init->isTypeDependent())
     return false;
 
@@ -12695,7 +12723,7 @@ static void DiagnoseImpCast(Sema &S, const Expr *E, QualType SourceType,
     return;
   }
   S.Diag(E->getExprLoc(), diag)
-    << SourceType << T << E->getSourceRange() << SourceRange(CContext);
+      << SourceType << T << E->getSourceRange() << SourceRange(CContext);
 }
 
 /// Diagnose an implicit cast;  purely a helper for CheckImplicitConversion.
@@ -12721,7 +12749,7 @@ static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
 
   llvm::APFloat Value(0.0);
   bool IsConstant =
-    E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects);
+      E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects);
   if (!IsConstant) {
     if (S.ObjC().isSignedCharBool(T)) {
       return S.ObjC().adornBoolConversionDiagWithTernaryFixit(
@@ -12729,8 +12757,8 @@ static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
                  << E->getType());
     }
 
-    return DiagnoseImpCast(S, E, T, CContext,
-                           diag::warn_impcast_float_integer, PruneWarnings);
+    return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer,
+                           PruneWarnings);
   }
 
   bool isExact = false;
@@ -12757,7 +12785,8 @@ static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
   }
 
   if (Result == llvm::APFloat::opOK && isExact) {
-    if (IsLiteral) return;
+    if (IsLiteral)
+      return;
     return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer,
                            PruneWarnings);
   }
@@ -12776,7 +12805,7 @@ static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
     // Warn on floating point literal to integer.
     DiagID = diag::warn_impcast_literal_float_to_integer;
   } else if (IntegerValue == 0) {
-    if (Value.isZero()) {  // Skip -0.0 to 0 conversion.
+    if (Value.isZero()) { // Skip -0.0 to 0 conversion.
       return DiagnoseImpCast(S, E, T, CContext,
                              diag::warn_impcast_float_integer, PruneWarnings);
     }
@@ -12788,7 +12817,7 @@ static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
         return DiagnoseImpCast(S, E, T, CContext,
                                diag::warn_impcast_float_integer, PruneWarnings);
       }
-    } else {  // IntegerValue.isSigned()
+    } else { // IntegerValue.isSigned()
       if (!IntegerValue.isMaxSignedValue() &&
           !IntegerValue.isMinSignedValue()) {
         return DiagnoseImpCast(S, E, T, CContext,
@@ -12837,7 +12866,8 @@ static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
                         ->getAs<BuiltinType>();
 
   // The below checks assume source is floating point.
-  if (!ResultBT || !RBT || !RBT->isFloatingPoint()) return;
+  if (!ResultBT || !RBT || !RBT->isFloatingPoint())
+    return;
 
   // If source is floating point but target is an integer.
   if (ResultBT->isInteger())
@@ -12858,7 +12888,8 @@ static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
 
 static std::string PrettyPrintInRange(const llvm::APSInt &Value,
                                       IntRange Range) {
-  if (!Range.Width) return "0";
+  if (!Range.Width)
+    return "0";
 
   llvm::APSInt ValueInRange = Value;
   ValueInRange.setIsSigned(!Range.NonNegative);
@@ -12874,7 +12905,7 @@ static bool IsImplicitBoolFloatConversion(Sema &S, const Expr *Ex,
   const Expr *InnerE = Ex->IgnoreParenImpCasts();
   const Type *Target = S.Context.getCanonicalType(Ex->getType()).getTypePtr();
   const Type *Source =
-    S.Context.getCanonicalType(InnerE->getType()).getTypePtr();
+      S.Context.getCanonicalType(InnerE->getType()).getTypePtr();
   if (Target->isDependentType())
     return false;
 
@@ -12899,8 +12930,7 @@ static void CheckImplicitArgumentConversions(Sema &S, const CallExpr *TheCall,
                                        S, TheCall->getArg(I + 1), false));
     if (IsSwapped) {
       // Warn on this floating-point to bool conversion.
-      DiagnoseImpCast(S, CurrA->IgnoreParenImpCasts(),
-                      CurrA->getType(), CC,
+      DiagnoseImpCast(S, CurrA->IgnoreParenImpCasts(), CurrA->getType(), CC,
                       diag::warn_impcast_floating_point_to_bool);
     }
   }
@@ -13102,12 +13132,15 @@ bool Sema::DiscardingCFIUncheckedCallee(QualType From, QualType To) const {
 
 void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
                                    bool *ICContext, bool IsListInit) {
-  if (E->isTypeDependent() || E->isValueDependent()) return;
+  if (E->isTypeDependent() || E->isValueDependent())
+    return;
 
   const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr();
   const Type *Target = Context.getCanonicalType(T).getTypePtr();
-  if (Source == Target) return;
-  if (Target->isDependentType()) return;
+  if (Source == Target)
+    return;
+  if (Target->isDependentType())
+    return;
 
   // If the conversion context location is invalid don't complain. We also
   // don't want to emit a warning if the issue occurs from the expansion of
@@ -13685,7 +13718,8 @@ static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
 
   // If -Wconversion would have warned about either of the candidates
   // for a signedness conversion to the context type...
-  if (!Suspicious) return;
+  if (!Suspicious)
+    return;
 
   // ...but it's currently ignored...
   if (!S.Diags.isIgnored(diag::warn_impcast_integer_sign_conditional, CC))
@@ -13693,7 +13727,8 @@ static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
 
   // ...then check whether it would have warned about either of the
   // candidates for a signedness conversion to the condition type.
-  if (E->getType() == T) return;
+  if (E->getType() == T)
+    return;
 
   Suspicious = false;
   S.CheckImplicitConversion(TrueExpr->IgnoreParenImpCasts(), E->getType(), CC,
@@ -13722,7 +13757,7 @@ struct AnalyzeImplicitConversionsWorkItem {
   SourceLocation CC;
   bool IsListInit;
 };
-}
+} // namespace
 
 static void CheckCommaOperand(
     Sema &S, Expr *E, QualType T, SourceLocation CC,
@@ -13895,10 +13930,12 @@ static void AnalyzeImplicitConversions(
   // we don't really need to recurse into them, because any internal
   // expressions should have been analyzed already when they were
   // built into statements.
-  if (isa<StmtExpr>(E)) return;
+  if (isa<StmtExpr>(E))
+    return;
 
   // Don't descend into unevaluated contexts.
-  if (isa<UnaryExprOrTypeTraitExpr>(E)) return;
+  if (isa<UnaryExprOrTypeTraitExpr>(E))
+    return;
 
   // Now just recurse over the expression's children.
   CC = E->getExprLoc();
@@ -13949,7 +13986,7 @@ static void AnalyzeImplicitConversions(
 /// implicit conversions in the given expression.  There are a couple
 /// of competing diagnostics here, -Wconversion and -Wsign-compare.
 static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC,
-                                       bool IsListInit/*= false*/) {
+                                       bool IsListInit /*= false*/) {
   llvm::SmallVector<AnalyzeImplicitConversionsWorkItem, 16> WorkList;
   WorkList.push_back({OrigE, CC, IsListInit});
   while (!WorkList.empty())
@@ -14041,8 +14078,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
     unsigned DiagID = IsCompare
                           ? diag::warn_address_of_reference_null_compare
                           : diag::warn_address_of_reference_bool_conversion;
-    PartialDiagnostic PD = PDiag(DiagID) << E->getSourceRange() << Range
-                                         << IsEqual;
+    PartialDiagnostic PD = PDiag(DiagID)
+                           << E->getSourceRange() << Range << IsEqual;
     if (CheckForReference(*this, E, PD)) {
       return;
     }
@@ -14055,8 +14092,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
     E->printPretty(S, nullptr, getPrintingPolicy());
     unsigned DiagID = IsCompare ? diag::warn_nonnull_expr_compare
                                 : diag::warn_cast_nonnull_to_bool;
-    Diag(E->getExprLoc(), DiagID) << IsParam << S.str()
-      << E->getSourceRange() << Range << IsEqual;
+    Diag(E->getExprLoc(), DiagID)
+        << IsParam << S.str() << E->getSourceRange() << Range << IsEqual;
     Diag(NonnullAttr->getLocation(), diag::note_declared_nonnull) << IsParam;
   };
 
@@ -14097,7 +14134,7 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
     return;
 
   // Check for parameter decl with nonnull attribute
-  if (const auto* PV = dyn_cast<ParmVarDecl>(D)) {
+  if (const auto *PV = dyn_cast<ParmVarDecl>(D)) {
     if (getCurFunction() &&
         !getCurFunction()->ModifiedNonNullParams.count(PV)) {
       if (const Attr *A = PV->getAttr<NonNullAttr>()) {
@@ -14115,8 +14152,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
 
         for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
           if (!NonNull->args_size()) {
-              ComplainAboutNonnullParamOrCall(NonNull);
-              return;
+            ComplainAboutNonnullParamOrCall(NonNull);
+            return;
           }
 
           for (const ParamIdx &ArgNo : NonNull->args()) {
@@ -14150,11 +14187,7 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
 
   unsigned DiagID = IsCompare ? diag::warn_null_pointer_compare
                               : diag::warn_impcast_pointer_to_bool;
-  enum {
-    AddressOf,
-    FunctionPointer,
-    ArrayPointer
-  } DiagType;
+  enum { AddressOf, FunctionPointer, ArrayPointer } DiagType;
   if (IsAddressOf)
     DiagType = AddressOf;
   else if (IsFunction)
@@ -14163,8 +14196,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
     DiagType = ArrayPointer;
   else
     llvm_unreachable("Could not determine diagnostic.");
-  Diag(E->getExprLoc(), DiagID) << DiagType << S.str() << E->getSourceRange()
-                                << Range << IsEqual;
+  Diag(E->getExprLoc(), DiagID)
+      << DiagType << S.str() << E->getSourceRange() << Range << IsEqual;
 
   if (!IsFunction)
     return;
@@ -14259,7 +14292,7 @@ void Sema::CheckBoolLikeConversion(Expr *E, SourceLocation CC) {
   ::CheckBoolLikeConversion(*this, E, CC);
 }
 
-void Sema::CheckForIntOverflow (const Expr *E) {
+void Sema::CheckForIntOverflow(const Expr *E) {
   // Use a work list to deal with nested struct initializers.
   SmallVector<const Expr *, 2> Exprs(1, E);
 
@@ -14288,8 +14321,7 @@ void Sema::CheckForIntOverflow (const Expr *E) {
       Exprs.push_back(Array->getIdx());
     else if (const auto *Compound = dyn_cast<CompoundLiteralExpr>(E))
       Exprs.push_back(Compound->getInitializer());
-    else if (const auto *New = dyn_cast<CXXNewExpr>(E);
-             New && New->isArray()) {
+    else if (const auto *New = dyn_cast<CXXNewExpr>(E); New && New->isArray()) {
       if (auto ArraySize = New->getArraySize())
         Exprs.push_back(*ArraySize);
     } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(OriginalE))
@@ -14344,9 +14376,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
     }
 
     /// Merge a sequence of operations into its parent.
-    void merge(Seq S) {
-      Values[S.Index].Merged = true;
-    }
+    void merge(Seq S) { Values[S.Index].Merged = true; }
 
     /// Determine whether two operations are unsequenced. This operation
     /// is asymmetric: \p Cur should be the more recent sequence, and \p Old
@@ -14437,7 +14467,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
   /// UK_ModAsValue.
   struct SequencedSubexpression {
     SequencedSubexpression(SequenceChecker &Self)
-      : Self(Self), OldModAsSideEffect(Self.ModAsSideEffect) {
+        : Self(Self), OldModAsSideEffect(Self.ModAsSideEffect) {
       Self.ModAsSideEffect = &ModAsSideEffect;
     }
 
@@ -15189,9 +15219,8 @@ void Sema::CheckCompletedExpr(Expr *E, SourceLocation CheckLoc,
 }
 
 void Sema::CheckBitFieldInitialization(SourceLocation InitLoc,
-                                       FieldDecl *BitField,
-                                       Expr *Init) {
-  (void) AnalyzeBitFieldAssignment(*this, BitField, Init, InitLoc);
+                                       FieldDecl *BitField, Expr *Init) {
+  (void)AnalyzeBitFieldAssignment(*this, BitField, Init, InitLoc);
 }
 
 static void diagnoseArrayStarInParamType(Sema &S, QualType PType,
@@ -15566,39 +15595,44 @@ void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
 
   // Require that the destination be a pointer type.
   const PointerType *DestPtr = T->getAs<PointerType>();
-  if (!DestPtr) return;
+  if (!DestPtr)
+    return;
 
   // If the destination has alignment 1, we're done.
   QualType DestPointee = DestPtr->getPointeeType();
-  if (DestPointee->isIncompleteType()) return;
+  if (DestPointee->isIncompleteType())
+    return;
   CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee);
-  if (DestAlign.isOne()) return;
+  if (DestAlign.isOne())
+    return;
 
   // Require that the source be a pointer type.
   const PointerType *SrcPtr = Op->getType()->getAs<PointerType>();
-  if (!SrcPtr) return;
+  if (!SrcPtr)
+    return;
   QualType SrcPointee = SrcPtr->getPointeeType();
 
   // Explicitly allow casts from cv void*.  We already implicitly
   // allowed casts to cv void*, since they have alignment 1.
   // Also allow casts involving incomplete types, which implicitly
   // includes 'void'.
-  if (SrcPointee->isIncompleteType()) return;
+  if (SrcPointee->isIncompleteType())
+    return;
 
   CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this);
 
-  if (SrcAlign >= DestAlign) return;
+  if (SrcAlign >= DestAlign)
+    return;
 
   Diag(TRange.getBegin(), diag::warn_cast_align)
-    << Op->getType() << T
-    << static_cast<unsigned>(SrcAlign.getQuantity())
-    << static_cast<unsigned>(DestAlign.getQuantity())
-    << TRange << Op->getSourceRange();
+      << Op->getType() << T << static_cast<unsigned>(SrcAlign.getQuantity())
+      << static_cast<unsigned>(DestAlign.getQuantity()) << TRange
+      << Op->getSourceRange();
 }
 
 void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
-                            const ArraySubscriptExpr *ASE,
-                            bool AllowOnePastEnd, bool IndexNegated) {
+                            const ArraySubscriptExpr *ASE, bool AllowOnePastEnd,
+                            bool IndexNegated) {
   // Already diagnosed by the constant evaluator.
   if (isConstantEvaluatedContext())
     return;
@@ -15613,8 +15647,8 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
   const ConstantArrayType *ArrayTy =
       Context.getAsConstantArrayType(BaseExpr->getType());
 
-  LangOptions::StrictFlexArraysLevelKind
-    StrictFlexArraysLevel = getLangOpts().getStrictFlexArraysLevel();
+  LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+      getLangOpts().getStrictFlexArraysLevel();
 
   const Type *BaseType =
       ArrayTy == nullptr ? nullptr : ArrayTy->getElementType().getTypePtr();
@@ -15752,8 +15786,8 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
     // ']' location) and the index expression are both from macro expansions
     // within a system header.
     if (ASE) {
-      SourceLocation RBracketLoc = SourceMgr.getSpellingLoc(
-          ASE->getRBracketLoc());
+      SourceLocation RBracketLoc =
+          SourceMgr.getSpellingLoc(ASE->getRBracketLoc());
       if (SourceMgr.isInSystemHeader(RBracketLoc)) {
         SourceLocation IndexLoc =
             SourceMgr.getSpellingLoc(IndexExpr->getBeginLoc());
@@ -15775,7 +15809,8 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
     unsigned DiagID = diag::warn_array_index_precedes_bounds;
     if (!ASE) {
       DiagID = diag::warn_ptr_arith_precedes_bounds;
-      if (index.isNegative()) index = -index;
+      if (index.isNegative())
+        index = -index;
     }
 
     DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
@@ -15801,68 +15836,67 @@ void Sema::CheckArrayAccess(const Expr *expr) {
   while (expr) {
     expr = expr->IgnoreParenImpCasts();
     switch (expr->getStmtClass()) {
-      case Stmt::ArraySubscriptExprClass: {
-        const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
-        CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE,
-                         AllowOnePastEnd > 0);
-        expr = ASE->getBase();
-        break;
-      }
-      case Stmt::MemberExprClass: {
-        expr = cast<MemberExpr>(expr)->getBase();
-        break;
-      }
-      case Stmt::CXXMemberCallExprClass: {
-        expr = cast<CXXMemberCallExpr>(expr)->getImplicitObjectArgument();
+    case Stmt::ArraySubscriptExprClass: {
+      const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
+      CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE, AllowOnePastEnd > 0);
+      expr = ASE->getBase();
+      break;
+    }
+    case Stmt::MemberExprClass: {
+      expr = cast<MemberExpr>(expr)->getBase();
+      break;
+    }
+    case Stmt::CXXMemberCallExprClass: {
+      expr = cast<CXXMemberCallExpr>(expr)->getImplicitObjectArgument();
+      break;
+    }
+    case Stmt::ArraySectionExprClass: {
+      const ArraySectionExpr *ASE = cast<ArraySectionExpr>(expr);
+      // FIXME: We should probably be checking all of the elements to the
+      // 'length' here as well.
+      if (ASE->getLowerBound())
+        CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
+                         /*ASE=*/nullptr, AllowOnePastEnd > 0);
+      return;
+    }
+    case Stmt::UnaryOperatorClass: {
+      // Only unwrap the * and & unary operators
+      const UnaryOperator *UO = cast<UnaryOperator>(expr);
+      expr = UO->getSubExpr();
+      switch (UO->getOpcode()) {
+      case UO_AddrOf:
+        AllowOnePastEnd++;
         break;
-      }
-      case Stmt::ArraySectionExprClass: {
-        const ArraySectionExpr *ASE = cast<ArraySectionExpr>(expr);
-        // FIXME: We should probably be checking all of the elements to the
-        // 'length' here as well.
-        if (ASE->getLowerBound())
-          CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
-                           /*ASE=*/nullptr, AllowOnePastEnd > 0);
-        return;
-      }
-      case Stmt::UnaryOperatorClass: {
-        // Only unwrap the * and & unary operators
-        const UnaryOperator *UO = cast<UnaryOperator>(expr);
-        expr = UO->getSubExpr();
-        switch (UO->getOpcode()) {
-          case UO_AddrOf:
-            AllowOnePastEnd++;
-            break;
-          case UO_Deref:
-            AllowOnePastEnd--;
-            break;
-          default:
-            return;
-        }
+      case UO_Deref:
+        AllowOnePastEnd--;
         break;
-      }
-      case Stmt::ConditionalOperatorClass: {
-        const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
-        if (const Expr *lhs = cond->getLHS())
-          CheckArrayAccess(lhs);
-        if (const Expr *rhs = cond->getRHS())
-          CheckArrayAccess(rhs);
-        return;
-      }
-      case Stmt::CXXOperatorCallExprClass: {
-        const auto *OCE = cast<CXXOperatorCallExpr>(expr);
-        for (const auto *Arg : OCE->arguments())
-          CheckArrayAccess(Arg);
-        return;
-      }
       default:
         return;
+      }
+      break;
+    }
+    case Stmt::ConditionalOperatorClass: {
+      const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
+      if (const Expr *lhs = cond->getLHS())
+        CheckArrayAccess(lhs);
+      if (const Expr *rhs = cond->getRHS())
+        CheckArrayAccess(rhs);
+      return;
+    }
+    case Stmt::CXXOperatorCallExprClass: {
+      const auto *OCE = cast<CXXOperatorCallExpr>(expr);
+      for (const auto *Arg : OCE->arguments())
+        CheckArrayAccess(Arg);
+      return;
+    }
+    default:
+      return;
     }
   }
 }
 
-static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
-                                     Expr *RHS, bool isProperty) {
+static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc, Expr *RHS,
+                                     bool isProperty) {
   // Check if RHS is an Objective-C object literal, which also can get
   // immediately zapped in a weak reference.  Note that we explicitly
   // allow ObjCStringLiterals, since those are designed to never really die.
@@ -15875,23 +15909,20 @@ static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
     return false;
 
   S.Diag(Loc, diag::warn_arc_literal_assign)
-    << (unsigned) Kind
-    << (isProperty ? 0 : 1)
-    << RHS->getSourceRange();
+      << (unsigned)Kind << (isProperty ? 0 : 1) << RHS->getSourceRange();
 
   return true;
 }
 
 static bool checkUnsafeAssignObject(Sema &S, SourceLocation Loc,
-                                    Qualifiers::ObjCLifetime LT,
-                                    Expr *RHS, bool isProperty) {
+                                    Qualifiers::ObjCLifetime LT, Expr *RHS,
+                                    bool isProperty) {
   // Strip off any implicit cast added to get to the one ARC-specific.
   while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
     if (cast->getCastKind() == CK_ARCConsumeObject) {
       S.Diag(Loc, diag::warn_arc_retained_assign)
-        << (LT == Qualifiers::OCL_ExplicitNone)
-        << (isProperty ? 0 : 1)
-        << RHS->getSourceRange();
+          << (LT == Qualifiers::OCL_ExplicitNone) << (isProperty ? 0 : 1)
+          << RHS->getSourceRange();
       return true;
     }
     RHS = cast->getSubExpr();
@@ -15904,8 +15935,7 @@ static bool checkUnsafeAssignObject(Sema &S, SourceLocation Loc,
   return false;
 }
 
-bool Sema::checkUnsafeAssigns(SourceLocation Loc,
-                              QualType LHS, Expr *RHS) {
+bool Sema::checkUnsafeAssigns(SourceLocation Loc, QualType LHS, Expr *RHS) {
   Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime();
 
   if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone)
@@ -15917,13 +15947,11 @@ bool Sema::checkUnsafeAssigns(SourceLocation Loc,
   return false;
 }
 
-void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
-                              Expr *LHS, Expr *RHS) {
+void Sema::checkUnsafeExprAssigns(SourceLocation Loc, Expr *LHS, Expr *RHS) {
   QualType LHSType;
   // PropertyRef on LHS type need be directly obtained from
   // its declaration as it has a PseudoType.
-  ObjCPropertyRefExpr *PRE
-    = dyn_cast<ObjCPropertyRefExpr>(LHS->IgnoreParens());
+  ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(LHS->IgnoreParens());
   if (PRE && !PRE->isImplicitProperty()) {
     const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
     if (PD)
@@ -15967,7 +15995,7 @@ void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
       while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
         if (cast->getCastKind() == CK_ARCConsumeObject) {
           Diag(Loc, diag::warn_arc_retained_property_assign)
-          << RHS->getSourceRange();
+              << RHS->getSourceRange();
           return;
         }
         RHS = cast->getSubExpr();
@@ -15994,14 +16022,14 @@ static bool ShouldDiagnoseEmptyStmtBody(const SourceManager &SourceMgr,
 
   // Get line numbers of statement and body.
   bool StmtLineInvalid;
-  unsigned StmtLine = SourceMgr.getPresumedLineNumber(StmtLoc,
-                                                      &StmtLineInvalid);
+  unsigned StmtLine =
+      SourceMgr.getPresumedLineNumber(StmtLoc, &StmtLineInvalid);
   if (StmtLineInvalid)
     return false;
 
   bool BodyLineInvalid;
-  unsigned BodyLine = SourceMgr.getSpellingLineNumber(Body->getSemiLoc(),
-                                                      &BodyLineInvalid);
+  unsigned BodyLine =
+      SourceMgr.getSpellingLineNumber(Body->getSemiLoc(), &BodyLineInvalid);
   if (BodyLineInvalid)
     return false;
 
@@ -16012,8 +16040,7 @@ static bool ShouldDiagnoseEmptyStmtBody(const SourceManager &SourceMgr,
   return true;
 }
 
-void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
-                                 const Stmt *Body,
+void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc, const Stmt *Body,
                                  unsigned DiagID) {
   // Since this is a syntactic check, don't emit diagnostic for template
   // instantiations, this just adds noise.
@@ -16033,8 +16060,7 @@ void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
   Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
 }
 
-void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
-                                 const Stmt *PossibleBody) {
+void Sema::DiagnoseEmptyLoopBody(const Stmt *S, const Stmt *PossibleBody) {
   assert(!CurrentInstantiationScope); // Ensured by caller
 
   SourceLocation StmtLoc;
@@ -16102,7 +16128,7 @@ void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
 //===--- CHECK: Warn on self move with std::move. -------------------------===//
 
 void Sema::DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
-                             SourceLocation OpLoc) {
+                            SourceLocation OpLoc) {
   if (Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess, OpLoc))
     return;
 
@@ -16278,10 +16304,10 @@ static bool isLayoutCompatibleUnion(const ASTContext &C, const RecordDecl *RD1,
     auto I = UnmatchedFields.begin();
     auto E = UnmatchedFields.end();
 
-    for ( ; I != E; ++I) {
+    for (; I != E; ++I) {
       if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
         bool Result = UnmatchedFields.erase(*I);
-        (void) Result;
+        (void)Result;
         assert(Result);
         break;
       }
@@ -16379,7 +16405,7 @@ bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
 static bool FindTypeTagExpr(const Expr *TypeExpr, const ASTContext &Ctx,
                             const ValueDecl **VD, uint64_t *MagicValue,
                             bool isConstantEvaluated) {
-  while(true) {
+  while (true) {
     if (!TypeExpr)
       return false;
 
@@ -16491,8 +16517,7 @@ static bool GetMatchingCType(
   if (!MagicValues)
     return false;
 
-  llvm::DenseMap<Sema::TypeTagMagicValue,
-                 Sema::TypeTagData>::const_iterator I =
+  llvm::DenseMap<Sema::TypeTagMagicValue, Sema::TypeTagData>::const_iterator I =
       MagicValues->find(std::make_pair(ArgumentKind, MagicValue));
   if (I == MagicValues->end())
     return false;
@@ -16503,8 +16528,7 @@ static bool GetMatchingCType(
 
 void Sema::RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
                                       uint64_t MagicValue, QualType Type,
-                                      bool LayoutCompatible,
-                                      bool MustBeNull) {
+                                      bool LayoutCompatible, bool MustBeNull) {
   if (!TypeTagForDatatypeMagicValues)
     TypeTagForDatatypeMagicValues.reset(
         new llvm::DenseMap<TypeTagMagicValue, TypeTagData>);
@@ -16526,8 +16550,8 @@ static bool IsSameCharType(QualType T1, QualType T2) {
   BuiltinType::Kind T1Kind = BT1->getKind();
   BuiltinType::Kind T2Kind = BT2->getKind();
 
-  return (T1Kind == BuiltinType::SChar  && T2Kind == BuiltinType::Char_S) ||
-         (T1Kind == BuiltinType::UChar  && T2Kind == BuiltinType::Char_U) ||
+  return (T1Kind == BuiltinType::SChar && T2Kind == BuiltinType::Char_S) ||
+         (T1Kind == BuiltinType::UChar && T2Kind == BuiltinType::Char_U) ||
          (T1Kind == BuiltinType::Char_U && T2Kind == BuiltinType::UChar) ||
          (T1Kind == BuiltinType::Char_S && T2Kind == BuiltinType::SChar);
 }
@@ -16554,7 +16578,7 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
     if (FoundWrongKind)
       Diag(TypeTagExpr->getExprLoc(),
            diag::warn_type_tag_for_datatype_wrong_kind)
-        << TypeTagExpr->getSourceRange();
+          << TypeTagExpr->getSourceRange();
     return;
   }
 
@@ -16581,12 +16605,11 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
 
   if (TypeInfo.MustBeNull) {
     // Type tag with matching void type requires a null pointer.
-    if (!ArgumentExpr->isNullPointerConstant(Context,
-                                             Expr::NPC_ValueDependentIsNotNull)) {
+    if (!ArgumentExpr->isNullPointerConstant(
+            Context, Expr::NPC_ValueDependentIsNotNull)) {
       Diag(ArgumentExpr->getExprLoc(),
            diag::warn_type_safety_null_pointer_required)
-          << ArgumentKind->getName()
-          << ArgumentExpr->getSourceRange()
+          << ArgumentKind->getName() << ArgumentExpr->getSourceRange()
           << TypeTagExpr->getSourceRange();
     }
     return;
@@ -16610,19 +16633,16 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
                                            RequiredType->getPointeeType())) ||
           (!IsPointerAttr && IsSameCharType(ArgumentType, RequiredType)))
         mismatch = false;
-  } else
-    if (IsPointerAttr)
-      mismatch = !isLayoutCompatible(Context,
-                                     ArgumentType->getPointeeType(),
-                                     RequiredType->getPointeeType());
-    else
-      mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType);
+  } else if (IsPointerAttr)
+    mismatch = !isLayoutCompatible(Context, ArgumentType->getPointeeType(),
+                                   RequiredType->getPointeeType());
+  else
+    mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType);
 
   if (mismatch)
     Diag(ArgumentExpr->getExprLoc(), diag::warn_type_safety_type_mismatch)
-        << ArgumentType << ArgumentKind
-        << TypeInfo.LayoutCompatible << RequiredType
-        << ArgumentExpr->getSourceRange()
+        << ArgumentType << ArgumentKind << TypeInfo.LayoutCompatible
+        << RequiredType << ArgumentExpr->getSourceRange()
         << TypeTagExpr->getSourceRange();
 }
 
@@ -16718,7 +16738,7 @@ void Sema::RefersToMemberWithReducedAlignment(
   // For now, just disregard these cases. This is left for future
   // improvement.
   if (!DRE && !isa<CXXThisExpr>(TopBase))
-      return;
+    return;
 
   // Alignment expected by the whole expression.
   CharUnits ExpectedAlignment = Context.getTypeAlignInChars(E->getType());
@@ -17298,3 +17318,50 @@ void Sema::CheckTCBEnforcement(const SourceLocation CallExprLoc,
     }
   }
 }
+
+bool Sema::BuiltinStartLifetimeAs(CallExpr *Call) {
+  if (checkArgCount(Call, 1))
+    return true;
+
+  // Defer checking if the call is type or value dependent (like inside an
+  // uninstatiated template).
+  if (Call->isTypeDependent() || Call->isValueDependent())
+    return false;
+
+  Expr *PtrArg = Call->getArg(0);
+  QualType PtrType = PtrArg->getType();
+
+  // Ensure that the argument is a standard C/C++ pointer.
+  if (!PtrType->isPointerType()) {
+    Diag(PtrArg->getExprLoc(), diag::err_typecheck_convert_incompatible)
+        << PtrType << "pointer" << 1 << 0 << 0 << PtrArg->getSourceRange();
+    return true;
+  }
+
+  QualType PointeeType = PtrType->getPointeeType();
+
+  // void and function pointers can't gave lifetimes.
+  if (PointeeType->isVoidType() || PointeeType->isFunctionType()) {
+    Diag(PtrArg->getExprLoc(), diag::err_start_lifetime_as_not_implicit)
+        << PointeeType << PtrArg->getSourceRange();
+    return true;
+  }
+
+  // P2679R2: Recursively unwrap array types.
+  QualType BaseElemType = Context.getBaseElementType(PointeeType);
+
+  // We cannot evaluate lifetime properties of an incomplete type
+  if (RequireCompleteType(PtrArg->getExprLoc(), BaseElemType,
+                          diag::err_incomplete_type))
+    return true;
+
+  // Enforce C++23 implicit-lifetime constraints.
+  if (!BaseElemType->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..eb5c1a559d648
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s
+
+struct Trivial {
+    int x;
+    float y;
+};
+
+struct NonTrivial {
+    int x;
+    ~NonTrivial() {}
+};
+
+struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
+
+void test_valid_cases(void* ptr) {
+    // Should complete without any diagnostics.
+    __builtin_start_lifetime_as((int*)ptr);
+    __builtin_start_lifetime_as((Trivial*)ptr);
+
+    // P2679R2 check
+    __builtin_start_lifetime_as((int(*)[5])ptr);
+  __builtin_start_lifetime_as((Trivial(*)[5][10])ptr);
+}
+
+void test_invalid_types(void* ptr) {
+  // expected-error at +1 {{type 'NonTrivial' is not an implicit-lifetime type, cannot start lifetime}}
+  __builtin_start_lifetime_as((NonTrivial*)ptr);
+
+  // expected-error at +1 {{type 'NonTrivial[5]' is not an implicit-lifetime type, cannot start lifetime}}
+  __builtin_start_lifetime_as((NonTrivial(*)[5])ptr);
+
+  // expected-error at +1 {{type 'void' is not an implicit-lifetime type, cannot start lifetime}}
+  __builtin_start_lifetime_as((void*)ptr);
+
+  // expected-error at +1 {{type 'void ()' is not an implicit-lifetime type, cannot start lifetime}}
+  __builtin_start_lifetime_as((void(*)())ptr);
+}
+
+void test_incomplete_types(void* ptr) {
+  // expected-error at +1 {{incomplete type 'Incomplete' where a complete type is required}}
+  __builtin_start_lifetime_as((Incomplete*)ptr);
+}
+
+void test_invalid_arguments() {
+  int x;
+  // expected-error at +1 {{passing 'int' to parameter of incompatible type pointer}}
+  __builtin_start_lifetime_as(x);
+  
+  // expected-error at +1 {{too few arguments to function call, expected 1, have 0}}
+  __builtin_start_lifetime_as();
+}
+
+void test_constexpr() {
+  // expected-error at +2 {{constexpr variable 'ptr' must be initialized by a constant expression}}
+  constexpr auto* ptr = (int*)__builtin_start_lifetime_as((int*)nullptr);
+}
\ No newline at end of file

>From 58faeff61395eedd5d99ecebe2e8044967ab57f0 Mon Sep 17 00:00:00 2001
From: Yash Verma <yashverma056 at proton.me>
Date: Thu, 7 May 2026 05:25:56 -0400
Subject: [PATCH 3/4] [Clang][CodeGen] Implement IR emission for
 `__builtin_start_lifetime_as`

Implements the CodeGen path for C++23's explicit lifetime management (P2679R2).

Includes a CodeGen test verifying the expected pass-through behavior.
---
 clang/lib/CodeGen/CGBuiltin.cpp               | 525 +++++++++---------
 .../CodeGenCXX/builtin-start-lifetime-as.cpp  |  13 +
 2 files changed, 277 insertions(+), 261 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..8135b7c1c000c 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"
@@ -244,15 +245,15 @@ llvm::Constant *CodeGenModule::getBuiltinLibFunction(const FunctionDecl *FD,
   }
 
   llvm::FunctionType *Ty =
-    cast<llvm::FunctionType>(getTypes().ConvertType(FD->getType()));
+      cast<llvm::FunctionType>(getTypes().ConvertType(FD->getType()));
 
   return GetOrCreateLLVMFunction(Name, Ty, D, /*ForVTable=*/false);
 }
 
 /// Emit the conversions required to turn the given value into an
 /// integer of the given size.
-Value *EmitToInt(CodeGenFunction &CGF, llvm::Value *V,
-                        QualType T, llvm::IntegerType *IntType) {
+Value *EmitToInt(CodeGenFunction &CGF, llvm::Value *V, QualType T,
+                 llvm::IntegerType *IntType) {
   V = CGF.EmitToMemory(V, T);
 
   if (V->getType()->isPointerTy())
@@ -262,8 +263,8 @@ Value *EmitToInt(CodeGenFunction &CGF, llvm::Value *V,
   return V;
 }
 
-Value *EmitFromInt(CodeGenFunction &CGF, llvm::Value *V,
-                          QualType T, llvm::Type *ResultType) {
+Value *EmitFromInt(CodeGenFunction &CGF, llvm::Value *V, QualType T,
+                   llvm::Type *ResultType) {
   V = CGF.EmitFromMemory(V, T);
 
   if (ResultType->isPointerTy())
@@ -292,14 +293,14 @@ Address CheckAtomicAlignment(CodeGenFunction &CGF, const CallExpr *E) {
 
 /// Utility to insert an atomic instruction based on Intrinsic::ID
 /// and the expression node.
-Value *MakeBinaryAtomicValue(
-    CodeGenFunction &CGF, llvm::AtomicRMWInst::BinOp Kind, const CallExpr *E,
-    AtomicOrdering Ordering) {
+Value *MakeBinaryAtomicValue(CodeGenFunction &CGF,
+                             llvm::AtomicRMWInst::BinOp Kind, const CallExpr *E,
+                             AtomicOrdering Ordering) {
 
   QualType T = E->getType();
   assert(E->getArg(0)->getType()->isPointerType());
-  assert(CGF.getContext().hasSameUnqualifiedType(T,
-                                  E->getArg(0)->getType()->getPointeeType()));
+  assert(CGF.getContext().hasSameUnqualifiedType(
+      T, E->getArg(0)->getType()->getPointeeType()));
   assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType()));
 
   Address DestAddr = CheckAtomicAlignment(CGF, E);
@@ -346,13 +347,12 @@ static RValue EmitBinaryAtomic(CodeGenFunction &CGF,
 /// operation.
 static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF,
                                    llvm::AtomicRMWInst::BinOp Kind,
-                                   const CallExpr *E,
-                                   Instruction::BinaryOps Op,
+                                   const CallExpr *E, Instruction::BinaryOps Op,
                                    bool Invert = false) {
   QualType T = E->getType();
   assert(E->getArg(0)->getType()->isPointerType());
-  assert(CGF.getContext().hasSameUnqualifiedType(T,
-                                  E->getArg(0)->getType()->getPointeeType()));
+  assert(CGF.getContext().hasSameUnqualifiedType(
+      T, E->getArg(0)->getType()->getPointeeType()));
   assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType()));
 
   Address DestAddr = CheckAtomicAlignment(CGF, E);
@@ -429,8 +429,8 @@ Value *MakeAtomicCmpXchgValue(CodeGenFunction &CGF, const CallExpr *E,
 /// function MakeAtomicCmpXchgValue since it expects the arguments to be
 /// already swapped.
 
-static
-Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E,
+static Value *EmitAtomicCmpXchgForMSIntrin(
+    CodeGenFunction &CGF, const CallExpr *E,
     AtomicOrdering SuccessOrdering = AtomicOrdering::SequentiallyConsistent) {
   assert(E->getArg(0)->getType()->isPointerType());
   assert(CGF.getContext().hasSameUnqualifiedType(
@@ -453,9 +453,9 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E,
   }
 
   // For Release ordering, the failure ordering should be Monotonic.
-  auto FailureOrdering = SuccessOrdering == AtomicOrdering::Release ?
-                         AtomicOrdering::Monotonic :
-                         SuccessOrdering;
+  auto FailureOrdering = SuccessOrdering == AtomicOrdering::Release
+                             ? AtomicOrdering::Monotonic
+                             : SuccessOrdering;
 
   // The atomic instruction is marked volatile for consistency with MSVC. This
   // blocks the few atomics optimizations that LLVM has. If we want to optimize
@@ -538,7 +538,8 @@ static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF,
   return CGF.Builder.CreateZExt(Success, CGF.Int8Ty);
 }
 
-static Value *EmitAtomicIncrementValue(CodeGenFunction &CGF, const CallExpr *E,
+static Value *EmitAtomicIncrementValue(
+    CodeGenFunction &CGF, const CallExpr *E,
     AtomicOrdering Ordering = AtomicOrdering::SequentiallyConsistent) {
   assert(E->getArg(0)->getType()->isPointerType());
 
@@ -591,14 +592,15 @@ static Value *EmitISOVolatileStore(CodeGenFunction &CGF, const CallExpr *E) {
 // matching the argument type. Depending on mode, this may be a constrained
 // floating-point intrinsic.
 Value *emitUnaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
-                                const CallExpr *E, unsigned IntrinsicID,
-                                unsigned ConstrainedIntrinsicID) {
+                                          const CallExpr *E,
+                                          unsigned IntrinsicID,
+                                          unsigned ConstrainedIntrinsicID) {
   llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
 
   CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
   if (CGF.Builder.getIsFPConstrained()) {
     Function *F = CGF.CGM.getIntrinsic(ConstrainedIntrinsicID, Src0->getType());
-    return CGF.Builder.CreateConstrainedFPCall(F, { Src0 });
+    return CGF.Builder.CreateConstrainedFPCall(F, {Src0});
   } else {
     Function *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
     return CGF.Builder.CreateCall(F, Src0);
@@ -607,19 +609,20 @@ Value *emitUnaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
 
 // Emit an intrinsic that has 2 operands of the same type as its result.
 // Depending on mode, this may be a constrained floating-point intrinsic.
-static Value *emitBinaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
-                                const CallExpr *E, unsigned IntrinsicID,
-                                unsigned ConstrainedIntrinsicID) {
+static Value *
+emitBinaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF, const CallExpr *E,
+                                    unsigned IntrinsicID,
+                                    unsigned ConstrainedIntrinsicID) {
   llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
   llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
 
   CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
   if (CGF.Builder.getIsFPConstrained()) {
     Function *F = CGF.CGM.getIntrinsic(ConstrainedIntrinsicID, Src0->getType());
-    return CGF.Builder.CreateConstrainedFPCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateConstrainedFPCall(F, {Src0, Src1});
   } else {
     Function *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
-    return CGF.Builder.CreateCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateCall(F, {Src0, Src1});
   }
 }
 
@@ -645,9 +648,10 @@ emitBinaryExpMaybeConstrainedFPBuiltin(CodeGenFunction &CGF, const CallExpr *E,
 
 // Emit an intrinsic that has 3 operands of the same type as its result.
 // Depending on mode, this may be a constrained floating-point intrinsic.
-static Value *emitTernaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
-                                 const CallExpr *E, unsigned IntrinsicID,
-                                 unsigned ConstrainedIntrinsicID) {
+static Value *
+emitTernaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF, const CallExpr *E,
+                                     unsigned IntrinsicID,
+                                     unsigned ConstrainedIntrinsicID) {
   llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
   llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
   llvm::Value *Src2 = CGF.EmitScalarExpr(E->getArg(2));
@@ -655,10 +659,10 @@ static Value *emitTernaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
   CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
   if (CGF.Builder.getIsFPConstrained()) {
     Function *F = CGF.CGM.getIntrinsic(ConstrainedIntrinsicID, Src0->getType());
-    return CGF.Builder.CreateConstrainedFPCall(F, { Src0, Src1, Src2 });
+    return CGF.Builder.CreateConstrainedFPCall(F, {Src0, Src1, Src2});
   } else {
     Function *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
-    return CGF.Builder.CreateCall(F, { Src0, Src1, Src2 });
+    return CGF.Builder.CreateCall(F, {Src0, Src1, Src2});
   }
 }
 
@@ -858,11 +862,11 @@ llvm::Value *EmitOverflowIntrinsic(CodeGenFunction &CGF,
 }
 
 namespace {
-  struct WidthAndSignedness {
-    unsigned Width;
-    bool Signed;
-  };
-}
+struct WidthAndSignedness {
+  unsigned Width;
+  bool Signed;
+};
+} // namespace
 
 static WidthAndSignedness
 getIntegerWidthAndSignedness(const clang::ASTContext &context,
@@ -921,11 +925,9 @@ getDefaultBuiltinObjectSizeResult(unsigned Type, llvm::IntegerType *ResType) {
   return ConstantInt::get(ResType, (Type & 2) ? 0 : -1, /*isSigned=*/true);
 }
 
-llvm::Value *
-CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
-                                                 llvm::IntegerType *ResType,
-                                                 llvm::Value *EmittedE,
-                                                 bool IsDynamic) {
+llvm::Value *CodeGenFunction::evaluateOrEmitBuiltinObjectSize(
+    const Expr *E, unsigned Type, llvm::IntegerType *ResType,
+    llvm::Value *EmittedE, bool IsDynamic) {
   if (std::optional<uint64_t> ObjectSize =
           E->tryEvaluateObjectSize(getContext(), Type))
     return ConstantInt::get(ResType, *ObjectSize, /*isSigned=*/true);
@@ -1505,10 +1507,11 @@ llvm::Value *CodeGenFunction::emitCountedByMemberSize(
 /// EmittedE is the result of emitting `E` as a scalar expr. If it's non-null
 /// and we wouldn't otherwise try to reference a pass_object_size parameter,
 /// we'll call @llvm.objectsize on EmittedE, rather than emitting E.
-llvm::Value *
-CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
-                                       llvm::IntegerType *ResType,
-                                       llvm::Value *EmittedE, bool IsDynamic) {
+llvm::Value *CodeGenFunction::emitBuiltinObjectSize(const Expr *E,
+                                                    unsigned Type,
+                                                    llvm::IntegerType *ResType,
+                                                    llvm::Value *EmittedE,
+                                                    bool IsDynamic) {
   // We need to reference an argument if the pointer is a parameter with the
   // pass_object_size attribute.
   if (auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
@@ -1637,16 +1640,19 @@ BitTest BitTest::decodeBitTestBuiltin(unsigned BuiltinID) {
 
 static char bitActionToX86BTCode(BitTest::ActionKind A) {
   switch (A) {
-  case BitTest::TestOnly:   return '\0';
-  case BitTest::Complement: return 'c';
-  case BitTest::Reset:      return 'r';
-  case BitTest::Set:        return 's';
+  case BitTest::TestOnly:
+    return '\0';
+  case BitTest::Complement:
+    return 'c';
+  case BitTest::Reset:
+    return 'r';
+  case BitTest::Set:
+    return 's';
   }
   llvm_unreachable("invalid action");
 }
 
-static llvm::Value *EmitX86BitTestIntrinsic(CodeGenFunction &CGF,
-                                            BitTest BT,
+static llvm::Value *EmitX86BitTestIntrinsic(CodeGenFunction &CGF, BitTest BT,
                                             const CallExpr *E, Value *BitBase,
                                             Value *BitPos) {
   char Action = bitActionToX86BTCode(BT.Action);
@@ -1683,11 +1689,16 @@ static llvm::Value *EmitX86BitTestIntrinsic(CodeGenFunction &CGF,
 static llvm::AtomicOrdering
 getBitTestAtomicOrdering(BitTest::InterlockingKind I) {
   switch (I) {
-  case BitTest::Unlocked:   return llvm::AtomicOrdering::NotAtomic;
-  case BitTest::Sequential: return llvm::AtomicOrdering::SequentiallyConsistent;
-  case BitTest::Acquire:    return llvm::AtomicOrdering::Acquire;
-  case BitTest::Release:    return llvm::AtomicOrdering::Release;
-  case BitTest::NoFence:    return llvm::AtomicOrdering::Monotonic;
+  case BitTest::Unlocked:
+    return llvm::AtomicOrdering::NotAtomic;
+  case BitTest::Sequential:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  case BitTest::Acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case BitTest::Release:
+    return llvm::AtomicOrdering::Release;
+  case BitTest::NoFence:
+    return llvm::AtomicOrdering::Monotonic;
   }
   llvm_unreachable("invalid interlocking");
 }
@@ -1789,11 +1800,7 @@ static llvm::Value *EmitBitTestIntrinsic(CodeGenFunction &CGF,
 }
 
 namespace {
-enum class MSVCSetJmpKind {
-  _setjmpex,
-  _setjmp3,
-  _setjmp
-};
+enum class MSVCSetJmpKind { _setjmpex, _setjmp3, _setjmp };
 }
 
 /// MSVC handles setjmp a bit differently on different platforms. On every
@@ -2029,7 +2036,7 @@ struct CallObjCArcUse final : EHScopeStack::Cleanup {
     CGF.EmitARCIntrinsicUse(object);
   }
 };
-}
+} // namespace
 
 Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
                                                  BuiltinCheckKind Kind) {
@@ -2291,8 +2298,8 @@ RValue CodeGenFunction::emitBuiltinOSLogFormat(const CallExpr &E) {
 
     unsigned ArgValSize =
         CGM.getDataLayout().getTypeSizeInBits(ArgVal->getType());
-    llvm::IntegerType *IntTy = llvm::Type::getIntNTy(getLLVMContext(),
-                                                     ArgValSize);
+    llvm::IntegerType *IntTy =
+        llvm::Type::getIntNTy(getLLVMContext(), ArgValSize);
     ArgVal = Builder.CreateBitOrPointerCast(ArgVal, IntTy);
     CanQualType ArgTy = getOSLogArgType(Ctx, Size);
     // If ArgVal has type x86_fp80, zero-extend ArgVal.
@@ -2427,8 +2434,7 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
         IsNegative, CGF.Builder.CreateIsNotNull(UnsignedResult));
     Overflow = CGF.Builder.CreateOr(UnsignedOverflow, Underflow);
     if (ResultInfo.Width < OpWidth) {
-      auto IntMax =
-          llvm::APInt::getMaxValue(ResultInfo.Width).zext(OpWidth);
+      auto IntMax = llvm::APInt::getMaxValue(ResultInfo.Width).zext(OpWidth);
       llvm::Value *TruncOverflow = CGF.Builder.CreateICmpUGT(
           UnsignedResult, llvm::ConstantInt::get(OpTy, IntMax));
       Overflow = CGF.Builder.CreateOr(Overflow, TruncOverflow);
@@ -2548,69 +2554,69 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
 // Map math builtins for long-double to f128 version.
 static unsigned mutateLongDoubleBuiltin(unsigned BuiltinID) {
   switch (BuiltinID) {
-#define MUTATE_LDBL(func) \
-  case Builtin::BI__builtin_##func##l: \
+#define MUTATE_LDBL(func)                                                      \
+  case Builtin::BI__builtin_##func##l:                                         \
     return Builtin::BI__builtin_##func##f128;
-  MUTATE_LDBL(sqrt)
-  MUTATE_LDBL(cbrt)
-  MUTATE_LDBL(fabs)
-  MUTATE_LDBL(log)
-  MUTATE_LDBL(log2)
-  MUTATE_LDBL(log10)
-  MUTATE_LDBL(log1p)
-  MUTATE_LDBL(logb)
-  MUTATE_LDBL(exp)
-  MUTATE_LDBL(exp2)
-  MUTATE_LDBL(expm1)
-  MUTATE_LDBL(fdim)
-  MUTATE_LDBL(hypot)
-  MUTATE_LDBL(ilogb)
-  MUTATE_LDBL(pow)
-  MUTATE_LDBL(fmin)
-  MUTATE_LDBL(fmax)
-  MUTATE_LDBL(ceil)
-  MUTATE_LDBL(trunc)
-  MUTATE_LDBL(rint)
-  MUTATE_LDBL(nearbyint)
-  MUTATE_LDBL(round)
-  MUTATE_LDBL(floor)
-  MUTATE_LDBL(lround)
-  MUTATE_LDBL(llround)
-  MUTATE_LDBL(lrint)
-  MUTATE_LDBL(llrint)
-  MUTATE_LDBL(fmod)
-  MUTATE_LDBL(modf)
-  MUTATE_LDBL(nan)
-  MUTATE_LDBL(nans)
-  MUTATE_LDBL(inf)
-  MUTATE_LDBL(fma)
-  MUTATE_LDBL(sin)
-  MUTATE_LDBL(cos)
-  MUTATE_LDBL(tan)
-  MUTATE_LDBL(sinh)
-  MUTATE_LDBL(cosh)
-  MUTATE_LDBL(tanh)
-  MUTATE_LDBL(asin)
-  MUTATE_LDBL(acos)
-  MUTATE_LDBL(atan)
-  MUTATE_LDBL(asinh)
-  MUTATE_LDBL(acosh)
-  MUTATE_LDBL(atanh)
-  MUTATE_LDBL(atan2)
-  MUTATE_LDBL(erf)
-  MUTATE_LDBL(erfc)
-  MUTATE_LDBL(ldexp)
-  MUTATE_LDBL(frexp)
-  MUTATE_LDBL(huge_val)
-  MUTATE_LDBL(copysign)
-  MUTATE_LDBL(nextafter)
-  MUTATE_LDBL(nexttoward)
-  MUTATE_LDBL(remainder)
-  MUTATE_LDBL(remquo)
-  MUTATE_LDBL(scalbln)
-  MUTATE_LDBL(scalbn)
-  MUTATE_LDBL(tgamma)
-  MUTATE_LDBL(lgamma)
+    MUTATE_LDBL(sqrt)
+    MUTATE_LDBL(cbrt)
+    MUTATE_LDBL(fabs)
+    MUTATE_LDBL(log)
+    MUTATE_LDBL(log2)
+    MUTATE_LDBL(log10)
+    MUTATE_LDBL(log1p)
+    MUTATE_LDBL(logb)
+    MUTATE_LDBL(exp)
+    MUTATE_LDBL(exp2)
+    MUTATE_LDBL(expm1)
+    MUTATE_LDBL(fdim)
+    MUTATE_LDBL(hypot)
+    MUTATE_LDBL(ilogb)
+    MUTATE_LDBL(pow)
+    MUTATE_LDBL(fmin)
+    MUTATE_LDBL(fmax)
+    MUTATE_LDBL(ceil)
+    MUTATE_LDBL(trunc)
+    MUTATE_LDBL(rint)
+    MUTATE_LDBL(nearbyint)
+    MUTATE_LDBL(round)
+    MUTATE_LDBL(floor)
+    MUTATE_LDBL(lround)
+    MUTATE_LDBL(llround)
+    MUTATE_LDBL(lrint)
+    MUTATE_LDBL(llrint)
+    MUTATE_LDBL(fmod)
+    MUTATE_LDBL(modf)
+    MUTATE_LDBL(nan)
+    MUTATE_LDBL(nans)
+    MUTATE_LDBL(inf)
+    MUTATE_LDBL(fma)
+    MUTATE_LDBL(sin)
+    MUTATE_LDBL(cos)
+    MUTATE_LDBL(tan)
+    MUTATE_LDBL(sinh)
+    MUTATE_LDBL(cosh)
+    MUTATE_LDBL(tanh)
+    MUTATE_LDBL(asin)
+    MUTATE_LDBL(acos)
+    MUTATE_LDBL(atan)
+    MUTATE_LDBL(asinh)
+    MUTATE_LDBL(acosh)
+    MUTATE_LDBL(atanh)
+    MUTATE_LDBL(atan2)
+    MUTATE_LDBL(erf)
+    MUTATE_LDBL(erfc)
+    MUTATE_LDBL(ldexp)
+    MUTATE_LDBL(frexp)
+    MUTATE_LDBL(huge_val)
+    MUTATE_LDBL(copysign)
+    MUTATE_LDBL(nextafter)
+    MUTATE_LDBL(nexttoward)
+    MUTATE_LDBL(remainder)
+    MUTATE_LDBL(remquo)
+    MUTATE_LDBL(scalbln)
+    MUTATE_LDBL(scalbn)
+    MUTATE_LDBL(tgamma)
+    MUTATE_LDBL(lgamma)
 #undef MUTATE_LDBL
   default:
     return BuiltinID;
@@ -2711,11 +2717,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   if (E->isPRValue() && E->EvaluateAsRValue(Result, CGM.getContext()) &&
       !Result.hasSideEffects()) {
     if (Result.Val.isInt())
-      return RValue::get(llvm::ConstantInt::get(getLLVMContext(),
-                                                Result.Val.getInt()));
+      return RValue::get(
+          llvm::ConstantInt::get(getLLVMContext(), Result.Val.getInt()));
     if (Result.Val.isFloat())
-      return RValue::get(llvm::ConstantFP::get(getLLVMContext(),
-                                               Result.Val.getFloat()));
+      return RValue::get(
+          llvm::ConstantFP::get(getLLVMContext(), Result.Val.getFloat()));
   }
 
   // If current long-double semantics is IEEE 128-bit, replace math builtins
@@ -2813,9 +2819,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_ceill:
     case Builtin::BI__builtin_ceilf128:
     case Builtin::BI__builtin_elementwise_ceil:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::ceil,
-                                   Intrinsic::experimental_constrained_ceil));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::ceil, Intrinsic::experimental_constrained_ceil));
 
     case Builtin::BIcopysign:
     case Builtin::BIcopysignf:
@@ -2837,9 +2842,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_cosl:
     case Builtin::BI__builtin_cosf128:
     case Builtin::BI__builtin_elementwise_cos:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::cos,
-                                   Intrinsic::experimental_constrained_cos));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::cos, Intrinsic::experimental_constrained_cos));
 
     case Builtin::BIcosh:
     case Builtin::BIcoshf:
@@ -2862,9 +2866,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_expl:
     case Builtin::BI__builtin_expf128:
     case Builtin::BI__builtin_elementwise_exp:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::exp,
-                                   Intrinsic::experimental_constrained_exp));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::exp, Intrinsic::experimental_constrained_exp));
 
     case Builtin::BIexp2:
     case Builtin::BIexp2f:
@@ -2875,9 +2878,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_exp2l:
     case Builtin::BI__builtin_exp2f128:
     case Builtin::BI__builtin_elementwise_exp2:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::exp2,
-                                   Intrinsic::experimental_constrained_exp2));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::exp2, Intrinsic::experimental_constrained_exp2));
     case Builtin::BI__builtin_exp10:
     case Builtin::BI__builtin_exp10f:
     case Builtin::BI__builtin_exp10f16:
@@ -2910,9 +2912,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_floorl:
     case Builtin::BI__builtin_floorf128:
     case Builtin::BI__builtin_elementwise_floor:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::floor,
-                                   Intrinsic::experimental_constrained_floor));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::floor,
+          Intrinsic::experimental_constrained_floor));
 
     case Builtin::BIfma:
     case Builtin::BIfmaf:
@@ -2923,9 +2925,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_fmal:
     case Builtin::BI__builtin_fmaf128:
     case Builtin::BI__builtin_elementwise_fma:
-      return RValue::get(emitTernaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::fma,
-                                   Intrinsic::experimental_constrained_fma));
+      return RValue::get(emitTernaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::fma, Intrinsic::experimental_constrained_fma));
 
     case Builtin::BIfmax:
     case Builtin::BIfmaxf:
@@ -3011,9 +3012,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_logl:
     case Builtin::BI__builtin_logf128:
     case Builtin::BI__builtin_elementwise_log:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::log,
-                                   Intrinsic::experimental_constrained_log));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::log, Intrinsic::experimental_constrained_log));
 
     case Builtin::BIlog10:
     case Builtin::BIlog10f:
@@ -3024,9 +3024,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_log10l:
     case Builtin::BI__builtin_log10f128:
     case Builtin::BI__builtin_elementwise_log10:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::log10,
-                                   Intrinsic::experimental_constrained_log10));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::log10,
+          Intrinsic::experimental_constrained_log10));
 
     case Builtin::BIlog2:
     case Builtin::BIlog2f:
@@ -3037,9 +3037,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_log2l:
     case Builtin::BI__builtin_log2f128:
     case Builtin::BI__builtin_elementwise_log2:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::log2,
-                                   Intrinsic::experimental_constrained_log2));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::log2, Intrinsic::experimental_constrained_log2));
 
     case Builtin::BInearbyint:
     case Builtin::BInearbyintf:
@@ -3049,9 +3048,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_nearbyintl:
     case Builtin::BI__builtin_nearbyintf128:
     case Builtin::BI__builtin_elementwise_nearbyint:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                Intrinsic::nearbyint,
-                                Intrinsic::experimental_constrained_nearbyint));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::nearbyint,
+          Intrinsic::experimental_constrained_nearbyint));
 
     case Builtin::BIpow:
     case Builtin::BIpowf:
@@ -3062,9 +3061,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_powl:
     case Builtin::BI__builtin_powf128:
     case Builtin::BI__builtin_elementwise_pow:
-      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::pow,
-                                   Intrinsic::experimental_constrained_pow));
+      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::pow, Intrinsic::experimental_constrained_pow));
 
     case Builtin::BIrint:
     case Builtin::BIrintf:
@@ -3075,9 +3073,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_rintl:
     case Builtin::BI__builtin_rintf128:
     case Builtin::BI__builtin_elementwise_rint:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::rint,
-                                   Intrinsic::experimental_constrained_rint));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::rint, Intrinsic::experimental_constrained_rint));
 
     case Builtin::BIround:
     case Builtin::BIroundf:
@@ -3088,9 +3085,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_roundl:
     case Builtin::BI__builtin_roundf128:
     case Builtin::BI__builtin_elementwise_round:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::round,
-                                   Intrinsic::experimental_constrained_round));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::round,
+          Intrinsic::experimental_constrained_round));
 
     case Builtin::BIroundeven:
     case Builtin::BIroundevenf:
@@ -3101,9 +3098,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_roundevenl:
     case Builtin::BI__builtin_roundevenf128:
     case Builtin::BI__builtin_elementwise_roundeven:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::roundeven,
-                                   Intrinsic::experimental_constrained_roundeven));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::roundeven,
+          Intrinsic::experimental_constrained_roundeven));
 
     case Builtin::BIsin:
     case Builtin::BIsinf:
@@ -3114,9 +3111,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_sinl:
     case Builtin::BI__builtin_sinf128:
     case Builtin::BI__builtin_elementwise_sin:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::sin,
-                                   Intrinsic::experimental_constrained_sin));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::sin, Intrinsic::experimental_constrained_sin));
 
     case Builtin::BIsinh:
     case Builtin::BIsinhf:
@@ -3199,9 +3195,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_truncl:
     case Builtin::BI__builtin_truncf128:
     case Builtin::BI__builtin_elementwise_trunc:
-      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::trunc,
-                                   Intrinsic::experimental_constrained_trunc));
+      return RValue::get(emitUnaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::trunc,
+          Intrinsic::experimental_constrained_trunc));
 
     case Builtin::BIlround:
     case Builtin::BIlroundf:
@@ -3282,7 +3278,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   };
 
   switch (BuiltinIDIfNoAsmLabel) {
-  default: break;
+  default:
+    break;
   case Builtin::BI__builtin___CFStringMakeConstantString:
   case Builtin::BI__builtin___NSStringMakeConstantString:
     return RValue::get(ConstantEmitter(*this).emitAbstract(E, E->getType()));
@@ -3361,13 +3358,15 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     // is available as debuginfo is needed to preserve user-level
     // access pattern.
     if (!getDebugInfo()) {
-      CGM.Error(E->getExprLoc(), "using builtin_preserve_access_index() without -g");
+      CGM.Error(E->getExprLoc(),
+                "using builtin_preserve_access_index() without -g");
       return RValue::get(EmitScalarExpr(E->getArg(0)));
     }
 
     // Nested builtin_preserve_access_index() not supported
     if (IsInPreservedAIRegion) {
-      CGM.Error(E->getExprLoc(), "nested builtin_preserve_access_index() not supported");
+      CGM.Error(E->getExprLoc(),
+                "nested builtin_preserve_access_index() not supported");
       return RValue::get(EmitScalarExpr(E->getArg(0)));
     }
 
@@ -3404,8 +3403,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *Ctlz = Builder.CreateCall(F, {Tmp, Builder.getFalse()});
     Value *Result =
         Builder.CreateNUWSub(Ctlz, llvm::ConstantInt::get(ArgType, 1));
-    Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
-                                   "cast");
+    Result =
+        Builder.CreateIntCast(Result, ResultType, /*isSigned*/ true, "cast");
     return RValue::get(Result);
   }
   case Builtin::BI__builtin_ctzs:
@@ -3499,8 +3498,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero, "iszero");
     Value *Result = Builder.CreateSelect(IsZero, Zero, Tmp, "ffs");
     if (Result->getType() != ResultType)
-      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
-                                     "cast");
+      Result =
+          Builder.CreateIntCast(Result, ResultType, /*isSigned*/ true, "cast");
     return RValue::get(Result);
   }
   case Builtin::BI__builtin_parity:
@@ -3516,8 +3515,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *Tmp = Builder.CreateCall(F, ArgValue);
     Value *Result = Builder.CreateAnd(Tmp, llvm::ConstantInt::get(ArgType, 1));
     if (Result->getType() != ResultType)
-      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
-                                     "cast");
+      Result =
+          Builder.CreateIntCast(Result, ResultType, /*isSigned*/ true, "cast");
     return RValue::get(Result);
   }
   case Builtin::BI__lzcnt16:
@@ -3531,8 +3530,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     llvm::Type *ResultType = ConvertType(E->getType());
     Value *Result = Builder.CreateCall(F, {ArgValue, Builder.getFalse()});
     if (Result->getType() != ResultType)
-      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
-                                     "cast");
+      Result =
+          Builder.CreateIntCast(Result, ResultType, /*isSigned*/ true, "cast");
     return RValue::get(Result);
   }
   case Builtin::BI__popcnt16:
@@ -3607,7 +3606,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     const Expr *Ptr = E->getArg(0);
     Value *PtrValue = EmitScalarExpr(Ptr);
     Value *OffsetValue =
-      (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2)) : nullptr;
+        (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2)) : nullptr;
 
     Value *AlignmentValue = EmitScalarExpr(E->getArg(1));
     ConstantInt *AlignmentCI = cast<ConstantInt>(AlignmentValue);
@@ -3982,7 +3981,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
         CGM.getIntrinsic(Intrinsic::is_constant, ConvertType(ArgType));
     Value *Result = Builder.CreateCall(F, ArgValue);
     if (Result->getType() != ResultType)
-      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/false);
+      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/ false);
     return RValue::get(Result);
   }
   case Builtin::BI__builtin_dynamic_object_size:
@@ -4029,10 +4028,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   case Builtin::BI__builtin_prefetch: {
     Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
     // FIXME: Technically these constants should of type 'int', yes?
-    RW = (E->getNumArgs() > 1) ? EmitScalarExpr(E->getArg(1)) :
-      llvm::ConstantInt::get(Int32Ty, 0);
-    Locality = (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2)) :
-      llvm::ConstantInt::get(Int32Ty, 3);
+    RW = (E->getNumArgs() > 1) ? EmitScalarExpr(E->getArg(1))
+                               : llvm::ConstantInt::get(Int32Ty, 0);
+    Locality = (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2))
+                                     : llvm::ConstantInt::get(Int32Ty, 3);
     Value *Data = llvm::ConstantInt::get(Int32Ty, 1);
     Function *F = CGM.getIntrinsic(Intrinsic::prefetch, Address->getType());
     Builder.CreateCall(F, {Address, RW, Locality, Data});
@@ -4091,12 +4090,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
       CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
       Function *F = CGM.getIntrinsic(Intrinsic::experimental_constrained_powi,
                                      Src0->getType());
-      return RValue::get(Builder.CreateConstrainedFPCall(F, { Src0, Src1 }));
+      return RValue::get(Builder.CreateConstrainedFPCall(F, {Src0, Src1}));
     }
 
-    Function *F = CGM.getIntrinsic(Intrinsic::powi,
-                                   { Src0->getType(), Src1->getType() });
-    return RValue::get(Builder.CreateCall(F, { Src0, Src1 }));
+    Function *F =
+        CGM.getIntrinsic(Intrinsic::powi, {Src0->getType(), Src1->getType()});
+    return RValue::get(Builder.CreateCall(F, {Src0, Src1}));
   }
   case Builtin::BI__builtin_frexpl: {
     // Linux PPC will not be adding additional PPCDoubleDouble support.
@@ -4133,7 +4132,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *RHS = EmitScalarExpr(E->getArg(1));
 
     switch (BuiltinID) {
-    default: llvm_unreachable("Unknown ordered comparison");
+    default:
+      llvm_unreachable("Unknown ordered comparison");
     case Builtin::BI__builtin_isgreater:
       LHS = Builder.CreateFCmpOGT(LHS, RHS, "cmp");
       break;
@@ -4620,8 +4620,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     llvm::Type *ResultType = ConvertType(E->getType());
     Value *Result = Builder.CreateCall(F);
     if (Result->getType() != ResultType)
-      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
-                                     "cast");
+      Result =
+          Builder.CreateIntCast(Result, ResultType, /*isSigned*/ true, "cast");
     return RValue::get(Result);
   }
 
@@ -4643,14 +4643,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     BasicBlock *Begin = Builder.GetInsertBlock();
     BasicBlock *End = createBasicBlock("fpclassify_end", this->CurFn);
     Builder.SetInsertPoint(End);
-    PHINode *Result =
-      Builder.CreatePHI(ConvertType(E->getArg(0)->getType()), 4,
-                        "fpclassify_result");
+    PHINode *Result = Builder.CreatePHI(ConvertType(E->getArg(0)->getType()), 4,
+                                        "fpclassify_result");
 
     // if (V==0) return FP_ZERO
     Builder.SetInsertPoint(Begin);
-    Value *IsZero = Builder.CreateFCmpOEQ(V, Constant::getNullValue(Ty),
-                                          "iszero");
+    Value *IsZero =
+        Builder.CreateFCmpOEQ(V, Constant::getNullValue(Ty), "iszero");
     Value *ZeroLiteral = EmitScalarExpr(E->getArg(4));
     BasicBlock *NotZero = createBasicBlock("fpclassify_not_zero", this->CurFn);
     Builder.CreateCondBr(IsZero, End, NotZero);
@@ -4667,9 +4666,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     // if (fabs(V) == infinity) return FP_INFINITY
     Builder.SetInsertPoint(NotNan);
     Value *VAbs = EmitFAbs(*this, V);
-    Value *IsInf =
-      Builder.CreateFCmpOEQ(VAbs, ConstantFP::getInfinity(V->getType()),
-                            "isinf");
+    Value *IsInf = Builder.CreateFCmpOEQ(
+        VAbs, ConstantFP::getInfinity(V->getType()), "isinf");
     Value *InfLiteral = EmitScalarExpr(E->getArg(1));
     BasicBlock *NotInf = createBasicBlock("fpclassify_not_inf", this->CurFn);
     Builder.CreateCondBr(IsInf, End, NotInf);
@@ -4679,12 +4677,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Builder.SetInsertPoint(NotInf);
     APFloat Smallest = APFloat::getSmallestNormalized(
         getContext().getFloatTypeSemantics(E->getArg(5)->getType()));
-    Value *IsNormal =
-      Builder.CreateFCmpUGE(VAbs, ConstantFP::get(V->getContext(), Smallest),
-                            "isnormal");
-    Value *NormalResult =
-      Builder.CreateSelect(IsNormal, EmitScalarExpr(E->getArg(2)),
-                           EmitScalarExpr(E->getArg(3)));
+    Value *IsNormal = Builder.CreateFCmpUGE(
+        VAbs, ConstantFP::get(V->getContext(), Smallest), "isnormal");
+    Value *NormalResult = Builder.CreateSelect(
+        IsNormal, EmitScalarExpr(E->getArg(2)), EmitScalarExpr(E->getArg(3)));
     Builder.CreateBr(End);
     Result->addIncoming(NormalResult, NotInf);
 
@@ -4837,8 +4833,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Address DestAddr = EmitPointerWithAlignment(E->getArg(0));
     Address SrcAddr = EmitPointerWithAlignment(E->getArg(1));
     Value *SizeVal = EmitScalarExpr(E->getArg(2));
-    CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this,
-                                                  DestAddr, SrcAddr, SizeVal);
+    CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this, DestAddr, SrcAddr,
+                                                  SizeVal);
     return RValue::get(DestAddr, *this);
   }
 
@@ -4883,8 +4879,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   case Builtin::BImemset:
   case Builtin::BI__builtin_memset: {
     Address Dest = EmitPointerWithAlignment(E->getArg(0));
-    Value *ByteVal = Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)),
-                                         Builder.getInt8Ty());
+    Value *ByteVal =
+        Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)), Builder.getInt8Ty());
     Value *SizeVal = EmitScalarExpr(E->getArg(2));
     EmitNonNullArgCheck(Dest, E->getArg(0)->getType(),
                         E->getArg(0)->getExprLoc(), FD, 0);
@@ -4916,8 +4912,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     if (Size.ugt(DstSize))
       break;
     Address Dest = EmitPointerWithAlignment(E->getArg(0));
-    Value *ByteVal = Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)),
-                                         Builder.getInt8Ty());
+    Value *ByteVal =
+        Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)), Builder.getInt8Ty());
     Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size);
     auto *I = Builder.CreateMemSet(Dest, ByteVal, SizeVal, false);
     addInstToNewSourceAtom(I, nullptr);
@@ -5038,12 +5034,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     int32_t Offset = 0;
 
     Function *F = CGM.getIntrinsic(Intrinsic::eh_dwarf_cfa);
-    return RValue::get(Builder.CreateCall(F,
-                                      llvm::ConstantInt::get(Int32Ty, Offset)));
+    return RValue::get(
+        Builder.CreateCall(F, llvm::ConstantInt::get(Int32Ty, Offset)));
   }
   case Builtin::BI__builtin_return_address: {
-    Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
-                                                   getContext().UnsignedIntTy);
+    Value *Depth = ConstantEmitter(*this).emitAbstract(
+        E->getArg(0), getContext().UnsignedIntTy);
     Function *F =
         CGM.getIntrinsic(Intrinsic::returnaddress, {CGM.ProgramPtrTy});
     return RValue::get(Builder.CreateCall(F, Depth));
@@ -5054,8 +5050,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     return RValue::get(Builder.CreateCall(F, Builder.getInt32(0)));
   }
   case Builtin::BI__builtin_frame_address: {
-    Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
-                                                   getContext().UnsignedIntTy);
+    Value *Depth = ConstantEmitter(*this).emitAbstract(
+        E->getArg(0), getContext().UnsignedIntTy);
     Function *F = CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy);
     return RValue::get(Builder.CreateCall(F, Depth));
   }
@@ -5074,8 +5070,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     return RValue::get(Result);
   }
   case Builtin::BI__builtin_dwarf_sp_column: {
-    llvm::IntegerType *Ty
-      = cast<llvm::IntegerType>(ConvertType(E->getType()));
+    llvm::IntegerType *Ty = cast<llvm::IntegerType>(ConvertType(E->getType()));
     int Column = getTargetHooks().getDwarfEHStackPointer(CGM);
     if (Column == -1) {
       CGM.ErrorUnsupported(E, "__builtin_dwarf_sp_column");
@@ -5189,6 +5184,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
     return RValue::get(Ptr);
   }
+  // Sema already validates the target as an implicit-lifetime type hence it doesn't require vptr laundering.
+  case Builtin::BI__builtin_start_lifetime_as: {
+    const Expr *PtrArg = E->getArg(0);
+    llvm::Value *Ptr = EmitScalarExpr(PtrArg);
+
+    return RValue::get(Ptr);
+  }
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_sub:
   case Builtin::BI__sync_fetch_and_or:
@@ -5380,8 +5382,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
         CGM.getTypes().arrangeBuiltinFunctionCall(E->getType(), Args);
     llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo);
     llvm::FunctionCallee Func = CGM.CreateRuntimeFunction(FTy, LibCallName);
-    return EmitCall(FuncInfo, CGCallee::forDirect(Func),
-                    ReturnValueSlot(), Args);
+    return EmitCall(FuncInfo, CGCallee::forDirect(Func), ReturnValueSlot(),
+                    Args);
   }
 
   case Builtin::BI__atomic_thread_fence:
@@ -5401,17 +5403,17 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
       case 0:  // memory_order_relaxed
       default: // invalid order
         break;
-      case 1:  // memory_order_consume
-      case 2:  // memory_order_acquire
+      case 1: // memory_order_consume
+      case 2: // memory_order_acquire
         Builder.CreateFence(llvm::AtomicOrdering::Acquire, SSID);
         break;
-      case 3:  // memory_order_release
+      case 3: // memory_order_release
         Builder.CreateFence(llvm::AtomicOrdering::Release, SSID);
         break;
-      case 4:  // memory_order_acq_rel
+      case 4: // memory_order_acq_rel
         Builder.CreateFence(llvm::AtomicOrdering::AcquireRelease, SSID);
         break;
-      case 5:  // memory_order_seq_cst
+      case 5: // memory_order_seq_cst
         Builder.CreateFence(llvm::AtomicOrdering::SequentiallyConsistent, SSID);
         break;
       }
@@ -5668,7 +5670,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     // Decide if we are lowering to a uadd.with.overflow or usub.with.overflow.
     Intrinsic::ID IntrinsicId;
     switch (BuiltinID) {
-    default: llvm_unreachable("Unknown multiprecision builtin id.");
+    default:
+      llvm_unreachable("Unknown multiprecision builtin id.");
     case Builtin::BI__builtin_addcb:
     case Builtin::BI__builtin_addcs:
     case Builtin::BI__builtin_addc:
@@ -5687,13 +5690,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
     // Construct our resulting LLVM IR expression.
     llvm::Value *Carry1;
-    llvm::Value *Sum1 = EmitOverflowIntrinsic(*this, IntrinsicId,
-                                              X, Y, Carry1);
+    llvm::Value *Sum1 = EmitOverflowIntrinsic(*this, IntrinsicId, X, Y, Carry1);
     llvm::Value *Carry2;
-    llvm::Value *Sum2 = EmitOverflowIntrinsic(*this, IntrinsicId,
-                                              Sum1, Carryin, Carry2);
-    llvm::Value *CarryOut = Builder.CreateZExt(Builder.CreateOr(Carry1, Carry2),
-                                               X->getType());
+    llvm::Value *Sum2 =
+        EmitOverflowIntrinsic(*this, IntrinsicId, Sum1, Carryin, Carry2);
+    llvm::Value *CarryOut =
+        Builder.CreateZExt(Builder.CreateOr(Carry1, Carry2), X->getType());
     Builder.CreateStore(CarryOut, CarryOutPtr);
     return RValue::get(Sum2);
   }
@@ -5784,7 +5786,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
     // Finally, store the result using the pointer.
     bool isVolatile =
-      ResultArg->getType()->getPointeeType().isVolatileQualified();
+        ResultArg->getType()->getPointeeType().isVolatileQualified();
     Builder.CreateStore(EmitToMemory(Result, ResultQTy), ResultPtr, isVolatile);
 
     return RValue::get(Overflow);
@@ -5819,7 +5821,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     // Decide which of the overflow intrinsics we are lowering to:
     Intrinsic::ID IntrinsicId;
     switch (BuiltinID) {
-    default: llvm_unreachable("Unknown overflow builtin id.");
+    default:
+      llvm_unreachable("Unknown overflow builtin id.");
     case Builtin::BI__builtin_uadd_overflow:
     case Builtin::BI__builtin_uaddl_overflow:
     case Builtin::BI__builtin_uaddll_overflow:
@@ -5852,7 +5855,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
       break;
     }
 
-
     llvm::Value *Carry;
     llvm::Value *Sum = EmitOverflowIntrinsic(*this, IntrinsicId, X, Y, Carry);
     Builder.CreateStore(Sum, SumOutPtr);
@@ -5887,9 +5889,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   case Builtin::BI__builtin_call_with_static_chain: {
     const CallExpr *Call = cast<CallExpr>(E->getArg(0));
     const Expr *Chain = E->getArg(1);
-    return EmitCall(Call->getCallee()->getType(),
-                    EmitCallee(Call->getCallee()), Call, ReturnValue,
-                    EmitScalarExpr(Chain));
+    return EmitCall(Call->getCallee()->getType(), EmitCallee(Call->getCallee()),
+                    Call, ReturnValue, EmitScalarExpr(Chain));
   }
   case Builtin::BI_InterlockedExchange8:
   case Builtin::BI_InterlockedExchange16:
@@ -6169,8 +6170,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
       const char *Name = (BuiltinID == Builtin::BIread_pipe) ? "__read_pipe_4"
                                                              : "__write_pipe_4";
 
-      llvm::Type *ArgTys[] = {Arg0->getType(), Arg1->getType(), Int32Ty, I8PTy,
-                              Int32Ty, Int32Ty};
+      llvm::Type *ArgTys[] = {Arg0->getType(), Arg1->getType(), Int32Ty,
+                              I8PTy,           Int32Ty,         Int32Ty};
       Value *Arg2 = EmitScalarExpr(E->getArg(2)),
             *Arg3 = EmitScalarExpr(E->getArg(3));
       llvm::FunctionType *FTy = llvm::FunctionType::get(Int32Ty, ArgTys, false);
@@ -6307,8 +6308,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     auto NewName = std::string("__") + E->getDirectCallee()->getName().str();
     auto NewCall =
         EmitRuntimeCall(CGM.CreateRuntimeFunction(FTy, NewName), {NewArg});
-    return RValue::get(Builder.CreateBitOrPointerCast(NewCall,
-      ConvertType(E->getType())));
+    return RValue::get(
+        Builder.CreateBitOrPointerCast(NewCall, ConvertType(E->getType())));
   }
 
   // OpenCL v2.0, s6.13.17 - Enqueue kernel function.
@@ -6731,7 +6732,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   // can move this up to the beginning of the function.
   checkTargetFeatures(E, FD);
 
-  if (unsigned VectorWidth = getContext().BuiltinInfo.getRequiredVectorWidth(BuiltinID))
+  if (unsigned VectorWidth =
+          getContext().BuiltinInfo.getRequiredVectorWidth(BuiltinID))
     LargestVectorWidth = std::max(LargestVectorWidth, VectorWidth);
 
   // See if we have a target specific intrinsic.
@@ -6752,7 +6754,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   }
 
   if (IntrinsicID != Intrinsic::not_intrinsic) {
-    SmallVector<Value*, 16> Args;
+    SmallVector<Value *, 16> Args;
 
     // Find out if any arguments are required to be integer constant
     // expressions.
@@ -6802,7 +6804,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     if (RetTy != V->getType()) {
       // XXX - vector of pointers?
       if (auto *PtrTy = dyn_cast<llvm::PointerType>(RetTy)) {
-        if (PtrTy->getAddressSpace() != V->getType()->getPointerAddressSpace()) {
+        if (PtrTy->getAddressSpace() !=
+            V->getType()->getPointerAddressSpace()) {
           V = Builder.CreateAddrSpaceCast(
               V, llvm::PointerType::get(getLLVMContext(),
                                         PtrTy->getAddressSpace()));
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..c3e38e31e89e4
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++23 -O2 -emit-llvm %s -o - | FileCheck %s
+
+struct Struct {
+    int x;
+    float y;
+};
+
+// {{.*}} to match for `noundef readnone returned captures(ret: address, provenance)`
+// CHECK-LABEL: define {{.*}} ptr @_Z15test_start_lifePv(ptr {{.*}} %buffer)
+Struct* test_start_life(void* buffer) {
+    // CHECK: ret ptr %buffer
+    return (Struct*)__builtin_start_lifetime_as((Struct*)buffer);
+}

>From fa1c699b3fc6ce9ea54857d9969cd07e452589d6 Mon Sep 17 00:00:00 2001
From: Yash Verma <yashverma056 at proton.me>
Date: Thu, 7 May 2026 06:42:30 -0400
Subject: [PATCH 4/4] [libc++] Implement C++23 std::start_lifetime_as and
 std::start_lifetime_as_array

Implements P2590R2 and P2679R2 by exposing the `__builtin_start_lifetime_as` instrinsic through the `<memory>` header.

Key changes:
  - Adds `<__memory/start_lifetime_as.h>` providing the scalar and array variants, and exporting them through the public `<memory>` header.
  - Adds test suites to validate their behavior.
---
 clang/lib/CodeGen/CGBuiltin.cpp               |  3 +-
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__memory/start_lifetime_as.h   | 72 ++++++++++++++++++
 libcxx/include/memory                         | 26 +++++++
 libcxx/include/module.modulemap.in            |  1 +
 .../start.lifetime/start_lifetime_as.pass.cpp | 76 +++++++++++++++++++
 .../start_lifetime_as.verify.cpp              | 35 +++++++++
 .../start_lifetime_as_array.pass.cpp          | 47 ++++++++++++
 8 files changed, 260 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__memory/start_lifetime_as.h
 create mode 100644 libcxx/test/std/memory/start.lifetime/start_lifetime_as.pass.cpp
 create mode 100644 libcxx/test/std/memory/start.lifetime/start_lifetime_as.verify.cpp
 create mode 100644 libcxx/test/std/memory/start.lifetime/start_lifetime_as_array.pass.cpp

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 8135b7c1c000c..ac90b4cfbf529 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5184,7 +5184,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
     return RValue::get(Ptr);
   }
-  // Sema already validates the target as an implicit-lifetime type hence it doesn't require vptr laundering.
+  // Sema already validates the target as an implicit-lifetime type hence it
+  // doesn't require vptr laundering.
   case Builtin::BI__builtin_start_lifetime_as: {
     const Expr *PtrArg = E->getArg(0);
     llvm::Value *Ptr = EmitScalarExpr(PtrArg);
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index e27fb7602430f..eb91879b5c965 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -610,6 +610,7 @@ set(files
   __memory/raw_storage_iterator.h
   __memory/shared_count.h
   __memory/shared_ptr.h
+  __memory/start_lifetime_as.h
   __memory/swap_allocator.h
   __memory/temp_value.h
   __memory/temporary_buffer.h
diff --git a/libcxx/include/__memory/start_lifetime_as.h b/libcxx/include/__memory/start_lifetime_as.h
new file mode 100644
index 0000000000000..9ca84dc386dd8
--- /dev/null
+++ b/libcxx/include/__memory/start_lifetime_as.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_START_LIFETIME_AS_H
+#define _LIBCPP___MEMORY_START_LIFETIME_AS_H
+
+#include "__configuration/attributes.h"
+#include <__config>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp* start_lifetime_as(void* __p) _NOEXCEPT {
+  return __builtin_start_lifetime_as(static_cast<_Tp*>(__p));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI const _Tp* start_lifetime_as(const void* __p) _NOEXCEPT {
+  return __builtin_start_lifetime_as(static_cast<const _Tp*>(__p));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI volatile _Tp* start_lifetime_as(volatile void* __p) _NOEXCEPT {
+  return __builtin_start_lifetime_as(static_cast<volatile _Tp*>(__p));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI const volatile _Tp* start_lifetime_as(const volatile void* __p) _NOEXCEPT {
+  return __builtin_start_lifetime_as(static_cast<const volatile _Tp*>(__p));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp* start_lifetime_as_array(void* __p, size_t __n) _NOEXCEPT {
+  static_cast<void>(__n);
+  return __builtin_start_lifetime_as(static_cast<_Tp*>(__p));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI const _Tp* start_lifetime_as_array(const void* __p, size_t __n) _NOEXCEPT {
+  static_cast<void>(__n);
+  return __builtin_start_lifetime_as(static_cast<const _Tp*>(__p));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI volatile _Tp* start_lifetime_as_array(volatile void* __p, size_t __n) _NOEXCEPT {
+  static_cast<void>(__n);
+  return __builtin_start_lifetime_as(static_cast<volatile _Tp*>(__p));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI const volatile _Tp* start_lifetime_as_array(const volatile void* __p, size_t __n) _NOEXCEPT {
+  static_cast<void>(__n);
+  return __builtin_start_lifetime_as(static_cast<const volatile _Tp*>(__p));
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_START_LIFETIME_AS_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index ca880c83a544d..4ba8872e093c5 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -931,6 +931,31 @@ template<class Smart, class Pointer, class... Args>
 template<class Pointer = void, class Smart, class... Args>
   auto inout_ptr(Smart& s, Args&&... args);                 // since c++23
 
+// start_lifetime_as
+template <typename _Tp>
+_Tp* start_lifetime_as(void* __p) noexcept; // since c++23
+
+template <typename _Tp>
+const _Tp* start_lifetime_as(const void* __p) noexcept;  // since c++23
+
+template <typename _Tp>
+volatile _Tp* start_lifetime_as(volatile void* __p) noexcept;  // since c++23
+
+template <class _Tp>
+const volatile _Tp* start_lifetime_as(const volatile void* __p) noexcept;  // since c++23
+
+template <typename _Tp>
+_Tp* start_lifetime_as_array(void* __p, size_t __n) noexcept; // since c++23
+
+template <typename _Tp>
+const _Tp* start_lifetime_as_array(const void* __p, size_t __n) noexcept; // since c++23
+
+template <typename _Tp>
+volatile _Tp* start_lifetime_as_array(volatile void* __p, size_t __n) noexcept; // since c++23
+
+template <class _Tp>
+const volatile _Tp* start_lifetime_as_array(const volatile void* __p, size_t __n) noexcept; // since c++23
+
 }  // std
 
 */
@@ -976,6 +1001,7 @@ template<class Pointer = void, class Smart, class... Args>
 
 #  if _LIBCPP_STD_VER >= 23
 #    include <__memory/allocate_at_least.h>
+#    include <__memory/start_lifetime_as.h>
 #  endif
 
 #  include <version>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 44d8e21b2fba3..0882e18a44c36 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1691,6 +1691,7 @@ module std [system] {
       header "__memory/shared_ptr.h"
       export std.functional.hash
     }
+    module start_lifetime_as                  { header "__memory/start_lifetime_as.h" }
     module swap_allocator                     { header "__memory/swap_allocator.h" }
     module temp_value                         { header "__memory/temp_value.h" }
     module temporary_buffer                   {
diff --git a/libcxx/test/std/memory/start.lifetime/start_lifetime_as.pass.cpp b/libcxx/test/std/memory/start.lifetime/start_lifetime_as.pass.cpp
new file mode 100644
index 0000000000000..c87f0291a8b36
--- /dev/null
+++ b/libcxx/test/std/memory/start.lifetime/start_lifetime_as.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+// template<class T> T* start_lifetime_as(void* p) noexcept;
+// template<class T> const T* start_lifetime_as(const void* p) noexcept;
+// template<class T> volatile T* start_lifetime_as(volatile void* p) noexcept;
+// template<class T> const volatile T* start_lifetime_as(const volatile void* p) noexcept;
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+
+struct Trivial {
+  int x;
+  float y;
+};
+
+struct NestedArray {
+  int arr[3][4];
+};
+
+static_assert(noexcept(std::start_lifetime_as<int>(std::declval<void*>())));
+static_assert(noexcept(std::start_lifetime_as<int>(std::declval<const void*>())));
+static_assert(noexcept(std::start_lifetime_as<int>(std::declval<volatile void*>())));
+static_assert(noexcept(std::start_lifetime_as<int>(std::declval<const volatile void*>())));
+
+static_assert(std::is_same_v<decltype(std::start_lifetime_as<int>(std::declval<void*>())), int*>);
+static_assert(std::is_same_v<decltype(std::start_lifetime_as<int>(std::declval<const void*>())), const int*>);
+static_assert(std::is_same_v<decltype(std::start_lifetime_as<int>(std::declval<volatile void*>())), volatile int*>);
+static_assert(
+    std::is_same_v<decltype(std::start_lifetime_as<int>(std::declval<const volatile void*>())), const volatile int*>);
+
+void test_cv_qualifiers() {
+  alignas(Trivial) unsigned char buffer[sizeof(Trivial)];
+  void* p                  = buffer;
+  const void* cp           = buffer;
+  volatile void* vp        = buffer;
+  const volatile void* cvp = buffer;
+
+  Trivial* ptr                  = std::start_lifetime_as<Trivial>(p);
+  const Trivial* cptr           = std::start_lifetime_as<Trivial>(cp);
+  volatile Trivial* vptr        = std::start_lifetime_as<Trivial>(vp);
+  const volatile Trivial* cvptr = std::start_lifetime_as<Trivial>(cvp);
+
+  assert(static_cast<void*>(ptr) == buffer);
+  assert(static_cast<const void*>(cptr) == buffer);
+  assert(static_cast<volatile void*>(vptr) == buffer);
+  assert(static_cast<const volatile void*>(cvptr) == buffer);
+}
+
+void test_multi_dimensional_arrays() {
+  alignas(NestedArray) unsigned char buffer[sizeof(NestedArray)];
+
+  // P2679R2: The builtin should unwrap NestedArray completely
+  NestedArray* ptr = std::start_lifetime_as<NestedArray>(buffer);
+  assert(static_cast<void*>(ptr) == buffer);
+
+  using Matrix = double[4][4];
+  alignas(double) unsigned char mat_buffer[sizeof(Matrix)];
+  Matrix* mat_ptr = std::start_lifetime_as<Matrix>(mat_buffer);
+  assert(static_cast<void*>(mat_ptr) == mat_buffer);
+}
+
+int main(int, char**) {
+  test_cv_qualifiers();
+  test_multi_dimensional_arrays();
+  return 0;
+}
diff --git a/libcxx/test/std/memory/start.lifetime/start_lifetime_as.verify.cpp b/libcxx/test/std/memory/start.lifetime/start_lifetime_as.verify.cpp
new file mode 100644
index 0000000000000..b29cda44aff07
--- /dev/null
+++ b/libcxx/test/std/memory/start.lifetime/start_lifetime_as.verify.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <memory>
+
+struct NonTrivial {
+  int x;
+  ~NonTrivial() {}
+};
+
+struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
+
+void test_invalid_types(void* p) {
+  // expected-error@*:* {{type 'NonTrivial' is not an implicit-lifetime type, cannot start lifetime}}
+  std::start_lifetime_as<NonTrivial>(p);
+
+  // expected-error@*:* {{incomplete type 'Incomplete' where a complete type is required}}
+  std::start_lifetime_as<Incomplete>(p);
+
+  // expected-error@*:* {{type 'void' is not an implicit-lifetime type, cannot start lifetime}}
+  std::start_lifetime_as<void>(p);
+
+  // expected-error@*:* {{static_cast from 'void *' to 'void (*)()' is not allowed}}
+  std::start_lifetime_as<void()>(p);
+}
+
+void test_constexpr() {
+  // expected-error at +1 {{constexpr variable 'fail' must be initialized by a constant expression}}
+  constexpr auto* fail = std::start_lifetime_as<int>((void*)nullptr);
+}
\ No newline at end of file
diff --git a/libcxx/test/std/memory/start.lifetime/start_lifetime_as_array.pass.cpp b/libcxx/test/std/memory/start.lifetime/start_lifetime_as_array.pass.cpp
new file mode 100644
index 0000000000000..8967e28e18e89
--- /dev/null
+++ b/libcxx/test/std/memory/start.lifetime/start_lifetime_as_array.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+
+struct Trivial {
+  int id;
+};
+
+static_assert(noexcept(std::start_lifetime_as_array<int>(std::declval<void*>(), 5)));
+static_assert(noexcept(std::start_lifetime_as_array<int>(std::declval<const volatile void*>(), 5)));
+
+static_assert(std::is_same_v<decltype(std::start_lifetime_as_array<int>(std::declval<void*>(), 5)), int*>);
+static_assert(std::is_same_v<decltype(std::start_lifetime_as_array<int>(std::declval<const void*>(), 5)), const int*>);
+static_assert(
+    std::is_same_v<decltype(std::start_lifetime_as_array<int>(std::declval<volatile void*>(), 5)), volatile int*>);
+static_assert(std::is_same_v<decltype(std::start_lifetime_as_array<int>(std::declval<const volatile void*>(), 5)),
+                             const volatile int*>);
+
+int main(int, char**) {
+  constexpr std::size_t count = 3;
+  alignas(Trivial) unsigned char buffer[sizeof(Trivial) * count];
+
+  void* p                  = buffer;
+  const void* cp           = buffer;
+  volatile void* vp        = buffer;
+  const volatile void* cvp = buffer;
+
+  Trivial* ptr                  = std::start_lifetime_as_array<Trivial>(p, count);
+  const Trivial* cptr           = std::start_lifetime_as_array<Trivial>(cp, count);
+  volatile Trivial* vptr        = std::start_lifetime_as_array<Trivial>(vp, count);
+  const volatile Trivial* cvptr = std::start_lifetime_as_array<Trivial>(cvp, count);
+
+  assert(static_cast<void*>(ptr) == buffer);
+  assert(static_cast<const void*>(cptr) == buffer);
+  assert(static_cast<volatile void*>(vptr) == buffer);
+  assert(static_cast<const volatile void*>(cvptr) == buffer);
+
+  return 0;
+}



More information about the libcxx-commits mailing list