https://github.com/My-Bad-2 created https://github.com/llvm/llvm-project/pull/196502
This PR introduces `__builtin_start_lifetime_as`, the compiler frontend primitive required to implement C++23 `std::start_lifetime_as` and `std::start_lifetime_as_array` ([P2590R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2590r2.pdf)). This builtin acts as a gatekeeper in the AST by validating standard compliance by enforcing implicit-lifetime type contraints, intercepting VLAs, and rejecting evaluation in `constexpr` contexts. The implementation also provides a dual-path approach via an optional boolean flag. When provided, this flag instructs `Sema` to bypass the C++23 implicit-lifetime checks. Why Reject VLAs? GCC 16 also rejects VLAs from `std::start_lifetime_as`. After a bit of research, it appears that because `std::start_lifetime_as` requires a fixed-size type where are VLAs have a runtime-determined size. >From a13a3feb53ea6d5d5c333094239267e71b0663cd Mon Sep 17 00:00:00 2001 From: Yash Verma <[email protected]> Date: Thu, 7 May 2026 13:22:51 -0400 Subject: [PATCH 1/3] Adds `bool isImplicitLifetimeType() const` to `clang::Type` to map the C++20 [basic.types.general] rules to the AST. This method serves as a common path to determine whether a type is an implicit-lifetime type. --- clang/include/clang/AST/TypeBase.h | 3 ++ clang/lib/AST/Type.cpp | 59 ++++++++++++++++++++++++++++++ clang/lib/Sema/SemaTypeTraits.cpp | 43 +--------------------- 3 files changed, 63 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index b2887bcc36246..6476d85d46984 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2470,6 +2470,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// or QualType::getSingleStepDesugaredType(const ASTContext&). QualType getLocallyUnqualifiedSingleStepDesugaredType() const; + /// Determine whether this type is an implicit-lifetime type. + bool isImplicitLifetimeType() const; + /// As an extension, we classify types as one of "sized" or "sizeless"; /// every type is one or the other. Standard types are all sized; /// sizeless types are purely an extension. diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 06023fc088a32..ffb25b4f51350 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5914,3 +5914,62 @@ StringRef PredefinedSugarType::getName(Kind KD) { } llvm_unreachable("unexpected kind"); } + +bool Type::isImplicitLifetimeType() const { + // [basic.types.general] p9 + // Scalar types, implicit-lifetime class types ([class.prop]), array types, + // and cv-qualified versions of these types are collectively called + // implicit-lifetime types. + QualType UnqualT = getCanonicalTypeUnqualified(); + + if (UnqualT->isScalarType()) + return true; + + // Arrays are implicit Lifetime types. + if (UnqualT->isArrayType() || UnqualT->isVectorType() || + UnqualT->isExtVectorType()) + return true; + + const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); + if (!RD) + return false; + + // [class.prop] p9 + // A class S is an implicit-lifetime class if + // - it is an aggregate whose destructor is not user-provided or + // - it has atleast one trivial eligible constructor and a trivial, + // non-deleted destructor. + + const CXXDestructorDecl *Dtor = RD->getDestructor(); + if (UnqualT->isAggregateType() && (!Dtor || !Dtor->isUserProvided())) + return true; + + // Type must have a trivial, non-deleted destructor + bool HasTrivialNonDeletedDtr = + RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()); + if (!HasTrivialNonDeletedDtr) + return false; + + for (CXXConstructorDecl *Ctor : RD->ctors()) { + if (Ctor->isIneligibleOrNotSelected() || Ctor->isDeleted()) + continue; + + if (Ctor->isTrivial()) + return true; + } + + if (RD->needsImplicitDefaultConstructor() && + RD->hasTrivialDefaultConstructor() && + !RD->hasNonTrivialDefaultConstructor()) + return true; + + if (RD->needsImplicitCopyConstructor() && RD->hasTrivialCopyConstructor() && + !RD->defaultedCopyConstructorIsDeleted()) + return true; + + if (RD->needsImplicitMoveConstructor() && RD->hasTrivialMoveConstructor() && + !RD->defaultedMoveConstructorIsDeleted()) + return true; + + return false; +} \ No newline at end of file diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index a94a59e8add7b..7f575fdacf9ae 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1070,48 +1070,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, DiagnoseAtomicInCXXTypeTrait(Self, TInfo, tok::kw___builtin_is_implicit_lifetime); - // [basic.types.general] p9 - // Scalar types, implicit-lifetime class types ([class.prop]), - // array types, and cv-qualified versions of these types - // are collectively called implicit-lifetime types. - QualType UnqualT = T->getCanonicalTypeUnqualified(); - if (UnqualT->isScalarType()) - return true; - if (UnqualT->isArrayType() || UnqualT->isVectorType()) - return true; - const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl(); - if (!RD) - return false; - - // [class.prop] p9 - // A class S is an implicit-lifetime class if - // - it is an aggregate whose destructor is not user-provided or - // - it has at least one trivial eligible constructor and a trivial, - // non-deleted destructor. - const CXXDestructorDecl *Dtor = RD->getDestructor(); - if (UnqualT->isAggregateType() && (!Dtor || !Dtor->isUserProvided())) - return true; - bool HasTrivialNonDeletedDtr = - RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()); - if (!HasTrivialNonDeletedDtr) - return false; - for (CXXConstructorDecl *Ctr : RD->ctors()) { - if (Ctr->isIneligibleOrNotSelected() || Ctr->isDeleted()) - continue; - if (Ctr->isTrivial()) - return true; - } - if (RD->needsImplicitDefaultConstructor() && - RD->hasTrivialDefaultConstructor() && - !RD->hasNonTrivialDefaultConstructor()) - return true; - if (RD->needsImplicitCopyConstructor() && RD->hasTrivialCopyConstructor() && - !RD->defaultedCopyConstructorIsDeleted()) - return true; - if (RD->needsImplicitMoveConstructor() && RD->hasTrivialMoveConstructor() && - !RD->defaultedMoveConstructorIsDeleted()) - return true; - return false; + return T->isImplicitLifetimeType(); } case UTT_IsIntangibleType: assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature"); >From 70c37dcf81c65f0f71f5a641068afe21a4df7a78 Mon Sep 17 00:00:00 2001 From: Yash Verma <[email protected]> Date: Fri, 8 May 2026 04:33:46 -0400 Subject: [PATCH 2/3] [Clang] Implement __builtin_start_lifetime_as instrinsic This introduces the compiler frontend primitive required to implement C++23 `std::start_lifetime_as` and `std::start_lifetime_as_array`. Key Changes: - Builtins.td: Register `__builtin_start_lifetime_as` - SemaChecking.cpp: Implement `Sema::BuiltinStartLifetimeAs` to validate pointer arguments and intercept VLAs. - AST Validation: Reuses `isImplicitLifetimeType()` to enforce C++23 standard compliance on the target type. - Dual-mode: Supports an optional secondary boolean argument to bypass the strict implicit-lifetime trait check. - ExprConstant.cpp: Explicitly rejects compile-time evaluation of the intrinsic. --- clang/include/clang/Basic/Builtins.td | 6 + .../clang/Basic/DiagnosticSemaKinds.td | 9 ++ clang/include/clang/Sema/Sema.h | 6 + clang/lib/AST/ExprConstant.cpp | 7 ++ clang/lib/Sema/SemaChecking.cpp | 77 ++++++++++++- .../SemaCXX/builtin-start-lifetime-as.cpp | 108 ++++++++++++++++++ 6 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 clang/test/SemaCXX/builtin-start-lifetime-as.cpp diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 4a7eaeb3d353e..e387c990efdd6 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5883,3 +5883,9 @@ def CountedByRef : Builtin { let Attributes = [NoThrow, CustomTypeChecking]; let Prototype = "int(...)"; } + +def StartLifetimeAs : Builtin { + let Spellings = ["__builtin_start_lifetime_as"]; + let Attributes = [NoThrow, CustomTypeChecking]; + let Prototype = "void *(...)"; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c69b2ce3648f8..a7bb79207c354 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -14238,4 +14238,13 @@ def err_cuda_device_kernel_launch_not_supported def err_cuda_device_kernel_launch_require_rdc : Error<"kernel launch from __device__ or __global__ function requires " "relocatable device code (i.e. requires -fgpu-rdc)">; +def err_start_lifetime_as_not_implicit : Error< + "type %0 is not an implicit-lifetime type; cannot start lifetime">; +def err_builtin_start_lifetime_as_invalid_arg : Error< + "non-pointer argument to '__builtin_start_lifetime_as' is not allowed">; +def note_constexpr_start_lifetime : Note< + "implicitly creating objects and dynamically altering lifetimes is not " + "supported during constant evalutaion">; +def err_builtin_start_lifetime_as_vla : Error< + "variable length arrays are not supported in '__builtin_start_lifetime_as'">; } // end of sema component. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 72beac7526dc5..c94d2db54e279 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2893,6 +2893,12 @@ class Sema final : public SemaBase { bool BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall, unsigned ArgNum, unsigned ArgBits); + /// BuiltinStartLifetimeAs - Ensures the argument is a valid pointer to a + /// complete type. In strict mode (default), it also enforces C++23 + /// implicit-lifetime constraints. Returns true on error, false on success or + /// if type-dependent. + bool BuiltinStartLifetimeAs(CallExpr *TheCall); + /// Checks that a call expression's argument count is at least the desired /// number. This is useful when doing custom type-checking on a variadic /// function. Returns true on error. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3f3a80f5b77a3..50a02031fb0b2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10549,6 +10549,13 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return ZeroInitialization(E); } + case Builtin::BI__builtin_start_lifetime_as: { + // C++23 forbids evaluating lifetime-altering abstract machine magic inside + // a constant expression. + Info.FFDiag(E, diag::note_constexpr_start_lifetime); + return false; + } + case Builtin::BImemcpy: case Builtin::BImemmove: case Builtin::BIwmemcpy: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 4706fa5d3cde0..15043defc459a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -41,6 +41,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/UnresolvedSet.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/IdentifierTable.h" @@ -3125,7 +3126,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return BuiltinIsWithinLifetime(*this, TheCall); case Builtin::BI__builtin_trivially_relocate: return BuiltinTriviallyRelocate(*this, TheCall); - + case Builtin::BI__builtin_start_lifetime_as: + if (BuiltinStartLifetimeAs(TheCall)) + return ExprError(); + break; case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_add_1: case Builtin::BI__sync_fetch_and_add_2: @@ -17298,3 +17302,74 @@ void Sema::CheckTCBEnforcement(const SourceLocation CallExprLoc, } } } + +bool Sema::BuiltinStartLifetimeAs(CallExpr *Call) { + // Argument count: 1 (Strict) or 2 (Pointer, StrictnessFlag) + if (checkArgCountRange(Call, 1, 2)) + return true; + + Expr *PtrArg = Call->getArg(0); + QualType PtrType = PtrArg->getType(); + + const PointerType *PT = PtrType->getAs<PointerType>(); + if (!PT) { + Diag(PtrArg->getExprLoc(), diag::err_builtin_start_lifetime_as_invalid_arg) + << PtrArg->getSourceRange(); + return true; + } + + QualType PointeeType = PT->getPointeeType(); + + // Variable Length Arrays are "complete types" in clang, but they lack a + // static size. GCC doesn't support variable-sized arrays in + // `std::start_lifetime_as`. + if (PointeeType->isVariableArrayType()) { + // We cannot use `err_vla_unsupported` because of some FormatDiagnostic + // issue + Diag(PtrArg->getExprLoc(), diag::err_builtin_start_lifetime_as_vla) + << PtrArg->getSourceRange(); + return true; + } + + if (Call->isTypeDependent() || Call->isValueDependent()) + return false; + + // Reject void and function pointers immediately. + if (PointeeType->isVoidType() || PointeeType->isFunctionType()) { + Diag(PtrArg->getExprLoc(), diag::err_start_lifetime_as_not_implicit) + << PointeeType << PtrArg->getSourceRange(); + return true; + } + + // Ensure the type is fully defined + if (RequireCompleteType(PtrArg->getExprLoc(), PointeeType, + diag::err_incomplete_type)) + return true; + + bool IsStrict = true; + if (Call->getNumArgs() == 2) { + Expr *StrictArg = Call->getArg(1); + + std::optional<llvm::APSInt> OptStrict = + StrictArg->getIntegerConstantExpr(Context); + + if (!OptStrict) { + Diag(StrictArg->getExprLoc(), diag::err_expr_not_ice) + << 0 << StrictArg->getSourceRange(); + return true; + } + + IsStrict = OptStrict->getBoolValue(); + } + + // Note: If the user called `start_lifetime_as_array<int>`, the PointeeType is + // simply `int` so no need to unwrap the array. + if (IsStrict && !PointeeType->isImplicitLifetimeType()) { + Diag(PtrArg->getExprLoc(), diag::err_start_lifetime_as_not_implicit) + << PointeeType << PtrArg->getSourceRange(); + return true; + } + + Call->setType(PtrType); + return false; +} diff --git a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp new file mode 100644 index 0000000000000..82286e60914aa --- /dev/null +++ b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp @@ -0,0 +1,108 @@ +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s + +namespace InvalidArgs { + void test_non_pointers(int x) { + __builtin_start_lifetime_as(x); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} + __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} + } + + void test_void_and_func(void *p, void (*f)()) { + __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not an implicit-lifetime type; cannot start lifetime}} + __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is not an implicit-lifetime type; cannot start lifetime}} + } + + struct Incomplete; // expected-note {{forward declaration of 'InvalidArgs::Incomplete'}} + void test_incomplete(Incomplete *p) { + __builtin_start_lifetime_as(p); // expected-error {{incomplete type 'Incomplete' where a complete type is required}} + } + + void test_vla(int n) { // expected-note {{declared here}} + int vla[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}} \ + // expected-note {{function parameter 'n' with unknown value cannot be used in a constant expression}} + __builtin_start_lifetime_as(&vla); // expected-error {{variable length arrays are not supported in '__builtin_start_lifetime_as'}} + } +} // namespace InvalidArgs + +namespace ImplicitLifetimeRules { + // Valid types + struct Trivial { int x; int y; }; + struct TrivialArray { int arr[5]; }; + union TrivialUnion { int a; float b; }; + + struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; }; + + // Invalid types + struct UserDtor { ~UserDtor() {} }; + struct UserCopy { UserCopy(const UserCopy&); }; + struct VirtualBase { virtual void f(); }; + + void test_valid_types(void* p) { + __builtin_start_lifetime_as((int*)p); + __builtin_start_lifetime_as((Trivial*)p); + __builtin_start_lifetime_as((TrivialArray*)p); + __builtin_start_lifetime_as((TrivialUnion*)p); + __builtin_start_lifetime_as((AggregateNoDtor*)p); + + // Arrays of implicit-lifetime types are implicitly valid + __builtin_start_lifetime_as((int(*)[5])p); + + // Arrays of non-implicit-lifetime types are also valid under C++23 + __builtin_start_lifetime_as((UserDtor(*)[5])p); + } + + void test_invalid_types(void *p) { + __builtin_start_lifetime_as((UserDtor*)p); // expected-error {{type 'UserDtor' is not an implicit-lifetime type; cannot start lifetime}} + __builtin_start_lifetime_as((UserCopy*)p); // expected-error {{type 'UserCopy' is not an implicit-lifetime type; cannot start lifetime}} + __builtin_start_lifetime_as((VirtualBase*)p); // expected-error {{type 'VirtualBase' is not an implicit-lifetime type; cannot start lifetime}} + } +} // namespace ImplicitLifetimeRules + +namespace StrictnessFlag { + struct NonTrivial { ~NonTrivial() {} }; + + void test_flag(NonTrivial* p, int runtime_flag) { + // defaults to strict + __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} + + // explicit strict + __builtin_start_lifetime_as(p, true); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} + + // bypasses the implicit-lifetime check + __builtin_start_lifetime_as(p, false); + + // Flag must be an ICE (Integer Constant Expression) + __builtin_start_lifetime_as(p, runtime_flag); // expected-error {{expression is not an integer constant expression}} + } +} // namespace StrictnessFlag + +namespace Templates { + template <typename T> + T* test_dependent_type(void *p) { + // Should defer evaluation until instantiation + return __builtin_start_lifetime_as((T*)p); // expected-error {{type 'Templates::std::string' is not an implicit-lifetime type}} + } + + namespace std { struct string { ~string() {} }; } + + void instantiate(void *p) { + test_dependent_type<int>(p); + test_dependent_type<std::string>(p); // expected-note {{in instantiation of function template specialization 'Templates::test_dependent_type<Templates::std::string>' requested here}} + } +} // namespace Templates + +namespace ConstexprEvaluation { + struct Trivial { int x; }; + + constexpr Trivial* test_constexpr_builtin(Trivial* p) { + return __builtin_start_lifetime_as(p); // expected-note {{subexpression not valid in a constant expression}} + } + + constexpr bool test_eval() { + Trivial t{0}; + Trivial* p = test_constexpr_builtin(&t); // expected-note {{in call to 'test_constexpr_builtin(&t)'}} + return p != nullptr; + } + + constexpr bool b = test_eval(); // expected-error {{constexpr variable 'b' must be initialized by a constant expression}} \ + // expected-note {{in call to 'test_eval()'}} +} >From 1de2a8a4777d0d4ad893260e73e0390971a5ff7b Mon Sep 17 00:00:00 2001 From: Yash Verma <[email protected]> Date: Fri, 8 May 2026 05:41:54 -0400 Subject: [PATCH 3/3] [Clang][CodeGen] Implement IR generation for __builtin_start_lifetime_as This path implements the CodeGen lowering for the C++23 `std::start_lifetime_as` intrinsic. Key Changes: - CGBuiltin.cpp: Route `BI__builtin_start_lifetime_as` to share the existing `__builtin_launder` optimization barrier path. --- clang/lib/CodeGen/CGBuiltin.cpp | 2 + .../CodeGenCXX/builtin-start-lifetime-as.cpp | 42 +++++++ .../SemaCXX/builtin-start-lifetime-as.cpp | 114 +++++++++--------- 3 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 67de2a34f44ea..5d05ecad6a6c8 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -26,6 +26,7 @@ #include "TargetInfo.h" #include "clang/AST/OSLog.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/TargetInfo.h" #include "llvm/IR/InlineAsm.h" @@ -5180,6 +5181,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(nullptr); } + case Builtin::BI__builtin_start_lifetime_as: case Builtin::BI__builtin_launder: { const Expr *Arg = E->getArg(0); QualType ArgTy = Arg->getType()->getPointeeType(); diff --git a/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp b/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp new file mode 100644 index 0000000000000..264ae6e4d4e5f --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-start-lifetime-as.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -std=c++23 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +struct Trivial { int x; float y; }; + +// CHECK-LABEL: define {{.*}}@_Z12test_trivialPv( +// CHECK-NOT: call ptr @llvm.launder.invariant.group +// CHECK: ret ptr +Trivial* test_trivial(void* p) { + return __builtin_start_lifetime_as((Trivial*)p); +} + +// CHECK-LABEL: define {{.*}}@_Z18test_array_trivialPv( +// CHECK-NOT: call ptr @llvm.launder.invariant.group +// CHECK: ret ptr +int (*test_array_trivial(void* p))[5] { + return __builtin_start_lifetime_as((int(*)[5])p); +} + +struct WithConst { const int x; }; +struct WithRef { int& x; }; + +// CHECK-LABEL: define {{.*}}@_Z10test_constPv( +// CHECK-NOT: call ptr @llvm.launder.invariant.group +// CHECK: ret ptr +WithConst* test_const(void* p) { + return __builtin_start_lifetime_as((WithConst*)p); +} + +// CHECK-LABEL: define {{.*}}@_Z8test_refPv( +// CHECK-NOT: call ptr @llvm.launder.invariant.group +// CHECK: ret ptr +WithRef* test_ref(void* p) { + return __builtin_start_lifetime_as((WithRef*)p); +} + +// CHECK-LABEL: define {{.*}}@_Z20test_strict_flag_laxPv( +// CHECK-NOT: call ptr @llvm.launder.invariant.group +// CHECK: ret ptr +WithConst* test_strict_flag_lax(void* p) { + // Pass 'false' to lax mode; CodeGen cleanly drops the second argument + return __builtin_start_lifetime_as((WithConst*)p, false); +} diff --git a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp index 82286e60914aa..fec29a1945b67 100644 --- a/clang/test/SemaCXX/builtin-start-lifetime-as.cpp +++ b/clang/test/SemaCXX/builtin-start-lifetime-as.cpp @@ -1,56 +1,56 @@ // RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s namespace InvalidArgs { - void test_non_pointers(int x) { - __builtin_start_lifetime_as(x); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} - __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} - } - - void test_void_and_func(void *p, void (*f)()) { - __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not an implicit-lifetime type; cannot start lifetime}} - __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is not an implicit-lifetime type; cannot start lifetime}} - } - - struct Incomplete; // expected-note {{forward declaration of 'InvalidArgs::Incomplete'}} - void test_incomplete(Incomplete *p) { - __builtin_start_lifetime_as(p); // expected-error {{incomplete type 'Incomplete' where a complete type is required}} - } - - void test_vla(int n) { // expected-note {{declared here}} - int vla[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}} \ - // expected-note {{function parameter 'n' with unknown value cannot be used in a constant expression}} - __builtin_start_lifetime_as(&vla); // expected-error {{variable length arrays are not supported in '__builtin_start_lifetime_as'}} - } -} // namespace InvalidArgs - -namespace ImplicitLifetimeRules { - // Valid types - struct Trivial { int x; int y; }; - struct TrivialArray { int arr[5]; }; - union TrivialUnion { int a; float b; }; - - struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; }; + void test_non_pointers(int x) { + __builtin_start_lifetime_as(x); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} + __builtin_start_lifetime_as(nullptr); // expected-error {{non-pointer argument to '__builtin_start_lifetime_as' is not allowed}} + } - // Invalid types - struct UserDtor { ~UserDtor() {} }; - struct UserCopy { UserCopy(const UserCopy&); }; - struct VirtualBase { virtual void f(); }; + void test_void_and_func(void *p, void (*f)()) { + __builtin_start_lifetime_as(p); // expected-error {{type 'void' is not an implicit-lifetime type; cannot start lifetime}} + __builtin_start_lifetime_as(f); // expected-error {{type 'void ()' is not an implicit-lifetime type; cannot start lifetime}} + } - void test_valid_types(void* p) { - __builtin_start_lifetime_as((int*)p); - __builtin_start_lifetime_as((Trivial*)p); - __builtin_start_lifetime_as((TrivialArray*)p); - __builtin_start_lifetime_as((TrivialUnion*)p); - __builtin_start_lifetime_as((AggregateNoDtor*)p); + struct Incomplete; // expected-note {{forward declaration of 'InvalidArgs::Incomplete'}} + void test_incomplete(Incomplete *p) { + __builtin_start_lifetime_as(p); // expected-error {{incomplete type 'Incomplete' where a complete type is required}} + } - // Arrays of implicit-lifetime types are implicitly valid - __builtin_start_lifetime_as((int(*)[5])p); + void test_vla(int n) { // expected-note {{declared here}} + int vla[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}} \ + // expected-note {{function parameter 'n' with unknown value cannot be used in a constant expression}} + __builtin_start_lifetime_as(&vla); // expected-error {{variable length arrays are not supported in '__builtin_start_lifetime_as'}} + } +} // namespace InvalidArgs - // Arrays of non-implicit-lifetime types are also valid under C++23 - __builtin_start_lifetime_as((UserDtor(*)[5])p); - } +namespace ImplicitLifetimeRules { + // Valid types + struct Trivial { int x; int y; }; + struct TrivialArray { int arr[5]; }; + union TrivialUnion { int a; float b; }; + + struct AggregateNoDtor { int a; ~AggregateNoDtor() = default; }; + + // Invalid types + struct UserDtor { ~UserDtor() {} }; + struct UserCopy { UserCopy(const UserCopy&); }; + struct VirtualBase { virtual void f(); }; + + void test_valid_types(void* p) { + __builtin_start_lifetime_as((int*)p); + __builtin_start_lifetime_as((Trivial*)p); + __builtin_start_lifetime_as((TrivialArray*)p); + __builtin_start_lifetime_as((TrivialUnion*)p); + __builtin_start_lifetime_as((AggregateNoDtor*)p); + + // Arrays of implicit-lifetime types are implicitly valid + __builtin_start_lifetime_as((int(*)[5])p); + + // Arrays of non-implicit-lifetime types are also valid under C++23 + __builtin_start_lifetime_as((UserDtor(*)[5])p); + } - void test_invalid_types(void *p) { + void test_invalid_types(void *p) { __builtin_start_lifetime_as((UserDtor*)p); // expected-error {{type 'UserDtor' is not an implicit-lifetime type; cannot start lifetime}} __builtin_start_lifetime_as((UserCopy*)p); // expected-error {{type 'UserCopy' is not an implicit-lifetime type; cannot start lifetime}} __builtin_start_lifetime_as((VirtualBase*)p); // expected-error {{type 'VirtualBase' is not an implicit-lifetime type; cannot start lifetime}} @@ -58,21 +58,21 @@ namespace ImplicitLifetimeRules { } // namespace ImplicitLifetimeRules namespace StrictnessFlag { - struct NonTrivial { ~NonTrivial() {} }; + struct NonTrivial { ~NonTrivial() {} }; - void test_flag(NonTrivial* p, int runtime_flag) { - // defaults to strict - __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} + void test_flag(NonTrivial* p, int runtime_flag) { + // defaults to strict + __builtin_start_lifetime_as(p); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} - // explicit strict - __builtin_start_lifetime_as(p, true); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} + // explicit strict + __builtin_start_lifetime_as(p, true); // expected-error {{type 'NonTrivial' is not an implicit-lifetime type}} - // bypasses the implicit-lifetime check - __builtin_start_lifetime_as(p, false); + // bypasses the implicit-lifetime check + __builtin_start_lifetime_as(p, false); - // Flag must be an ICE (Integer Constant Expression) - __builtin_start_lifetime_as(p, runtime_flag); // expected-error {{expression is not an integer constant expression}} - } + // Flag must be an ICE (Integer Constant Expression) + __builtin_start_lifetime_as(p, runtime_flag); // expected-error {{expression is not an integer constant expression}} + } } // namespace StrictnessFlag namespace Templates { @@ -105,4 +105,4 @@ namespace ConstexprEvaluation { constexpr bool b = test_eval(); // expected-error {{constexpr variable 'b' must be initialized by a constant expression}} \ // expected-note {{in call to 'test_eval()'}} -} +} // namespace ConstexprEvaluation _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
