Skip to content

Commit ef42900

Browse files
committed
[Clang] [SemaCXX] Diagnose unknown std::initializer_list layout in SemaInit
1 parent b6fd6d4 commit ef42900

29 files changed

+167
-103
lines changed

clang-tools-extra/clangd/unittests/HoverTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2284,7 +2284,7 @@ TEST(Hover, All) {
22842284
namespace std
22852285
{
22862286
template<class _E>
2287-
class initializer_list {};
2287+
class initializer_list { const _E *a, *b; };
22882288
}
22892289
void foo() {
22902290
^[[auto]] i = {1,2};

clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ TEST(ParameterHints, ConstructorStdInitList) {
945945
// Do not show hints for std::initializer_list constructors.
946946
assertParameterHints(R"cpp(
947947
namespace std {
948-
template <typename> class initializer_list {};
948+
template <typename E> class initializer_list { const E *a, *b; };
949949
}
950950
struct S {
951951
S(std::initializer_list<int> param);

clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ T max(T a, T b) {
1111
namespace std {
1212
template< class T >
1313
struct initializer_list {
14+
const T *a, *b;
1415
initializer_list()=default;
1516
initializer_list(T*,int){}
1617
const T* begin() const {return nullptr;}

clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
// RUN: true}}"
55

66
namespace std {
7-
template <typename>
7+
template <typename E>
88
class initializer_list
99
{
1010
public:
11+
const E *a, *b;
1112
initializer_list() noexcept {}
1213
};
1314

clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
// RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}"
99

1010
namespace std {
11-
template <typename>
11+
template <typename E>
1212
class initializer_list {
1313
public:
14+
const E *a, *b;
1415
initializer_list() noexcept {}
1516
};
1617

clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace std {
77

8-
typedef int size_t;
8+
typedef decltype(sizeof 0) size_t;
99

1010
template<class E> class initializer_list {
1111
public:
@@ -15,6 +15,8 @@ template<class E> class initializer_list {
1515
using size_type = size_t;
1616
using iterator = const E*;
1717
using const_iterator = const E*;
18+
iterator ptr;
19+
size_type sz;
1820
initializer_list();
1921
size_t size() const; // number of elements
2022
const E* begin() const; // first element

clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ struct SomeClass {
3131

3232
namespace std {
3333
template <typename T>
34-
class initializer_list {};
34+
class initializer_list { const T *a, *b; };
3535

3636
template <typename T>
3737
class vector {

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12216,6 +12216,9 @@ def err_std_source_location_impl_not_found : Error<
1221612216
def err_std_source_location_impl_malformed : Error<
1221712217
"'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">;
1221812218

12219+
def err_std_initializer_list_malformed : Error<
12220+
"%0 layout not recognized. Must be a struct with two fields, a 'const E *' and either another 'const E *' or a 'std::size_t'">;
12221+
1221912222
// HLSL Diagnostics
1222012223
def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">;
1222112224
def err_hlsl_attr_invalid_type : Error<

clang/lib/AST/ExprConstant.cpp

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10528,48 +10528,37 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr(
1052810528
// Get a pointer to the first element of the array.
1052910529
Array.addArray(Info, E, ArrayType);
1053010530

10531-
auto InvalidType = [&] {
10532-
Info.FFDiag(E, diag::note_constexpr_unsupported_layout)
10533-
<< E->getType();
10534-
return false;
10535-
};
10536-
10537-
// FIXME: Perform the checks on the field types in SemaInit.
10538-
RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
10539-
RecordDecl::field_iterator Field = Record->field_begin();
10540-
if (Field == Record->field_end())
10541-
return InvalidType();
10542-
10543-
// Start pointer.
10544-
if (!Field->getType()->isPointerType() ||
10545-
!Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
10546-
ArrayType->getElementType()))
10547-
return InvalidType();
10548-
1054910531
// FIXME: What if the initializer_list type has base classes, etc?
1055010532
Result = APValue(APValue::UninitStruct(), 0, 2);
1055110533
Array.moveInto(Result.getStructField(0));
1055210534

10553-
if (++Field == Record->field_end())
10554-
return InvalidType();
10555-
10556-
if (Field->getType()->isPointerType() &&
10557-
Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
10558-
ArrayType->getElementType())) {
10535+
RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
10536+
RecordDecl::field_iterator Field = Record->field_begin();
10537+
assert(Field != Record->field_end() &&
10538+
Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
10539+
ArrayType->getElementType()) &&
10540+
"Expected std::initializer_list first field to be const E *");
10541+
++Field;
10542+
assert(Field != Record->field_end() &&
10543+
"Expected std::initializer_list to have two fields");
10544+
10545+
if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType())) {
10546+
// Length.
10547+
Result.getStructField(1) = APValue(APSInt(ArrayType->getSize()));
10548+
} else {
1055910549
// End pointer.
10550+
assert(Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
10551+
ArrayType->getElementType()) &&
10552+
"Expected std::initializer_list second field to be const E *");
1056010553
if (!HandleLValueArrayAdjustment(Info, E, Array,
1056110554
ArrayType->getElementType(),
1056210555
ArrayType->getZExtSize()))
1056310556
return false;
1056410557
Array.moveInto(Result.getStructField(1));
10565-
} else if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType()))
10566-
// Length.
10567-
Result.getStructField(1) = APValue(APSInt(ArrayType->getSize()));
10568-
else
10569-
return InvalidType();
10558+
}
1057010559

10571-
if (++Field != Record->field_end())
10572-
return InvalidType();
10560+
assert(++Field == Record->field_end() &&
10561+
"Expected std::initializer_list to only have two fields");
1057310562

1057410563
return true;
1057510564
}

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -429,53 +429,45 @@ AggExprEmitter::VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) {
429429
Ctx.getAsConstantArrayType(E->getSubExpr()->getType());
430430
assert(ArrayType && "std::initializer_list constructed from non-array");
431431

432-
// FIXME: Perform the checks on the field types in SemaInit.
433432
RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
434433
RecordDecl::field_iterator Field = Record->field_begin();
435-
if (Field == Record->field_end()) {
436-
CGF.ErrorUnsupported(E, "weird std::initializer_list");
437-
return;
438-
}
434+
assert(Field != Record->field_end() &&
435+
Ctx.hasSameType(Field->getType()->getPointeeType(),
436+
ArrayType->getElementType()) &&
437+
"Expected std::initializer_list first field to be const E *");
439438

440439
// Start pointer.
441-
if (!Field->getType()->isPointerType() ||
442-
!Ctx.hasSameType(Field->getType()->getPointeeType(),
443-
ArrayType->getElementType())) {
444-
CGF.ErrorUnsupported(E, "weird std::initializer_list");
445-
return;
446-
}
447-
448440
AggValueSlot Dest = EnsureSlot(E->getType());
449441
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
450442
LValue Start = CGF.EmitLValueForFieldInitialization(DestLV, *Field);
451443
llvm::Value *ArrayStart = ArrayPtr.emitRawPointer(CGF);
452444
CGF.EmitStoreThroughLValue(RValue::get(ArrayStart), Start);
453445
++Field;
454-
455-
if (Field == Record->field_end()) {
456-
CGF.ErrorUnsupported(E, "weird std::initializer_list");
457-
return;
458-
}
446+
assert(Field != Record->field_end() &&
447+
"Expected std::initializer_list to have two fields");
459448

460449
llvm::Value *Size = Builder.getInt(ArrayType->getSize());
461450
LValue EndOrLength = CGF.EmitLValueForFieldInitialization(DestLV, *Field);
462-
if (Field->getType()->isPointerType() &&
463-
Ctx.hasSameType(Field->getType()->getPointeeType(),
464-
ArrayType->getElementType())) {
451+
if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) {
452+
// Length.
453+
CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength);
454+
455+
} else {
465456
// End pointer.
457+
assert(Field->getType()->isPointerType() &&
458+
Ctx.hasSameType(Field->getType()->getPointeeType(),
459+
ArrayType->getElementType()) &&
460+
"Expected std::initializer_list second field to be const E *");
466461
llvm::Value *Zero = llvm::ConstantInt::get(CGF.PtrDiffTy, 0);
467462
llvm::Value *IdxEnd[] = { Zero, Size };
468463
llvm::Value *ArrayEnd = Builder.CreateInBoundsGEP(
469464
ArrayPtr.getElementType(), ArrayPtr.emitRawPointer(CGF), IdxEnd,
470465
"arrayend");
471466
CGF.EmitStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength);
472-
} else if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) {
473-
// Length.
474-
CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength);
475-
} else {
476-
CGF.ErrorUnsupported(E, "weird std::initializer_list");
477-
return;
478467
}
468+
469+
assert(++Field == Record->field_end() &&
470+
"Expected std::initializer_list to only have two fields");
479471
}
480472

481473
/// Determine if E is a trivial array filler, that is, one that is

clang/lib/Sema/SemaInit.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9392,6 +9392,57 @@ ExprResult InitializationSequence::Perform(Sema &S,
93929392
// Wrap it in a construction of a std::initializer_list<T>.
93939393
CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE);
93949394

9395+
if (!Step->Type->isDependentType()) {
9396+
assert(S.isCompleteType(CurInit.get()->getExprLoc(), Step->Type,
9397+
Sema::CompleteTypeKind::Normal) &&
9398+
"std::initializer_list<E> incomplete when used during "
9399+
"initialization");
9400+
QualType ElementType;
9401+
[[maybe_unused]] bool IsStdInitializerList =
9402+
S.isStdInitializerList(Step->Type, &ElementType);
9403+
assert(IsStdInitializerList &&
9404+
"StdInitializerList step to non-std::initializer_list");
9405+
RecordDecl *Record = Step->Type->castAs<RecordType>()->getDecl();
9406+
9407+
auto InvalidType = [&] {
9408+
S.Diag(Record->getLocation(),
9409+
diag::err_std_initializer_list_malformed)
9410+
<< Step->Type.getUnqualifiedType();
9411+
return ExprError();
9412+
};
9413+
9414+
// FIXME: What if the initializer_list type has base classes, etc?
9415+
if (Record->isUnion())
9416+
return InvalidType();
9417+
9418+
RecordDecl::field_iterator Field = Record->field_begin();
9419+
if (Field == Record->field_end())
9420+
return InvalidType();
9421+
9422+
// Start pointer
9423+
if (!Field->getType()->isPointerType() ||
9424+
!S.Context.hasSameType(Field->getType()->getPointeeType(),
9425+
ElementType.withConst()))
9426+
return InvalidType();
9427+
9428+
if (++Field == Record->field_end())
9429+
return InvalidType();
9430+
9431+
// Size or end pointer
9432+
if (Field->getType()->isPointerType()) {
9433+
if (!S.Context.hasSameType(Field->getType()->getPointeeType(),
9434+
ElementType.withConst()))
9435+
return InvalidType();
9436+
} else {
9437+
if (Field->isUnnamedBitField() ||
9438+
!S.Context.hasSameType(Field->getType(), S.Context.getSizeType()))
9439+
return InvalidType();
9440+
}
9441+
9442+
if (++Field != Record->field_end())
9443+
return InvalidType();
9444+
}
9445+
93959446
// Bind the result, in case the library has given initializer_list a
93969447
// non-trivial destructor.
93979448
if (shouldBindAsTemporary(Entity))

clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ struct S {
4343
const int S::b;
4444
const auto S::c = 0;
4545

46-
namespace std { template<typename T> struct initializer_list { initializer_list(); }; }
46+
namespace std { template<typename T> struct initializer_list { const T *a, *b; initializer_list(); }; }
4747

4848
// In an initializer of the form ( expression-list ), the expression-list
4949
// shall be a single assigment-expression.

clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace std {
66
template<typename T> struct initializer_list {
7-
const T *p;
8-
unsigned long n;
9-
initializer_list(const T *p, unsigned long n);
7+
const T *a, *b;
108
};
119
}
1210

clang/test/CodeCompletion/ctor-signature.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void foo() {
1717
}
1818

1919
namespace std {
20-
template <typename> struct initializer_list {};
20+
template <typename E> struct initializer_list { const E *a, *b; };
2121
} // namespace std
2222

2323
struct Bar {

clang/test/Coverage/unresolved-ctor-expr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// GH62105 demonstrated a crash with this example code when calculating
55
// coverage mapping because some source location information was being dropped.
66
// Demonstrate that we do not crash on this code.
7-
namespace std { template <typename> class initializer_list {}; }
7+
namespace std { template <typename E> class initializer_list { const E *a, *b; }; }
88

99
template <typename> struct T {
1010
T(std::initializer_list<int>, int = int());

clang/test/Modules/Inputs/initializer_list/direct.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace std {
22
using size_t = decltype(sizeof(0));
33

44
template<typename T> struct initializer_list {
5-
initializer_list(T*, size_t);
5+
const T* ptr; size_t sz;
66
};
77

88
template<typename T> int min(initializer_list<T>);

clang/test/Modules/pr60775.cppm

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@
2929
namespace std {
3030
typedef decltype(sizeof(int)) size_t;
3131
template<typename T> struct initializer_list {
32+
const T* ptr; size_t sz;
3233
initializer_list(const T *, size_t);
33-
T* begin();
34-
T* end();
34+
const T* begin();
35+
const T* end();
3536
};
3637
}
3738

clang/test/OpenMP/declare_reduction_codegen_in_templates.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#ifndef HEADER
1717
#define HEADER
1818

19-
typedef long unsigned a;
19+
typedef decltype(sizeof 0) a;
2020
namespace std {
2121
template <class> class initializer_list {
2222
const int *b;

clang/test/Preprocessor/macro_with_initializer_list.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace std {
44
template <class X>
55
class initializer_list {
6-
public:
6+
public: const X *a, *b;
77
initializer_list();
88
};
99
}
Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s
2-
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98
3-
// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s -fexperimental-new-constant-interpreter
4-
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98 -fexperimental-new-constant-interpreter
1+
// RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s
2+
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98
3+
// RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s -fexperimental-new-constant-interpreter
4+
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98 -fexperimental-new-constant-interpreter
55

66

77
namespace std {
88
template <class _E>
99
class initializer_list
1010
{};
11+
// cxx11-error@-2 {{'std::initializer_list<int>' layout not recognized}}
1112
}
1213

1314
template<class E> int f(std::initializer_list<E> il);
1415

1516

1617
int F = f({1, 2, 3});
17-
#ifdef CPP98
18-
//expected-error@-2{{expected expression}}
19-
#else
20-
//expected-error@-4{{cannot compile}}
21-
#endif
22-
23-
18+
// cxx98-error@-1 {{expected expression}}

0 commit comments

Comments
 (0)