[clang] 231c432 - [clang][Sema] Expand `#embed` in braced lists that initialize non-aggregates (#231938)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 23 20:56:33 PDT 2026
Author: Akash Manna
Date: 2026-10-24T03:56:27Z
New Revision: 231c432591e66aee3bd6a2e0271a6fae17ab0a73
URL: https://github.com/llvm/llvm-project/commit/231c432591e66aee3bd6a2e0271a6fae17ab0a73
DIFF: https://github.com/llvm/llvm-project/commit/231c432591e66aee3bd6a2e0271a6fae17ab0a73.diff
LOG: [clang][Sema] Expand `#embed` in braced lists that initialize non-aggregates (#231938)
Fixes #222796
A multi-byte `#embed` inside braces is one `EmbedExpr`, and only
aggregate initialization slices it. Every other consumer of the braced
list counted it as a single element, so `X x = { #embed ... }` called
the constructor with the first byte only, and a reference or fixed enum
silently took the first byte instead of being rejected.
`Sema::getInitsWithEmbedExpanded()` hands those consumers the list with
each multi-element `EmbedExpr` split into one single-element `EmbedExpr`
per byte, the same slices aggregate initialization already builds.
Constructor selection, braced-argument overload resolution and CTAD use
it; the single-element and excess-element checks go through the new
`InitListExpr::hasZeroInits()` / `hasOneInit()`. Aggregates still keep
the whole `EmbedExpr`.
For more reference, see https://github.com/llvm/llvm-project/pull/218262
LLM tools were used for this contribution. I've reviewed, built, and
tested the change myself before pushing to GitHub.
Added:
Modified:
clang/docs/ReleaseNotes.md
clang/include/clang/AST/Expr.h
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/test/Preprocessor/embed_codegen.cpp
clang/test/Preprocessor/embed_constexpr.c
clang/test/Preprocessor/embed_constexpr.cpp
clang/test/Sema/excess-embed-data.c
clang/test/SemaCXX/embed-init-list.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index adcdd911beb09..44f30666457f9 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -916,6 +916,9 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and
of an array new-expression, or of an array whose elements are of class type.
(#GH128985)
+- Fixed list-initialization from `#embed` passing only the first embedded
+ element to a constructor, reference, scalar or `auto` deduction. (#GH222796)
+
- Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``.
- Fixed a bug where we accepted ``__super`` being qualified by a scope specifier, causing codegen to assertion fail elsewhere. (#GH212988)
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 475feec1a3891..d77efd9bea605 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -5419,6 +5419,17 @@ class InitListExpr : public Expr {
return Sum;
}
+ bool hasZeroInits() const { return InitExprs.empty(); }
+
+ /// Whether getNumInitsWithEmbedExpanded() is 1, without walking the list.
+ bool hasOneInit() const {
+ if (InitExprs.size() != 1)
+ return false;
+ auto *EE =
+ dyn_cast<EmbedExpr>(cast<Expr>(InitExprs[0])->IgnoreParenImpCasts());
+ return !EE || EE->getDataElementCount() == 1;
+ }
+
/// Retrieve the set of initializers.
Expr **getInits() { return reinterpret_cast<Expr **>(InitExprs.data()); }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9082042c48502..cd2227f2aa5c6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9188,6 +9188,11 @@ class Sema final : public SemaBase {
bool IsStringInit(Expr *Init, const ArrayType *AT);
+ /// The initializers of \p ILE with each multi-element EmbedExpr split into
+ /// one EmbedExpr per embedded element; \p Storage is used only if needed.
+ MultiExprArg getInitsWithEmbedExpanded(InitListExpr *ILE,
+ SmallVectorImpl<Expr *> &Storage);
+
/// Determine whether we can perform aggregate initialization for the purposes
/// of overload resolution.
bool CanPerformAggregateInitializationForOverloadResolution(
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index b55ad09c099a6..fa4ef1ce57f17 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1718,7 +1718,7 @@ void InitListChecker::CheckComplexType(const InitializedEntity &Entity,
// the element type of the complex type. The first element initializes
// the real part, and the second element intitializes the imaginary part.
- if (IList->getNumInits() < 2)
+ if (IList->hasZeroInits() || IList->hasOneInit())
return CheckScalarType(Entity, IList, DeclType, Index, StructuredList,
StructuredIndex);
@@ -3637,6 +3637,32 @@ bool Sema::CanPerformAggregateInitializationForOverloadResolution(
return !Check.HadError();
}
+MultiExprArg Sema::getInitsWithEmbedExpanded(InitListExpr *ILE,
+ SmallVectorImpl<Expr *> &Storage) {
+ auto AsMultiElementEmbed = [](Expr *Init) {
+ auto *Embed = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts());
+ return Embed && Embed->getDataElementCount() > 1 ? Embed : nullptr;
+ };
+ if (llvm::none_of(ILE->inits(), AsMultiElementEmbed))
+ return MultiExprArg(ILE->getInits(), ILE->getNumInits());
+
+ Storage.clear();
+ Storage.reserve(ILE->getNumInitsWithEmbedExpanded());
+ for (Expr *Init : ILE->inits()) {
+ EmbedExpr *Embed = AsMultiElementEmbed(Init);
+ if (!Embed) {
+ Storage.push_back(Init);
+ continue;
+ }
+ for (unsigned I = Embed->getStartingElementPos(),
+ E = I + Embed->getDataElementCount();
+ I != E; ++I)
+ Storage.push_back(new (Context) EmbedExpr(Context, Embed->getLocation(),
+ Embed->getData(), I, 1));
+ }
+ return Storage;
+}
+
/// Check that the given Index expression is a valid array designator
/// value. This is essentially just a wrapper around
/// VerifyIntegerConstantExpression that also checks for negative values
@@ -4600,8 +4626,9 @@ static void TryConstructorInitialization(Sema &S,
"argument.");
InitListExpr *ILE =
(IsListInit || IsInitListCopy) ? cast<InitListExpr>(Args[0]) : nullptr;
+ SmallVector<Expr *, 8> ExpandedInits;
MultiExprArg UnwrappedArgs =
- ILE ? MultiExprArg(ILE->getInits(), ILE->getNumInits()) : Args;
+ ILE ? S.getInitsWithEmbedExpanded(ILE, ExpandedInits) : Args;
// The type we're constructing needs to be complete.
if (!S.isCompleteType(Kind.getLocation(), DestType)) {
@@ -4948,7 +4975,7 @@ static void TryReferenceListInitialization(Sema &S,
// (possibly creating temporaries).
// Otherwise, initialize a temporary with the initializer list and
// bind to that.
- if (InitList->getNumInits() == 1) {
+ if (InitList->hasOneInit()) {
Expr *Initializer = InitList->getInit(0);
QualType cv2T2 = S.getCompletedType(Initializer);
Qualifiers T2Quals;
@@ -5042,7 +5069,8 @@ static void TryListInitialization(Sema &S,
// C++ doesn't allow scalar initialization with more than one argument.
// But C99 complex numbers are scalars and it makes sense there.
if (S.getLangOpts().CPlusPlus && DestType->isScalarType() &&
- !DestType->isAnyComplexType() && InitList->getNumInits() > 1) {
+ !DestType->isAnyComplexType() && !InitList->hasZeroInits() &&
+ !InitList->hasOneInit()) {
Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar);
return;
}
@@ -5085,7 +5113,7 @@ static void TryListInitialization(Sema &S,
// (8.5.2 [dcl.init.string]), initialization is performed as described
// in that section.
// - Otherwise, if T is an aggregate, [...] (continue below).
- if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1 &&
+ if (S.getLangOpts().CPlusPlus11 && InitList->hasOneInit() &&
!IsDesignatedInit) {
if (DestType->isRecordType() && DestType->isAggregateType()) {
QualType InitType = InitList->getInit(0)->getType();
@@ -5167,7 +5195,7 @@ static void TryListInitialization(Sema &S,
// - Otherwise, if the initializer list has no elements and T is a
// class type with a default constructor, the object is
// value-initialized.
- if (InitList->getNumInits() == 0) {
+ if (InitList->hasZeroInits()) {
CXXRecordDecl *RD = DestType->castAsCXXRecordDecl();
if (S.LookupDefaultConstructor(RD)) {
TryValueInitialization(S, Entity, Kind, Sequence, InitList);
@@ -5191,7 +5219,7 @@ static void TryListInitialization(Sema &S,
}
if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() &&
- InitList->getNumInits() == 1) {
+ InitList->hasOneInit()) {
Expr *E = InitList->getInit(0);
// - Otherwise, if T is an enumeration with a fixed underlying type,
@@ -8465,7 +8493,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
InitListExpr *InitList = cast<InitListExpr>(Args[0]);
S.Diag(InitList->getExprLoc(), diag::warn_cxx98_compat_ctor_list_init)
<< InitList->getSourceRange();
- MultiExprArg Arg(InitList->getInits(), InitList->getNumInits());
+ SmallVector<Expr *, 8> ExpandedInits;
+ MultiExprArg Arg = S.getInitsWithEmbedExpanded(InitList, ExpandedInits);
CurInit = PerformConstructorInitialization(S, UseTemporary ? TempEntity :
Entity,
Kind, Arg, *Step,
@@ -9000,7 +9029,7 @@ static void diagnoseListInit(Sema &S, const InitializedEntity &Entity,
QualType ArrayType = S.Context.getConstantArrayType(
E.withConst(),
llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()),
- InitList->getNumInits()),
+ InitList->getNumInitsWithEmbedExpanded()),
nullptr, clang::ArraySizeModifier::Normal, 0);
InitializedEntity HiddenArray =
InitializedEntity::InitializeTemporary(ArrayType);
@@ -9041,7 +9070,7 @@ bool InitializationSequence::Diagnose(Sema &S,
Expr *OnlyArg;
if (Args.size() == 1) {
auto *List = dyn_cast<InitListExpr>(Args[0]);
- if (List && List->getNumInits() == 1)
+ if (List && List->hasOneInit())
OnlyArg = List->getInit(0);
else
OnlyArg = Args[0];
@@ -9361,11 +9390,12 @@ bool InitializationSequence::Diagnose(Sema &S,
ArgsRange =
SourceRange(Args.front()->getBeginLoc(), Args.back()->getEndLoc());
+ SmallVector<Expr *, 8> ExpandedInits;
if (Failure == FK_ListConstructorOverloadFailed) {
assert(Args.size() == 1 &&
"List construction from other than 1 argument.");
InitListExpr *InitList = cast<InitListExpr>(Args[0]);
- Args = MultiExprArg(InitList->getInits(), InitList->getNumInits());
+ Args = S.getInitsWithEmbedExpanded(InitList, ExpandedInits);
}
// FIXME: Using "DestType" for the entity we're printing is probably
@@ -9920,6 +9950,20 @@ void InitializationSequence::dump() const {
dump(llvm::errs());
}
+static NarrowingKind getNarrowingKindWithEmbedExpanded(
+ Sema &S, const StandardConversionSequence &SCS, const Expr *Init,
+ APValue &ConstantValue, QualType &ConstantType) {
+ const auto *Embed = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts());
+ if (!Embed || Embed->getDataElementCount() <= 1)
+ return SCS.getNarrowingKind(S.Context, Init, ConstantValue, ConstantType);
+ for (const IntegerLiteral *Elt : Embed->underlying_data_elements())
+ if (NarrowingKind NK =
+ SCS.getNarrowingKind(S.Context, Elt, ConstantValue, ConstantType);
+ NK != NK_Not_Narrowing)
+ return NK;
+ return NK_Not_Narrowing;
+}
+
static void DiagnoseNarrowingInInitList(Sema &S,
const ImplicitConversionSequence &ICS,
QualType PreNarrowingType,
@@ -9956,8 +10000,8 @@ static void DiagnoseNarrowingInInitList(Sema &S,
// C++11 [dcl.init.list]p7: Check whether this is a narrowing conversion.
APValue ConstantValue;
QualType ConstantType;
- switch (SCS->getNarrowingKind(S.Context, PostInit, ConstantValue,
- ConstantType)) {
+ switch (getNarrowingKindWithEmbedExpanded(S, *SCS, PostInit, ConstantValue,
+ ConstantType)) {
case NK_Not_Narrowing:
case NK_Dependent_Narrowing:
// No narrowing occurred.
@@ -10040,9 +10084,8 @@ static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
APValue Value;
QualType PreNarrowingType;
// Reuse C++ narrowing check.
- switch (ICS.Standard.getNarrowingKind(
- S.Context, Init, Value, PreNarrowingType,
- /*IgnoreFloatToIntegralConversion*/ false)) {
+ switch (getNarrowingKindWithEmbedExpanded(S, ICS.Standard, Init, Value,
+ PreNarrowingType)) {
// The value doesn't fit.
case NK_Constant_Narrowing:
S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
@@ -10265,6 +10308,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
(Inits.size() == 1 && Kind.getKind() != InitializationKind::IK_Direct)
? dyn_cast<InitListExpr>(Inits[0])
: nullptr;
+ SmallVector<Expr *, 8> ExpandedInits;
// C++1z [over.match.class.deduct]p1:
// Initialization and overload resolution are performed as described in
@@ -10458,7 +10502,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// Try list constructors unless the list is empty and the class has one or
// more default constructors, in which case those constructors win.
- if (!ListInit->getNumInits()) {
+ if (ListInit->hasZeroInits()) {
for (NamedDecl *D : Guides) {
auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl());
if (FD && FD->getMinRequiredArguments() == 0) {
@@ -10466,7 +10510,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
break;
}
}
- } else if (ListInit->getNumInits() == 1) {
+ } else if (ListInit->hasOneInit()) {
// C++ [over.match.class.deduct]:
// As an exception, the first phase in [over.match.list] (considering
// initializer-list constructors) is omitted if the initializer list
@@ -10484,7 +10528,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Result = TryToResolveOverload(/*OnlyListConstructor*/true);
// Then unwrap the initializer list and try again considering all
// constructors.
- Inits = MultiExprArg(ListInit->getInits(), ListInit->getNumInits());
+ Inits = getInitsWithEmbedExpanded(ListInit, ExpandedInits);
}
// If list-initialization fails, or if we're doing any other kind of
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 72dcb96f098c8..c56f8127834a5 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4244,6 +4244,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
Expr **Args = &From;
unsigned NumArgs = 1;
bool ListInitializing = false;
+ SmallVector<Expr *, 8> ExpandedInits;
if (InitListExpr *InitList = dyn_cast<InitListExpr>(From)) {
// But first, see if there is an init-list-constructor that will work.
OverloadingResult Result = IsInitializerListConstructorConversion(
@@ -4257,8 +4258,10 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
// If we're list-initializing, we pass the individual elements as
// arguments, not the entire list.
- Args = InitList->getInits();
- NumArgs = InitList->getNumInits();
+ MultiExprArg Inits =
+ S.getInitsWithEmbedExpanded(InitList, ExpandedInits);
+ Args = Inits.data();
+ NumArgs = Inits.size();
ListInitializing = true;
}
@@ -5866,7 +5869,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// and the initializer list has a single element that is an
// appropriately-typed string literal (8.5.2 [dcl.init.string]), the
// implicit conversion sequence is the identity conversion.
- if (From->getNumInits() == 1 && !IsDesignatedInit) {
+ if (From->hasOneInit() && !IsDesignatedInit) {
if (ToType->isRecordType() && ToType->isAggregateType()) {
QualType InitType = From->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, ToType) ||
@@ -5905,7 +5908,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// can be implicitly converted to X, the implicit conversion sequence is
// the worst conversion necessary to convert an element of the list to X.
if ((AT || S.isStdInitializerList(ToType, &InitTy)) && !IsDesignatedInit) {
- unsigned e = From->getNumInits();
+ unsigned e = From->getNumInitsWithEmbedExpanded();
ImplicitConversionSequence DfltElt;
DfltElt.setBad(BadConversionSequence::no_conversion, QualType(),
QualType());
@@ -5957,8 +5960,9 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
Result.Standard.setAsIdentityConversion();
Result.Standard.setFromType(InitTy);
Result.Standard.setAllToTypes(InitTy);
- for (unsigned i = 0; i < e; ++i) {
- Expr *Init = From->getInit(i);
+ // An EmbedExpr is a single int prvalue, so one conversion sequence covers
+ // all of its elements; narrowing is checked per element after resolution.
+ for (Expr *Init : From->inits()) {
ImplicitConversionSequence ICS = TryCopyInitialization(
S, Init, InitTy, SuppressUserConversions, InOverloadResolution,
AllowObjCWritebackConversion);
@@ -6046,7 +6050,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// If the initializer list has a single element that is reference-related
// to the parameter type, we initialize the reference from that.
- if (From->getNumInits() == 1 && !IsDesignatedInit) {
+ if (From->hasOneInit() && !IsDesignatedInit) {
Expr *Init = From->getInit(0);
QualType T2 = Init->getType();
@@ -6108,11 +6112,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// - if the initializer list has one element that is not itself an
// initializer list, the implicit conversion sequence is the one
// required to convert the element to the parameter type.
- // Bail out on EmbedExpr as well since we never create EmbedExpr for a
- // single integer.
- unsigned NumInits = From->getNumInits();
- if (NumInits == 1 && !isa<InitListExpr>(From->getInit(0)) &&
- !isa<EmbedExpr>(From->getInit(0))) {
+ if (From->hasOneInit() && !isa<InitListExpr>(From->getInit(0))) {
Result = TryCopyInitialization(
S, From->getInit(0), ToType, SuppressUserConversions,
InOverloadResolution, AllowObjCWritebackConversion);
@@ -6121,7 +6121,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
}
// - if the initializer list has no elements, the implicit conversion
// sequence is the identity conversion.
- else if (NumInits == 0) {
+ else if (From->hasZeroInits()) {
Result.setStandard();
Result.Standard.setAsIdentityConversion();
Result.Standard.setFromType(ToType);
diff --git a/clang/test/Preprocessor/embed_codegen.cpp b/clang/test/Preprocessor/embed_codegen.cpp
index 4f2b5f263985a..9b58131e0a9bf 100644
--- a/clang/test/Preprocessor/embed_codegen.cpp
+++ b/clang/test/Preprocessor/embed_codegen.cpp
@@ -91,3 +91,14 @@ constexpr struct Wrapper W = {
};
}
+
+struct Ctor {
+ Ctor(int, int);
+};
+
+// CHECK: call void @_ZN4CtorC1Eii(ptr {{.*}}%c, i32 noundef 106, i32 noundef 107)
+void ctor_list() {
+ Ctor c = {
+#embed <jk.txt>
+ };
+}
diff --git a/clang/test/Preprocessor/embed_constexpr.c b/clang/test/Preprocessor/embed_constexpr.c
index e4c85cce459e8..3e36bc74db003 100644
--- a/clang/test/Preprocessor/embed_constexpr.c
+++ b/clang/test/Preprocessor/embed_constexpr.c
@@ -24,3 +24,7 @@ static constexpr int data4[] = {
static constexpr float data5[] = {
#embed "big_char.txt" suffix(, -1)
};
+
+static constexpr _BitInt(7) data6[] = {
+#embed __FILE__ limit(4) // expected-error {{constexpr initializer evaluates to 82 which is not exactly representable in type 'const _BitInt(7)'}}
+};
diff --git a/clang/test/Preprocessor/embed_constexpr.cpp b/clang/test/Preprocessor/embed_constexpr.cpp
index c51c02def7dfb..63369599cfbb8 100644
--- a/clang/test/Preprocessor/embed_constexpr.cpp
+++ b/clang/test/Preprocessor/embed_constexpr.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -Wno-c23-extensions
// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -fexperimental-new-constant-interpreter -Wno-c23-extensions
+// RUN: %clang_cc1 %s -ast-dump -ast-dump-filter=ctor_list --embed-dir=%S/Inputs -Wno-c23-extensions | FileCheck %s
// expected-no-diagnostics
constexpr int value(int a, int b) {
@@ -104,3 +105,29 @@ constexpr struct Wrapper W = {
};
static_assert(W.d.h[2] == '3');
+
+struct X {
+ template <typename... T>
+ constexpr X(T... t) : sum((0 + ... + int(t))), size(sizeof...(T)) {}
+ int sum;
+ int size;
+};
+
+// CHECK: CXXConstructExpr {{.*}} 'const X' 'void (int, int)' list
+// CHECK-NEXT: EmbedExpr {{.*}} 'int'
+// CHECK-NEXT: begin: 0
+// CHECK-NEXT: number of elements: 1
+// CHECK-NEXT: EmbedExpr {{.*}} 'int'
+// CHECK-NEXT: begin: 1
+// CHECK-NEXT: number of elements: 1
+constexpr X ctor_list = {
+#embed <jk.txt>
+};
+static_assert(ctor_list.size == 2);
+static_assert(ctor_list.sum == ExpectedValue);
+
+constexpr X ctor_paren(
+#embed <jk.txt>
+);
+static_assert(ctor_paren.size == 2);
+static_assert(ctor_paren.sum == ExpectedValue);
diff --git a/clang/test/Sema/excess-embed-data.c b/clang/test/Sema/excess-embed-data.c
index d5b84921abc89..f6d06bff6a7ca 100644
--- a/clang/test/Sema/excess-embed-data.c
+++ b/clang/test/Sema/excess-embed-data.c
@@ -47,4 +47,10 @@ struct S1 s1 = {
// cpp-error at -2{{excess elements in struct initializer}}
, x
};
+
+int f = {
+#embed __FILE__
+ // c-warning at -1{{excess elements in scalar initializer}}
+ // cpp-error at -3{{excess elements in scalar initializer}}
+};
}
diff --git a/clang/test/SemaCXX/embed-init-list.cpp b/clang/test/SemaCXX/embed-init-list.cpp
index c511ca707a537..d949afccb219f 100644
--- a/clang/test/SemaCXX/embed-init-list.cpp
+++ b/clang/test/SemaCXX/embed-init-list.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s
-// expected-no-diagnostics
namespace std {
typedef decltype(sizeof(int)) size_t;
@@ -13,6 +12,7 @@ template <class _E> class initializer_list {
public:
constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
+ constexpr size_t size() const { return __size_; }
};
} // namespace std
@@ -69,3 +69,159 @@ void tests() {
#embed __FILE__
});
}
+
+namespace GH222796 {
+struct X {
+ template <typename... T>
+ constexpr X(T... t) : sum((0 + ... + int(t))), size(sizeof...(T)) {}
+ int sum;
+ int size;
+};
+
+constexpr X copy_list = {
+#embed __FILE__ limit(2)
+};
+static_assert(copy_list.size == 2, "");
+static_assert(copy_list.sum == '/' + '/', "");
+
+constexpr X direct_list{
+#embed __FILE__ limit(3)
+};
+static_assert(direct_list.size == 3, "");
+static_assert(direct_list.sum == '/' + '/' + ' ', "");
+
+constexpr X functional_cast() {
+ return X{
+#embed __FILE__ limit(2)
+ };
+}
+static_assert(functional_cast().size == 2, "");
+
+constexpr X returned() {
+ return {
+#embed __FILE__ limit(2)
+ };
+}
+static_assert(returned().size == 2, "");
+
+constexpr int arg(X x) { return x.size; }
+constexpr int arg(int) { return -1; }
+static_assert(arg({
+#embed __FILE__ limit(2)
+}) == 2, "");
+
+struct Member {
+ X m{
+#embed __FILE__ limit(2)
+ };
+};
+constexpr Member member{};
+static_assert(member.m.size == 2, "");
+
+constexpr X empty{
+#embed __FILE__ limit(0)
+};
+static_assert(empty.size == 0, "");
+
+struct Two {
+ Two(int, int);
+};
+void two() {
+ Two t = {
+#embed __FILE__ limit(2)
+ };
+ Two *p = new Two{
+#embed __FILE__ limit(2)
+ };
+ delete p;
+ (void)Two{
+#embed __FILE__ limit(2)
+ };
+}
+
+template <class... T> struct Deduced {
+ Deduced(T...);
+};
+Deduced deduced{
+#embed __FILE__ limit(2)
+};
+static_assert(__is_same(decltype(deduced), Deduced<int, int>), "");
+
+template <class T, unsigned N> constexpr unsigned bound(const T (&)[N]) {
+ return N;
+}
+static_assert(bound({
+#embed __FILE__ limit(3)
+}) == 3, "");
+
+template <class T> constexpr std::size_t il_size(std::initializer_list<T> l) {
+ return l.size();
+}
+static_assert(il_size({
+#embed __FILE__ limit(3)
+}) == 3, "");
+
+constexpr std::initializer_list<int> il = {
+#embed __FILE__ limit(3)
+};
+static_assert(il.size() == 3, "");
+
+constexpr const int &one_ref = {
+#embed __FILE__ limit(1)
+};
+static_assert(one_ref == '/', "");
+
+const int &ref = { // expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+};
+
+enum E : int { E1 };
+constexpr E one_enum{
+#embed __FILE__ limit(1)
+};
+static_assert(one_enum == '/', "");
+
+const E e{ // expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+};
+
+int scalar = { // expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+};
+
+constexpr _Complex double complex = {
+#embed __FILE__ limit(2)
+};
+static_assert(__real__ complex == '/' && __imag__ complex == '/', "");
+
+auto deduced_auto{ // expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+};
+
+void deduced_auto_expr() {
+ (void)new auto{ // expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+ };
+ (void)auto{ // expected-warning {{'auto' as a functional-style cast is a C++23 extension}} \
+ expected-error {{excess elements in scalar initializer}}
+#embed __FILE__ limit(2)
+ };
+}
+
+void scalar_arg(int); // expected-note {{candidate function not viable: cannot convert initializer list argument to 'int'}}
+void call_scalar_arg() {
+ scalar_arg({ // expected-error {{no matching function for call to 'scalar_arg'}}
+#embed __FILE__ limit(2)
+ });
+}
+
+_BitInt(7) fits[] = {
+#embed __FILE__ limit(3)
+};
+_BitInt(7) narrowed[] = { // expected-error at +1 {{constant expression evaluates to 82 which cannot be narrowed to type '_BitInt(7)'}}
+#embed __FILE__ limit(4)
+};
+std::initializer_list<_BitInt(7)> narrowed_il = { // expected-error at +1 {{constant expression evaluates to 82 which cannot be narrowed to type '_BitInt(7)'}}
+#embed __FILE__ limit(4)
+};
+}
More information about the cfe-commits
mailing list