https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/231938
>From 83b7d0718cd041648bb90884614a3cf79a845070 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Wed, 14 Oct 2026 00:03:45 +0530 Subject: [PATCH 1/4] 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, fixed enum or `auto` deduction silently took the first byte instead of being rejected. `Sema::getInitsWithEmbedExpanded()` hands those consumers the list with each `EmbedExpr` replaced by one `int` literal per byte, which is what the parenthesized form already gets from the token stream. Constructor selection, braced-argument overload resolution, CTAD and `auto` deduction use it; the single-element and excess-element checks count the expanded length. Aggregates still keep the `EmbedExpr`. LLM tools were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub. --- clang/docs/ReleaseNotes.md | 3 + clang/include/clang/Sema/Sema.h | 5 + clang/lib/Sema/SemaDecl.cpp | 3 +- clang/lib/Sema/SemaExprCXX.cpp | 16 +-- clang/lib/Sema/SemaInit.cpp | 64 +++++++-- clang/lib/Sema/SemaOverload.cpp | 24 ++-- clang/test/Preprocessor/embed_codegen.cpp | 11 ++ clang/test/Preprocessor/embed_constexpr.cpp | 23 +++ clang/test/Sema/excess-embed-data.c | 6 + clang/test/SemaCXX/embed-init-list.cpp | 150 +++++++++++++++++++- 10 files changed, 268 insertions(+), 37 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 1dc1764978ea2..fa0f41dd69363 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -775,6 +775,9 @@ features cannot lower the translation-unit ABI level; 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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4c51a3c03c80c..2bf79b96344c1 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9132,6 +9132,11 @@ class Sema final : public SemaBase { bool IsStringInit(Expr *Init, const ArrayType *AT); + /// The initializers of \p ILE with each EmbedExpr expanded to one + /// IntegerLiteral 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/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1459b71326375..95baa2b65be39 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13577,9 +13577,10 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl, InitsCopy); } + SmallVector<Expr *, 8> ExpandedInits; if (DirectInit) { if (auto *IL = dyn_cast<InitListExpr>(Init)) - DeduceInits = IL->inits(); + DeduceInits = getInitsWithEmbedExpanded(IL, ExpandedInits); } // Deduction only works if we have exactly one source expression. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d9bd060f7a0bf..9b4ec2af7ef26 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1570,10 +1570,10 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); } else if (Deduced && !Deduced->isDeduced()) { MultiExprArg Inits = Exprs; - if (ListInitialization) { - auto *ILE = cast<InitListExpr>(Exprs[0]); - Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits()); - } + SmallVector<Expr *, 8> ExpandedInits; + if (ListInitialization) + Inits = getInitsWithEmbedExpanded(cast<InitListExpr>(Exprs[0]), + ExpandedInits); if (Ty->getAs<AutoType>()) DiagCompat(TyBeginLoc, diag_compat::auto_expr) << FullRange; @@ -2235,11 +2235,11 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, return ExprError(); } else if (Deduced && !Deduced->isDeduced()) { MultiExprArg Inits = Exprs; + SmallVector<Expr *, 8> ExpandedInits; bool Braced = (InitStyle == CXXNewInitializationStyle::Braces); - if (Braced) { - auto *ILE = cast<InitListExpr>(Exprs[0]); - Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits()); - } + if (Braced) + Inits = getInitsWithEmbedExpanded(cast<InitListExpr>(Exprs[0]), + ExpandedInits); if (InitStyle == CXXNewInitializationStyle::None || Inits.empty()) return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index d2aa8691102aa..6a9c89ecf8d03 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->getNumInitsWithEmbedExpanded() < 2) return CheckScalarType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); @@ -3637,6 +3637,34 @@ bool Sema::CanPerformAggregateInitializationForOverloadResolution( return !Check.HadError(); } +MultiExprArg Sema::getInitsWithEmbedExpanded(InitListExpr *ILE, + SmallVectorImpl<Expr *> &Storage) { + if (llvm::none_of(ILE->inits(), [](const Expr *Init) { + return isa<EmbedExpr>(Init->IgnoreParenImpCasts()); + })) + return MultiExprArg(ILE->getInits(), ILE->getNumInits()); + + Storage.clear(); + Storage.reserve(ILE->getNumInitsWithEmbedExpanded()); + for (Expr *Init : ILE->inits()) { + auto *Embed = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts()); + if (!Embed) { + Storage.push_back(Init); + continue; + } + const StringLiteral *Data = Embed->getDataStringLiteral(); + QualType ElemTy = Embed->getType(); + unsigned Width = Context.getIntWidth(ElemTy); + for (unsigned I = Embed->getStartingElementPos(), + E = I + Embed->getDataElementCount(); + I != E; ++I) + Storage.push_back(IntegerLiteral::Create( + Context, llvm::APInt(Width, Data->getCodeUnit(I)), ElemTy, + Embed->getLocation())); + } + 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 +4628,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 +4977,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->getNumInitsWithEmbedExpanded() == 1) { Expr *Initializer = InitList->getInit(0); QualType cv2T2 = S.getCompletedType(Initializer); Qualifiers T2Quals; @@ -5039,10 +5068,12 @@ static void TryListInitialization(Sema &S, return; } + unsigned NumInits = InitList->getNumInitsWithEmbedExpanded(); + // 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() && NumInits > 1) { Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar); return; } @@ -5085,8 +5116,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 && - !IsDesignatedInit) { + if (S.getLangOpts().CPlusPlus11 && NumInits == 1 && !IsDesignatedInit) { if (DestType->isRecordType() && DestType->isAggregateType()) { QualType InitType = InitList->getInit(0)->getType(); if (S.Context.hasSameUnqualifiedType(InitType, DestType) || @@ -5167,7 +5197,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 (NumInits == 0) { CXXRecordDecl *RD = DestType->castAsCXXRecordDecl(); if (S.LookupDefaultConstructor(RD)) { TryValueInitialization(S, Entity, Kind, Sequence, InitList); @@ -5191,7 +5221,7 @@ static void TryListInitialization(Sema &S, } if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() && - InitList->getNumInits() == 1) { + NumInits == 1) { Expr *E = InitList->getInit(0); // - Otherwise, if T is an enumeration with a fixed underlying type, @@ -8464,7 +8494,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, @@ -8998,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); @@ -9039,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->getNumInitsWithEmbedExpanded() == 1) OnlyArg = List->getInit(0); else OnlyArg = Args[0]; @@ -9359,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 @@ -10263,6 +10295,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 @@ -10456,7 +10489,8 @@ 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()) { + unsigned NumInits = ListInit->getNumInitsWithEmbedExpanded(); + if (!NumInits) { for (NamedDecl *D : Guides) { auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl()); if (FD && FD->getMinRequiredArguments() == 0) { @@ -10464,7 +10498,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( break; } } - } else if (ListInit->getNumInits() == 1) { + } else if (NumInits == 1) { // 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 @@ -10482,7 +10516,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 62b6ad8ffb564..c3bf72c10b01a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -4242,6 +4242,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( @@ -4255,8 +4256,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; } @@ -5854,6 +5857,8 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, IsDesignatedInit) return Result; + unsigned NumInits = From->getNumInitsWithEmbedExpanded(); + // Per DR1467 and DR2137: // If the parameter type is an aggregate class X and the initializer list // has a single element of type cv U, where U is X or a class derived from @@ -5864,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 (NumInits == 1 && !IsDesignatedInit) { if (ToType->isRecordType() && ToType->isAggregateType()) { QualType InitType = From->getInit(0)->getType(); if (S.Context.hasSameUnqualifiedType(InitType, ToType) || @@ -5903,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 = NumInits; ImplicitConversionSequence DfltElt; DfltElt.setBad(BadConversionSequence::no_conversion, QualType(), QualType()); @@ -5955,8 +5960,7 @@ 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); + for (Expr *Init : From->inits()) { ImplicitConversionSequence ICS = TryCopyInitialization( S, Init, InitTy, SuppressUserConversions, InOverloadResolution, AllowObjCWritebackConversion); @@ -6044,7 +6048,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 (NumInits == 1 && !IsDesignatedInit) { Expr *Init = From->getInit(0); QualType T2 = Init->getType(); @@ -6106,11 +6110,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 (NumInits == 1 && !isa<InitListExpr>(From->getInit(0))) { Result = TryCopyInitialization( S, From->getInit(0), ToType, SuppressUserConversions, InOverloadResolution, AllowObjCWritebackConversion); 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.cpp b/clang/test/Preprocessor/embed_constexpr.cpp index c51c02def7dfb..31d5e22587a6f 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,25 @@ 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: IntegerLiteral {{.*}} 'int' 106 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 107 +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@-2{{excess elements in struct initializer}} , x }; + +int f = { +#embed __FILE__ + // c-warning@-1{{excess elements in scalar initializer}} + // cpp-error@-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..0ab7e53765cbd 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,151 @@ 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{ +#embed __FILE__ limit(2) + // expected-error@-1 {{initializer for variable 'deduced_auto' with type 'auto' contains multiple expressions}} +}; + +void deduced_auto_expr() { + (void)new auto{ +#embed __FILE__ limit(2) + // expected-error@-1 {{new expression for type 'auto' contains multiple constructor arguments}} + }; + (void)auto{ // expected-warning {{'auto' as a functional-style cast is a C++23 extension}} +#embed __FILE__ limit(2) + // expected-error@-1 {{initializer for functional-style cast to 'auto' contains multiple expressions}} + }; +} + +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) + }); +} +} >From f85e083590e55a5fc69e0b3bf4425216717de65b Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 10:39:53 +0530 Subject: [PATCH 2/4] Address review: EmbedExpr slices, hasZeroInits/hasOneInit, no expansion for auto Expanded elements are single-element EmbedExprs so the embed origin survives. Zero/one-element checks use the new InitListExpr predicates instead of counting the list. auto deduction no longer expands; the deduced scalar rejects the list anyway. --- clang/include/clang/AST/Expr.h | 11 ++++++ clang/include/clang/Sema/Sema.h | 4 +- clang/lib/Sema/SemaDecl.cpp | 3 +- clang/lib/Sema/SemaExprCXX.cpp | 16 ++++---- clang/lib/Sema/SemaInit.cpp | 41 ++++++++++----------- clang/lib/Sema/SemaOverload.cpp | 12 +++--- clang/test/Preprocessor/embed_constexpr.cpp | 8 +++- clang/test/SemaCXX/embed-init-list.cpp | 10 ++--- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 6863d9c2633df..ef442213a70d8 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5398,6 +5398,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 61ff0e06e0471..f478bd73cfd39 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9139,8 +9139,8 @@ class Sema final : public SemaBase { bool IsStringInit(Expr *Init, const ArrayType *AT); - /// The initializers of \p ILE with each EmbedExpr expanded to one - /// IntegerLiteral per embedded element; \p Storage is used only if needed. + /// 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); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f16c5b58149b7..4f6e65c1c4708 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13595,10 +13595,9 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl, InitsCopy); } - SmallVector<Expr *, 8> ExpandedInits; if (DirectInit) { if (auto *IL = dyn_cast<InitListExpr>(Init)) - DeduceInits = getInitsWithEmbedExpanded(IL, ExpandedInits); + DeduceInits = IL->inits(); } // Deduction only works if we have exactly one source expression. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 5310220ca2987..916c5308508f1 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1570,10 +1570,10 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); } else if (Deduced && !Deduced->isDeduced()) { MultiExprArg Inits = Exprs; - SmallVector<Expr *, 8> ExpandedInits; - if (ListInitialization) - Inits = getInitsWithEmbedExpanded(cast<InitListExpr>(Exprs[0]), - ExpandedInits); + if (ListInitialization) { + auto *ILE = cast<InitListExpr>(Exprs[0]); + Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits()); + } if (Ty->getAs<AutoType>()) DiagCompat(TyBeginLoc, diag_compat::auto_expr) << FullRange; @@ -2235,11 +2235,11 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, return ExprError(); } else if (Deduced && !Deduced->isDeduced()) { MultiExprArg Inits = Exprs; - SmallVector<Expr *, 8> ExpandedInits; bool Braced = (InitStyle == CXXNewInitializationStyle::Braces); - if (Braced) - Inits = getInitsWithEmbedExpanded(cast<InitListExpr>(Exprs[0]), - ExpandedInits); + if (Braced) { + auto *ILE = cast<InitListExpr>(Exprs[0]); + Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits()); + } if (InitStyle == CXXNewInitializationStyle::None || Inits.empty()) return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 2b284af9d6ac0..da4c18eadcd6e 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->getNumInitsWithEmbedExpanded() < 2) + if (IList->hasZeroInits() || IList->hasOneInit()) return CheckScalarType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); @@ -3639,28 +3639,26 @@ bool Sema::CanPerformAggregateInitializationForOverloadResolution( MultiExprArg Sema::getInitsWithEmbedExpanded(InitListExpr *ILE, SmallVectorImpl<Expr *> &Storage) { - if (llvm::none_of(ILE->inits(), [](const Expr *Init) { - return isa<EmbedExpr>(Init->IgnoreParenImpCasts()); - })) + 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()) { - auto *Embed = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts()); + EmbedExpr *Embed = AsMultiElementEmbed(Init); if (!Embed) { Storage.push_back(Init); continue; } - const StringLiteral *Data = Embed->getDataStringLiteral(); - QualType ElemTy = Embed->getType(); - unsigned Width = Context.getIntWidth(ElemTy); for (unsigned I = Embed->getStartingElementPos(), E = I + Embed->getDataElementCount(); I != E; ++I) - Storage.push_back(IntegerLiteral::Create( - Context, llvm::APInt(Width, Data->getCodeUnit(I)), ElemTy, - Embed->getLocation())); + Storage.push_back(new (Context) EmbedExpr( + Context, Embed->getLocation(), Embed->getData(), I, 1)); } return Storage; } @@ -4977,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->getNumInitsWithEmbedExpanded() == 1) { + if (InitList->hasOneInit()) { Expr *Initializer = InitList->getInit(0); QualType cv2T2 = S.getCompletedType(Initializer); Qualifiers T2Quals; @@ -5068,12 +5066,11 @@ static void TryListInitialization(Sema &S, return; } - unsigned NumInits = InitList->getNumInitsWithEmbedExpanded(); - // 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() && NumInits > 1) { + !DestType->isAnyComplexType() && !InitList->hasZeroInits() && + !InitList->hasOneInit()) { Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar); return; } @@ -5116,7 +5113,8 @@ 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 && NumInits == 1 && !IsDesignatedInit) { + if (S.getLangOpts().CPlusPlus11 && InitList->hasOneInit() && + !IsDesignatedInit) { if (DestType->isRecordType() && DestType->isAggregateType()) { QualType InitType = InitList->getInit(0)->getType(); if (S.Context.hasSameUnqualifiedType(InitType, DestType) || @@ -5197,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 (NumInits == 0) { + if (InitList->hasZeroInits()) { CXXRecordDecl *RD = DestType->castAsCXXRecordDecl(); if (S.LookupDefaultConstructor(RD)) { TryValueInitialization(S, Entity, Kind, Sequence, InitList); @@ -5221,7 +5219,7 @@ static void TryListInitialization(Sema &S, } if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() && - NumInits == 1) { + InitList->hasOneInit()) { Expr *E = InitList->getInit(0); // - Otherwise, if T is an enumeration with a fixed underlying type, @@ -9071,7 +9069,7 @@ bool InitializationSequence::Diagnose(Sema &S, Expr *OnlyArg; if (Args.size() == 1) { auto *List = dyn_cast<InitListExpr>(Args[0]); - if (List && List->getNumInitsWithEmbedExpanded() == 1) + if (List && List->hasOneInit()) OnlyArg = List->getInit(0); else OnlyArg = Args[0]; @@ -10490,8 +10488,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. - unsigned NumInits = ListInit->getNumInitsWithEmbedExpanded(); - if (!NumInits) { + if (ListInit->hasZeroInits()) { for (NamedDecl *D : Guides) { auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl()); if (FD && FD->getMinRequiredArguments() == 0) { @@ -10499,7 +10496,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( break; } } - } else if (NumInits == 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 diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 08fc31cedb0aa..3a1d6f0565790 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5857,8 +5857,6 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, IsDesignatedInit) return Result; - unsigned NumInits = From->getNumInitsWithEmbedExpanded(); - // Per DR1467 and DR2137: // If the parameter type is an aggregate class X and the initializer list // has a single element of type cv U, where U is X or a class derived from @@ -5869,7 +5867,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 (NumInits == 1 && !IsDesignatedInit) { + if (From->hasOneInit() && !IsDesignatedInit) { if (ToType->isRecordType() && ToType->isAggregateType()) { QualType InitType = From->getInit(0)->getType(); if (S.Context.hasSameUnqualifiedType(InitType, ToType) || @@ -5908,7 +5906,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 = NumInits; + unsigned e = From->getNumInitsWithEmbedExpanded(); ImplicitConversionSequence DfltElt; DfltElt.setBad(BadConversionSequence::no_conversion, QualType(), QualType()); @@ -6048,7 +6046,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 (NumInits == 1 && !IsDesignatedInit) { + if (From->hasOneInit() && !IsDesignatedInit) { Expr *Init = From->getInit(0); QualType T2 = Init->getType(); @@ -6110,7 +6108,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. - if (NumInits == 1 && !isa<InitListExpr>(From->getInit(0))) { + if (From->hasOneInit() && !isa<InitListExpr>(From->getInit(0))) { Result = TryCopyInitialization( S, From->getInit(0), ToType, SuppressUserConversions, InOverloadResolution, AllowObjCWritebackConversion); @@ -6119,7 +6117,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_constexpr.cpp b/clang/test/Preprocessor/embed_constexpr.cpp index 31d5e22587a6f..63369599cfbb8 100644 --- a/clang/test/Preprocessor/embed_constexpr.cpp +++ b/clang/test/Preprocessor/embed_constexpr.cpp @@ -114,8 +114,12 @@ struct X { }; // CHECK: CXXConstructExpr {{.*}} 'const X' 'void (int, int)' list -// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 106 -// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 107 +// 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> }; diff --git a/clang/test/SemaCXX/embed-init-list.cpp b/clang/test/SemaCXX/embed-init-list.cpp index 0ab7e53765cbd..f8e8d027f7b1f 100644 --- a/clang/test/SemaCXX/embed-init-list.cpp +++ b/clang/test/SemaCXX/embed-init-list.cpp @@ -194,19 +194,17 @@ constexpr _Complex double complex = { }; static_assert(__real__ complex == '/' && __imag__ complex == '/', ""); -auto deduced_auto{ +auto deduced_auto{ // expected-error {{excess elements in scalar initializer}} #embed __FILE__ limit(2) - // expected-error@-1 {{initializer for variable 'deduced_auto' with type 'auto' contains multiple expressions}} }; void deduced_auto_expr() { - (void)new auto{ + (void)new auto{ // expected-error {{excess elements in scalar initializer}} #embed __FILE__ limit(2) - // expected-error@-1 {{new expression for type 'auto' contains multiple constructor arguments}} }; - (void)auto{ // expected-warning {{'auto' as a functional-style cast is a C++23 extension}} + (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) - // expected-error@-1 {{initializer for functional-style cast to 'auto' contains multiple expressions}} }; } >From b9780c5504d4084f092cd5d8e8cc3745e2ab2928 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 10:45:25 +0530 Subject: [PATCH 3/4] fix the formatting --- clang/include/clang/AST/Expr.h | 4 ++-- clang/lib/Sema/SemaInit.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index ef442213a70d8..ab98fe216963f 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5404,8 +5404,8 @@ class InitListExpr : public Expr { bool hasOneInit() const { if (InitExprs.size() != 1) return false; - auto *EE = dyn_cast<EmbedExpr>( - cast<Expr>(InitExprs[0])->IgnoreParenImpCasts()); + auto *EE = + dyn_cast<EmbedExpr>(cast<Expr>(InitExprs[0])->IgnoreParenImpCasts()); return !EE || EE->getDataElementCount() == 1; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index da4c18eadcd6e..a02f39b481787 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3657,8 +3657,8 @@ MultiExprArg Sema::getInitsWithEmbedExpanded(InitListExpr *ILE, 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)); + Storage.push_back(new (Context) EmbedExpr(Context, Embed->getLocation(), + Embed->getData(), I, 1)); } return Storage; } >From a93b96643582d89d3b9f83e4c7bc4ebcc5e19a49 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 12:02:53 +0530 Subject: [PATCH 4/4] Check narrowing for every element of an embedded initializer DiagnoseNarrowingInInitList and the C23 constexpr check evaluated a multi-element EmbedExpr as one constant, its first byte, so a later byte that does not fit the element type was never diagnosed. --- clang/lib/Sema/SemaInit.cpp | 23 ++++++++++++++++++----- clang/test/Preprocessor/embed_constexpr.c | 4 ++++ clang/test/SemaCXX/embed-init-list.cpp | 10 ++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a02f39b481787..cacd27f21dd07 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -9949,6 +9949,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, @@ -9985,8 +9999,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. @@ -10069,9 +10083,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) 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/SemaCXX/embed-init-list.cpp b/clang/test/SemaCXX/embed-init-list.cpp index f8e8d027f7b1f..d949afccb219f 100644 --- a/clang/test/SemaCXX/embed-init-list.cpp +++ b/clang/test/SemaCXX/embed-init-list.cpp @@ -214,4 +214,14 @@ void call_scalar_arg() { #embed __FILE__ limit(2) }); } + +_BitInt(7) fits[] = { +#embed __FILE__ limit(3) +}; +_BitInt(7) narrowed[] = { // expected-error@+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@+1 {{constant expression evaluates to 82 which cannot be narrowed to type '_BitInt(7)'}} +#embed __FILE__ limit(4) +}; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
