https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/197458
This PR implement [P2752R3 'Static storage for braced initializers'](https://wg21.link/P2752R3). Fixes https://github.com/llvm/llvm-project/issues/104487 >From 345e312510c89e67d69d686c05345b8351159054 Mon Sep 17 00:00:00 2001 From: yronglin <[email protected]> Date: Wed, 13 May 2026 02:27:01 -0700 Subject: [PATCH] [C++26] Implement P2752R3 'Static storage for braced initializers' Signed-off-by: yronglin <[email protected]> --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/CodeGen/CGExpr.cpp | 44 ++++ clang/lib/CodeGen/CGExprAgg.cpp | 14 +- clang/lib/CodeGen/CodeGenFunction.h | 2 + clang/lib/CodeGen/CodeGenModule.cpp | 22 ++ clang/lib/CodeGen/CodeGenModule.h | 6 + .../dcl.decl/dcl.init/dcl.init.list/p5.cpp | 71 +++++++ .../cxx0x-initializer-stdinitializerlist.cpp | 4 +- .../ptrauth-member-function-pointer.cpp | 6 +- .../static-storage-for-initializer-list.cpp | 190 ++++++++++++++++++ clang/www/cxx_status.html | 2 +- 11 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p5.cpp create mode 100644 clang/test/CodeGenCXX/static-storage-for-initializer-list.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bd91b8723a5c6..5af44ca30aa0f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -149,6 +149,9 @@ C++ Language Changes - ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672) +- Clang now emits eligible ``std::initializer_list`` backing arrays in static + storage by default, implementing `P2752R3 <https://wg21.link/P2752R3>`_. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 5764b59e538ae..b694bff950a23 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -511,6 +511,50 @@ static RawAddress createReferenceTemporary(CodeGenFunction &CGF, llvm_unreachable("unknown storage duration"); } +std::optional<RawAddress> CodeGenFunction::tryEmitStaticInitListBackingArray( + const MaterializeTemporaryExpr *M) { + switch (M->getStorageDuration()) { + case SD_FullExpression: + case SD_Automatic: + break; + case SD_Thread: + case SD_Static: + return std::nullopt; + case SD_Dynamic: + llvm_unreachable("temporary can't have dynamic storage duration"); + } + + SmallVector<const Expr *, 2> CommaLHSs; + SmallVector<SubobjectAdjustment, 2> Adjustments; + const Expr *E = + M->getSubExpr()->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); + if (!Adjustments.empty()) + return std::nullopt; + + QualType ArrayType = E->getType(); + if (!getContext().getAsConstantArrayType(ArrayType)) + return std::nullopt; + + QualType BaseElementType = getContext().getBaseElementType(ArrayType); + if (BaseElementType.isVolatileQualified()) + return std::nullopt; + + if (!ArrayType.isConstantStorage(getContext(), /*ExcludeCtor=*/true, + /*ExcludeDtor=*/false)) + return std::nullopt; + + llvm::Constant *Initializer = + ConstantEmitter(*this).tryEmitAbstract(E, ArrayType); + if (!Initializer) + return std::nullopt; + + for (const Expr *Ignored : CommaLHSs) + EmitIgnoredExpr(Ignored); + + CharUnits Alignment = getContext().getTypeAlignInChars(ArrayType); + return CGM.EmitStaticInitListBackingArray(ArrayType, Initializer, Alignment); +} + /// Helper method to check if the underlying ABI is AAPCS static bool isAAPCS(const TargetInfo &TargetInfo) { return TargetInfo.getABI().starts_with("aapcs"); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index a4282c4f51199..7f362398382e4 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -432,9 +432,17 @@ void AggExprEmitter::VisitCXXStdInitializerListExpr( // Emit an array containing the elements. The array is externally destructed // if the std::initializer_list object is. ASTContext &Ctx = CGF.getContext(); - LValue Array = CGF.EmitLValue(E->getSubExpr()); - assert(Array.isSimple() && "initializer_list array not a simple lvalue"); - Address ArrayPtr = Array.getAddress(); + Address ArrayPtr = Address::invalid(); + if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E->getSubExpr())) { + if (std::optional<RawAddress> StaticArray = + CGF.tryEmitStaticInitListBackingArray(MTE)) + ArrayPtr = Address(*StaticArray); + } + if (!ArrayPtr.isValid()) { + LValue Array = CGF.EmitLValue(E->getSubExpr()); + assert(Array.isSimple() && "initializer_list array not a simple lvalue"); + ArrayPtr = Array.getAddress(); + } const ConstantArrayType *ArrayType = Ctx.getAsConstantArrayType(E->getSubExpr()->getType()); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 464bdeb801a29..8c6b78b9a1a85 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4482,6 +4482,8 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitConditionalOperatorLValue(const AbstractConditionalOperator *E); LValue EmitCastLValue(const CastExpr *E); LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); + std::optional<RawAddress> + tryEmitStaticInitListBackingArray(const MaterializeTemporaryExpr *E); LValue EmitOpaqueValueLValue(const OpaqueValueExpr *e); LValue EmitHLSLArrayAssignLValue(const BinaryOperator *E); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 72a1f771962b1..38fc933ec2917 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7570,6 +7570,28 @@ ConstantAddress CodeGenModule::GetAddrOfGlobalTemporary( return ConstantAddress(CV, Type, Align); } +ConstantAddress +CodeGenModule::EmitStaticInitListBackingArray(QualType /*ArrayType*/, + llvm::Constant *Initializer, + CharUnits Alignment) { + LangAS AddrSpace = GetGlobalConstantAddressSpace(); + auto TargetAS = getContext().getTargetAddressSpace(AddrSpace); + auto *GV = new llvm::GlobalVariable( + getModule(), Initializer->getType(), /*isConstant=*/true, + llvm::GlobalValue::PrivateLinkage, Initializer, ".init.list", nullptr, + llvm::GlobalValue::NotThreadLocal, TargetAS); + GV->setAlignment(Alignment.getAsAlign()); + + llvm::Constant *CV = GV; + if (AddrSpace != LangAS::Default) + CV = performAddrSpaceCast( + GV, llvm::PointerType::get( + getLLVMContext(), + getContext().getTargetAddressSpace(LangAS::Default))); + + return ConstantAddress(CV, Initializer->getType(), Alignment); +} + /// EmitObjCPropertyImplementations - Emit information for synthesized /// properties for an implementation. void CodeGenModule::EmitObjCPropertyImplementations(const diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index b7dab4ef583ff..b666937286572 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1283,6 +1283,12 @@ class CodeGenModule : public CodeGenTypeCache { ConstantAddress GetAddrOfGlobalTemporary(const MaterializeTemporaryExpr *E, const Expr *Inner); + /// Create a private constant global for a std::initializer_list backing array + /// that CodeGen has chosen to place in static storage. + ConstantAddress EmitStaticInitListBackingArray(QualType ArrayType, + llvm::Constant *Initializer, + CharUnits Alignment); + /// Retrieve the record type that describes the state of an /// Objective-C fast enumeration loop (for..in). QualType getObjCFastEnumerationStateType(); diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p5.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p5.cpp new file mode 100644 index 0000000000000..450ef84ab6898 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p5.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 %std_cxx11- -triple x86_64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 %std_cxx11- -DVERIFY -fsyntax-only -verify %s + +namespace std { +using size_t = decltype(sizeof(int)); + +template <class E> class initializer_list { + const E *begin_; + size_t size_; + +public: + constexpr initializer_list() : begin_(nullptr), size_(0) {} + constexpr initializer_list(const E *begin, size_t size) + : begin_(begin), size_(size) {} + constexpr const E *begin() const { return begin_; } + constexpr size_t size() const { return size_; } +}; +} // namespace std + +// CHECK-DAG: @[[STATIC_DOUBLES:.+]] = private constant [3 x double] [double 1.000000e+00, double 2.000000e+00, double 3.000000e+00], align 8 + +// [dcl.init.list] example 12. +void f(std::initializer_list<double> il); + +void g(float x) { + // CHECK-LABEL: define{{.*}} void @_Z1gf( + // CHECK: alloca [3 x double] + f({1, x, 3}); +} + +void h() { + // CHECK-LABEL: define{{.*}} void @_Z1hv() + // CHECK-NOT: alloca [3 x double] + // CHECK: store ptr @[[STATIC_DOUBLES]], ptr + // CHECK: call void @_Z1fSt16initializer_listIdE( + f({1, 2, 3}); +} + +struct A { + mutable int i; +}; + +void q(std::initializer_list<A>); + +void r() { + // CHECK-LABEL: define{{.*}} void @_Z1rv() + // CHECK: alloca [3 x %struct.A] + q({A{1}, A{2}, A{3}}); +} + +struct Dtor { + int i; + ~Dtor(); +}; + +void f_with_dtor(std::initializer_list<Dtor>); + +void non_trivial_dtor() { + // CHECK-LABEL: define{{.*}} void @_Z16non_trivial_dtorv() + // CHECK: alloca [3 x %struct.Dtor] + // CHECK: call void @_ZN4DtorD1Ev( + f_with_dtor({{1}, {2}, {3}}); +} + +#ifdef VERIFY +struct C5 { + std::initializer_list<int> il; // expected-note {{'std::initializer_list' member declared here}} + C5() : il{1, 2, 3} {} + // expected-error@-1 {{backing array for 'std::initializer_list' member 'il' is a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} +}; +#endif diff --git a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp index 62ea3c991f26c..d813acd3c71f8 100644 --- a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -481,8 +481,8 @@ namespace B19773010 { void f1() { // CHECK-LABEL: @_ZN9B197730102f1Ev testcase a{{"", ENUM_CONSTANT}}; - // X86: store ptr @.ref.tmp{{.*}}, ptr %{{.*}}, align 8 - // AMDGCN: store ptr addrspacecast{{.*}} @.ref.tmp{{.*}}{{.*}}, ptr %{{.*}}, align 8 + // X86: store ptr @.init.list{{.*}}, ptr %{{.*}}, align 8 + // AMDGCN: store ptr addrspacecast{{.*}} @.init.list{{.*}}{{.*}}, ptr %{{.*}}, align 8 } void f2() { // CHECK-LABEL: @_ZN9B197730102f2Ev diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index e9436f11b5106..98ae76d082608 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -21,6 +21,8 @@ // CHECK: @__const._Z13testArrayInitv.p1 = private unnamed_addr constant [1 x { i64, i64 }] [{ i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 }], align 8 // CHECK: @__const._Z13testArrayInitv.c0 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 } }, align 8 // CHECK: @__const._Z13testArrayInitv.c1 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 } }, align 8 +// CHECK: @[[INIT_LIST0:[.]init[.]list]] = private constant [1 x { i64, i64 }] [{ i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }], align 8 +// CHECK: @[[INIT_LIST1:[.]init[.]list[.]1]] = private constant [1 x { i64, i64 }] [{ i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }], align 8 // CHECK: @_ZN22testNoexceptConversion6mfptr1E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3:.*]]) to i64), i64 0 }, // CHECK: @_ZN22testNoexceptConversion6mfptr2E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, @@ -427,8 +429,8 @@ MethodTy0 gmethod2 = reinterpret_cast<MethodTy0>(&Derived0::virtual1); // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %p1, ptr align 8 @__const._Z13testArrayInitv.p1, i64 16, i1 false) // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %c0, ptr align 8 @__const._Z13testArrayInitv.c0, i64 16, i1 false) // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %c1, ptr align 8 @__const._Z13testArrayInitv.c1, i64 16, i1 false) -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %{{.*}} align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %{{.*}}, align 8 +// CHECK: store ptr @[[INIT_LIST0]], ptr %{{.*}}, align 8 +// CHECK: store ptr @[[INIT_LIST1]], ptr %{{.*}}, align 8 void initList(std::initializer_list<MethodTy1>); diff --git a/clang/test/CodeGenCXX/static-storage-for-initializer-list.cpp b/clang/test/CodeGenCXX/static-storage-for-initializer-list.cpp new file mode 100644 index 0000000000000..52a2364380dc6 --- /dev/null +++ b/clang/test/CodeGenCXX/static-storage-for-initializer-list.cpp @@ -0,0 +1,190 @@ +// RUN: %clang_cc1 %std_cxx11- -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86,SIZE %s +// RUN: %clang_cc1 %std_cxx11- -triple x86_64-unknown-linux-gnu -fmerge-all-constants -emit-llvm -o - %s | FileCheck --check-prefix=MERGE %s +// RUN: %clang_cc1 %std_cxx11- -triple x86_64-unknown-linux-gnu -DSTART_END -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86,END %s +// RUN: %clang_cc1 %std_cxx11- -triple amdgcn-amd-amdhsa -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,AMDGCN,SIZE %s + +namespace std { +typedef decltype(sizeof(int)) size_t; + +template <class _E> class initializer_list { +#ifdef START_END + const _E *__begin_; + const _E *__end_; + constexpr initializer_list(const _E *__b, const _E *__e) + : __begin_(__b), __end_(__e) {} +#else + const _E *__begin_; + size_t __size_; + constexpr initializer_list(const _E *__b, size_t __s) + : __begin_(__b), __size_(__s) {} +#endif + +public: + typedef _E value_type; + typedef const _E &reference; + typedef const _E &const_reference; + typedef size_t size_type; + typedef const _E *iterator; + typedef const _E *const_iterator; + +#ifdef START_END + constexpr initializer_list() : __begin_(nullptr), __end_(nullptr) {} + constexpr size_t size() const { return __end_ - __begin_; } + constexpr const _E *end() const { return __end_; } +#else + constexpr initializer_list() : __begin_(nullptr), __size_(0) {} + constexpr size_t size() const { return __size_; } + constexpr const _E *end() const { return __begin_ + __size_; } +#endif + constexpr const _E *begin() const { return __begin_; } +}; +} // namespace std + +// X86-DAG: @[[THREE_INTS:.+]] = private constant [3 x i32] [i32 1, i32 2, i32 3], align 4 +// X86-DAG: @[[LOCAL_INTS:.+]] = private constant [3 x i32] [i32 4, i32 5, i32 6], align 4 +// X86-DAG: @[[LIFETIME_INTS:.+]] = private constant [3 x i32] [i32 7, i32 8, i32 9], align 4 +// X86-DAG: @[[CLASS_VALUES:.+]] = private constant [2 x %{{.*}}ConstexprClass] [%{{.*}}ConstexprClass { i32 11 }, %{{.*}}ConstexprClass { i32 12 }], align 4 +// X86-DAG: @[[ISSUE_A:_ZGRN11issue104487L1aE_]] = internal constant [1 x i8] c"x", align 1 +// X86-DAG: @[[ISSUE_B:_ZGRN11issue104487L1bE_]] = internal constant [1 x i8] c"x", align 1 +// MERGE-DAG: @_ZGRN11issue104487L1aE_ = internal constant [1 x i8] c"x", align 1 +// MERGE-DAG: @_ZGRN11issue104487L1bE_ = internal constant [1 x i8] c"x", align 1 +// AMDGCN-DAG: @[[THREE_INTS:.+]] = private addrspace(4) constant [3 x i32] [i32 1, i32 2, i32 3], align 4 +// AMDGCN-DAG: @[[LOCAL_INTS:.+]] = private addrspace(4) constant [3 x i32] [i32 4, i32 5, i32 6], align 4 +// AMDGCN-DAG: @[[LIFETIME_INTS:.+]] = private addrspace(4) constant [3 x i32] [i32 7, i32 8, i32 9], align 4 +// AMDGCN-DAG: @[[CLASS_VALUES:.+]] = private addrspace(4) constant [2 x %{{.*}}ConstexprClass] [%{{.*}}ConstexprClass { i32 11 }, %{{.*}}ConstexprClass { i32 12 }], align 4 +// AMDGCN-DAG: @[[ISSUE_A:_ZGRN11issue104487L1aE_]] = internal addrspace(1) constant [1 x i8] c"x", align 1 +// AMDGCN-DAG: @[[ISSUE_B:_ZGRN11issue104487L1bE_]] = internal addrspace(1) constant [1 x i8] c"x", align 1 + +void take_ints(std::initializer_list<int>); + +void constant_call() { + // CHECK-LABEL: define{{.*}} void @_Z13constant_callv() + // CHECK-NOT: alloca [3 x i32] + // X86: store ptr @[[THREE_INTS]], ptr + // AMDGCN: store ptr addrspacecast (ptr addrspace(4) @[[THREE_INTS]] to ptr), ptr + // SIZE: store i64 3, + // END: store ptr getelementptr inbounds nuw (i8, ptr @[[THREE_INTS]], i64 12), + // CHECK: call void @_Z9take_intsSt16initializer_listIiE( + take_ints({1, 2, 3}); +} + +void local_list() { + // CHECK-LABEL: define{{.*}} void @_Z10local_listv() + // CHECK-NOT: alloca [3 x i32] + // X86: store ptr @[[LOCAL_INTS]], ptr + // AMDGCN: store ptr addrspacecast (ptr addrspace(4) @[[LOCAL_INTS]] to ptr), ptr + std::initializer_list<int> il = {4, 5, 6}; + take_ints(il); +} + +void empty_list() { + // CHECK-LABEL: define{{.*}} void @_Z10empty_listv() + // CHECK-NOT: alloca [0 x i32] + // CHECK: call void @_Z9take_intsSt16initializer_listIiE( + take_ints({}); +} + +void non_constant(int x) { + // CHECK-LABEL: define{{.*}} void @_Z12non_constanti( + // CHECK: alloca [3 x i32] + // CHECK: store i32 %{{.*}}, + take_ints({1, x, 3}); +} + +void take_volatile(std::initializer_list<volatile int>); + +void volatile_element() { + // CHECK-LABEL: define{{.*}} void @_Z16volatile_elementv() + // CHECK: alloca [2 x i32] + take_volatile({21, 22}); +} + +struct MutableMember { + mutable int value; + constexpr MutableMember(int v) : value(v) {} +}; +void take_mutable(std::initializer_list<MutableMember>); + +void mutable_member() { + // CHECK-LABEL: define{{.*}} void @_Z14mutable_memberv() + // CHECK: alloca [2 x %{{.*}}MutableMember] + take_mutable({MutableMember(31), MutableMember(32)}); +} + +struct NestedMutable { + MutableMember member; + constexpr NestedMutable(int v) : member(v) {} +}; +void take_nested_mutable(std::initializer_list<NestedMutable>); + +void recursive_mutable() { + // CHECK-LABEL: define{{.*}} void @_Z17recursive_mutablev() + // CHECK: alloca [2 x %{{.*}}NestedMutable] + take_nested_mutable({NestedMutable(41), NestedMutable(42)}); +} + +struct NonTrivialDtor { + int value; + ~NonTrivialDtor(); +}; +void take_dtor(std::initializer_list<NonTrivialDtor>); + +void non_trivial_dtor() { + // CHECK-LABEL: define{{.*}} void @_Z16non_trivial_dtorv() + // CHECK: alloca [2 x %{{.*}}NonTrivialDtor] + // CHECK: call void @_ZN14NonTrivialDtorD1Ev( + take_dtor({{51}, {52}}); +} + +void take_ints_ref(const std::initializer_list<int> &); + +void lifetime_extension() { + // CHECK-LABEL: define{{.*}} void @_Z18lifetime_extensionv() + // CHECK-NOT: alloca [3 x i32] + // X86: store ptr @[[LIFETIME_INTS]], ptr + // AMDGCN: store ptr addrspacecast (ptr addrspace(4) @[[LIFETIME_INTS]] to ptr), ptr + const auto &il = std::initializer_list<int>{7, 8, 9}; + take_ints_ref(il); +} + +struct ConstexprClass { + int value; + constexpr ConstexprClass(int v) : value(v) {} +}; +void take_class(std::initializer_list<ConstexprClass>); + +void literal_class() { + // CHECK-LABEL: define{{.*}} void @_Z13literal_classv() + // CHECK-NOT: alloca [2 x %{{.*}}ConstexprClass] + // X86: store ptr @[[CLASS_VALUES]], ptr + // AMDGCN: store ptr addrspacecast (ptr addrspace(4) @[[CLASS_VALUES]] to ptr), ptr + take_class({ConstexprClass(11), ConstexprClass(12)}); +} + +namespace issue104487 { +static constexpr std::initializer_list<char> a = {'x'}; +static constexpr std::initializer_list<char> b = {'x'}; +static constexpr bool direct_value = a.begin() == b.begin(); + +bool direct() { + // CHECK-LABEL: define{{.*}} noundef zeroext i1 @_ZN11issue1044876directEv() + // CHECK: ret i1 false + // MERGE-LABEL: define{{.*}} noundef zeroext i1 @_ZN11issue1044876directEv() + // MERGE: ret i1 false + return direct_value; +} + +bool runtime() { + // CHECK-LABEL: define{{.*}} noundef zeroext i1 @_ZN11issue1044877runtimeEv() + // CHECK: store volatile ptr + // CHECK: store volatile ptr + // CHECK: icmp eq ptr + // MERGE-LABEL: define{{.*}} noundef zeroext i1 @_ZN11issue1044877runtimeEv() + // MERGE: @_ZN11issue104487L1aE + // MERGE: @_ZN11issue104487L1bE + // MERGE: icmp eq ptr + const char *volatile ap = a.begin(); + const char *volatile bp = b.begin(); + return ap == bp; +} +} // namespace issue104487 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 8b7290a2a60cc..1137e2161b5f7 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -135,7 +135,7 @@ <h2 id="cxx26">C++2c implementation status</h2> <tr> <td>Static storage for braced initializers</td> <td><a href="https://wg21.link/P2752R3">P2752R3</a> (<a href="#dr">DR</a>)</td> - <td class="none" align="center">No</td> + <td class="unreleased" align="center">Clang 23</td> </tr> <tr> <td>User-generated <tt>static_assert</tt> messages</td> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
