[clang] Fix issues with #embed and intializer lists/template arguments (#128890)

Sometimes number of expressions in InitListExpr is used for template
argument deduction. So, in these cases we need to pay attention to real
number of expressions including expanded #embed data.

Fixes https://github.com/llvm/llvm-project/issues/122306
This commit is contained in:
Mariya Podchishchaeva 2025-02-28 09:35:59 +01:00 committed by GitHub
parent 15c49b9db3
commit 2871f69052
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 93 additions and 7 deletions

View File

@ -225,6 +225,8 @@ Bug Fixes in This Version
- Clang now outputs correct values when #embed data contains bytes with negative
signed char values (#GH102798).
- Fixed rejects-valid problem when #embed appears in std::initializer_list or
when it can affect template argument deduction (#GH122306).
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -5189,6 +5189,16 @@ public:
unsigned getNumInits() const { return InitExprs.size(); }
/// getNumInits but if the list has an EmbedExpr inside includes full length
/// of embedded data.
unsigned getNumInitsWithEmbedExpanded() const {
unsigned Sum = InitExprs.size();
for (auto *IE : InitExprs)
if (auto *EE = dyn_cast<EmbedExpr>(IE))
Sum += EE->getDataElementCount() - 1;
return Sum;
}
/// Retrieve the set of initializers.
Expr **getInits() { return reinterpret_cast<Expr **>(InitExprs.data()); }

View File

@ -4261,7 +4261,7 @@ static bool TryInitializerListConstruction(Sema &S,
QualType ArrayType = S.Context.getConstantArrayType(
E.withConst(),
llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()),
List->getNumInits()),
List->getNumInitsWithEmbedExpanded()),
nullptr, clang::ArraySizeModifier::Normal, 0);
InitializedEntity HiddenArray =
InitializedEntity::InitializeTemporary(ArrayType);

View File

@ -5710,12 +5710,14 @@ 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)))
Result = TryCopyInitialization(S, From->getInit(0), ToType,
SuppressUserConversions,
InOverloadResolution,
AllowObjCWritebackConversion);
if (NumInits == 1 && !isa<InitListExpr>(From->getInit(0)) &&
!isa<EmbedExpr>(From->getInit(0)))
Result = TryCopyInitialization(
S, From->getInit(0), ToType, SuppressUserConversions,
InOverloadResolution, AllowObjCWritebackConversion);
// - if the initializer list has no elements, the implicit conversion
// sequence is the identity conversion.
else if (NumInits == 0) {

View File

@ -4506,7 +4506,8 @@ static TemplateDeductionResult DeduceFromInitializerList(
// C++ [temp.deduct.type]p13:
// The type of N in the type T[N] is std::size_t.
QualType T = S.Context.getSizeType();
llvm::APInt Size(S.Context.getIntWidth(T), ILE->getNumInits());
llvm::APInt Size(S.Context.getIntWidth(T),
ILE->getNumInitsWithEmbedExpanded());
if (auto Result = DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, llvm::APSInt(Size), T,
/*ArrayBound=*/true, Info, /*PartialOrdering=*/false, Deduced,

View File

@ -0,0 +1,71 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s
// expected-no-diagnostics
namespace std {
typedef decltype(sizeof(int)) size_t;
template <class _E> class initializer_list {
const _E *__begin_;
size_t __size_;
constexpr initializer_list(const _E *__b, size_t __s)
: __begin_(__b), __size_(__s) {}
public:
constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
};
} // namespace std
template <typename T> struct S {
S(std::initializer_list<T>);
};
template <> struct S<char> {
S(std::initializer_list<char>);
};
struct S1 {
S<char> data;
int a;
};
template <typename _Tp, std::size_t _Nm> void to_array(_Tp (&&__a)[_Nm]) {}
template<typename T>
void tfn(T) {}
void tests() {
S<char>{{
#embed __FILE__
}};
S1 ss{std::initializer_list<char>{
#embed __FILE__
}};
S sss = {
#embed __FILE__
};
std::initializer_list<int> il{
#embed __FILE__
};
static constexpr auto initializer_list = std::initializer_list<char>{
#embed __FILE__
, '\0'};
static constexpr auto intinitializer_list = std::initializer_list<int>{
#embed __FILE__
, '\0'};
to_array({
#embed __FILE__
});
tfn<std::initializer_list<int>>({
#embed __FILE__
});
}