[Clang] [Sema] Diagnose unknown std::initializer_list layout in SemaInit (#95580)

This checks if the layout of `std::initializer_list` is something Clang
can handle much earlier and deduplicates the checks in
CodeGen/CGExprAgg.cpp and AST/ExprConstant.cpp

Also now diagnose `union initializer_list` (Fixes #95495), bit-field for
the size (Fixes a crash that would happen during codegen if it were
unnamed), base classes (that wouldn't be initialized) and polymorphic
classes (whose vtable pointer wouldn't be initialized).
This commit is contained in:
Mital Ashok 2024-06-20 18:44:06 +01:00 committed by GitHub
parent 6cea40400d
commit 482c41e992
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 194 additions and 107 deletions

View File

@ -80,7 +80,7 @@ TEST(GetDeducedType, KwAutoKwDecltypeExpansion) {
namespace std namespace std
{ {
template<class _E> template<class _E>
class [[initializer_list]] {}; class [[initializer_list]] { const _E *a, *b; };
} }
^auto i = {1,2}; ^auto i = {1,2};

View File

@ -2284,7 +2284,7 @@ TEST(Hover, All) {
namespace std namespace std
{ {
template<class _E> template<class _E>
class initializer_list {}; class initializer_list { const _E *a, *b; };
} }
void foo() { void foo() {
^[[auto]] i = {1,2}; ^[[auto]] i = {1,2};

View File

@ -945,7 +945,7 @@ TEST(ParameterHints, ConstructorStdInitList) {
// Do not show hints for std::initializer_list constructors. // Do not show hints for std::initializer_list constructors.
assertParameterHints(R"cpp( assertParameterHints(R"cpp(
namespace std { namespace std {
template <typename> class initializer_list {}; template <typename E> class initializer_list { const E *a, *b; };
} }
struct S { struct S {
S(std::initializer_list<int> param); S(std::initializer_list<int> param);

View File

@ -771,7 +771,7 @@ TEST(LocateSymbol, All) {
namespace std namespace std
{ {
template<class _E> template<class _E>
class [[initializer_list]] {}; class [[initializer_list]] { const _E *a, *b; };
} }
^auto i = {1,2}; ^auto i = {1,2};

View File

@ -534,7 +534,7 @@ TEST(WalkAST, Enums) {
TEST(WalkAST, InitializerList) { TEST(WalkAST, InitializerList) {
testWalk(R"cpp( testWalk(R"cpp(
namespace std { namespace std {
template <typename T> struct $implicit^initializer_list {}; template <typename T> struct $implicit^initializer_list { const T *a, *b; };
})cpp", })cpp",
R"cpp( R"cpp(
const char* s = ""; const char* s = "";

View File

@ -11,6 +11,7 @@ T max(T a, T b) {
namespace std { namespace std {
template< class T > template< class T >
struct initializer_list { struct initializer_list {
const T *a, *b;
initializer_list()=default; initializer_list()=default;
initializer_list(T*,int){} initializer_list(T*,int){}
const T* begin() const {return nullptr;} const T* begin() const {return nullptr;}

View File

@ -4,10 +4,11 @@
// RUN: true}}" // RUN: true}}"
namespace std { namespace std {
template <typename> template <typename E>
class initializer_list class initializer_list
{ {
public: public:
const E *a, *b;
initializer_list() noexcept {} initializer_list() noexcept {}
}; };

View File

@ -8,9 +8,10 @@
// RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}" // RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}"
namespace std { namespace std {
template <typename> template <typename E>
class initializer_list { class initializer_list {
public: public:
const E *a, *b;
initializer_list() noexcept {} initializer_list() noexcept {}
}; };

View File

@ -5,7 +5,7 @@
namespace std { namespace std {
typedef int size_t; typedef decltype(sizeof 0) size_t;
template<class E> class initializer_list { template<class E> class initializer_list {
public: public:
@ -15,6 +15,8 @@ public:
using size_type = size_t; using size_type = size_t;
using iterator = const E*; using iterator = const E*;
using const_iterator = const E*; using const_iterator = const E*;
iterator p;
size_t sz;
initializer_list(); initializer_list();
size_t size() const; // number of elements size_t size() const; // number of elements
const E* begin() const; // first element const E* begin() const; // first element

View File

@ -31,7 +31,7 @@ struct SomeClass {
namespace std { namespace std {
template <typename T> template <typename T>
class initializer_list {}; class initializer_list { const T *a, *b; };
template <typename T> template <typename T>
class vector { class vector {

View File

@ -598,6 +598,10 @@ Improvements to Clang's diagnostics
- Clang no longer emits a "declared here" note for a builtin function that has no declaration in source. - Clang no longer emits a "declared here" note for a builtin function that has no declaration in source.
Fixes #GH93369. Fixes #GH93369.
- Clang now diagnoses unsupported class declarations for ``std::initializer_list<E>`` when they are
used rather than when they are needed for constant evaluation or when code is generated for them.
The check is now stricter to prevent crashes for some unsupported declarations (Fixes #GH95495).
Improvements to Clang's time-trace Improvements to Clang's time-trace
---------------------------------- ----------------------------------

View File

@ -12207,6 +12207,9 @@ def err_std_source_location_impl_not_found : Error<
def err_std_source_location_impl_malformed : Error< def err_std_source_location_impl_malformed : Error<
"'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'">; "'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'">;
def err_std_initializer_list_malformed : Error<
"%0 layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'">;
// HLSL Diagnostics // HLSL Diagnostics
def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">; def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">;
def err_hlsl_attr_invalid_type : Error< def err_hlsl_attr_invalid_type : Error<

View File

@ -10545,48 +10545,37 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr(
// Get a pointer to the first element of the array. // Get a pointer to the first element of the array.
Array.addArray(Info, E, ArrayType); Array.addArray(Info, E, ArrayType);
auto InvalidType = [&] {
Info.FFDiag(E, diag::note_constexpr_unsupported_layout)
<< E->getType();
return false;
};
// FIXME: Perform the checks on the field types in SemaInit.
RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
RecordDecl::field_iterator Field = Record->field_begin();
if (Field == Record->field_end())
return InvalidType();
// Start pointer.
if (!Field->getType()->isPointerType() ||
!Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType()))
return InvalidType();
// FIXME: What if the initializer_list type has base classes, etc? // FIXME: What if the initializer_list type has base classes, etc?
Result = APValue(APValue::UninitStruct(), 0, 2); Result = APValue(APValue::UninitStruct(), 0, 2);
Array.moveInto(Result.getStructField(0)); Array.moveInto(Result.getStructField(0));
if (++Field == Record->field_end()) RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
return InvalidType(); RecordDecl::field_iterator Field = Record->field_begin();
assert(Field != Record->field_end() &&
if (Field->getType()->isPointerType() &&
Info.Ctx.hasSameType(Field->getType()->getPointeeType(), Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType())) { ArrayType->getElementType()) &&
"Expected std::initializer_list first field to be const E *");
++Field;
assert(Field != Record->field_end() &&
"Expected std::initializer_list to have two fields");
if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType())) {
// Length.
Result.getStructField(1) = APValue(APSInt(ArrayType->getSize()));
} else {
// End pointer. // End pointer.
assert(Info.Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType()) &&
"Expected std::initializer_list second field to be const E *");
if (!HandleLValueArrayAdjustment(Info, E, Array, if (!HandleLValueArrayAdjustment(Info, E, Array,
ArrayType->getElementType(), ArrayType->getElementType(),
ArrayType->getZExtSize())) ArrayType->getZExtSize()))
return false; return false;
Array.moveInto(Result.getStructField(1)); Array.moveInto(Result.getStructField(1));
} else if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType())) }
// Length.
Result.getStructField(1) = APValue(APSInt(ArrayType->getSize()));
else
return InvalidType();
if (++Field != Record->field_end()) assert(++Field == Record->field_end() &&
return InvalidType(); "Expected std::initializer_list to only have two fields");
return true; return true;
} }

View File

@ -426,53 +426,45 @@ AggExprEmitter::VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) {
Ctx.getAsConstantArrayType(E->getSubExpr()->getType()); Ctx.getAsConstantArrayType(E->getSubExpr()->getType());
assert(ArrayType && "std::initializer_list constructed from non-array"); assert(ArrayType && "std::initializer_list constructed from non-array");
// FIXME: Perform the checks on the field types in SemaInit.
RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl(); RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl();
RecordDecl::field_iterator Field = Record->field_begin(); RecordDecl::field_iterator Field = Record->field_begin();
if (Field == Record->field_end()) { assert(Field != Record->field_end() &&
CGF.ErrorUnsupported(E, "weird std::initializer_list"); Ctx.hasSameType(Field->getType()->getPointeeType(),
return; ArrayType->getElementType()) &&
} "Expected std::initializer_list first field to be const E *");
// Start pointer. // Start pointer.
if (!Field->getType()->isPointerType() ||
!Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType())) {
CGF.ErrorUnsupported(E, "weird std::initializer_list");
return;
}
AggValueSlot Dest = EnsureSlot(E->getType()); AggValueSlot Dest = EnsureSlot(E->getType());
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
LValue Start = CGF.EmitLValueForFieldInitialization(DestLV, *Field); LValue Start = CGF.EmitLValueForFieldInitialization(DestLV, *Field);
llvm::Value *ArrayStart = ArrayPtr.emitRawPointer(CGF); llvm::Value *ArrayStart = ArrayPtr.emitRawPointer(CGF);
CGF.EmitStoreThroughLValue(RValue::get(ArrayStart), Start); CGF.EmitStoreThroughLValue(RValue::get(ArrayStart), Start);
++Field; ++Field;
assert(Field != Record->field_end() &&
if (Field == Record->field_end()) { "Expected std::initializer_list to have two fields");
CGF.ErrorUnsupported(E, "weird std::initializer_list");
return;
}
llvm::Value *Size = Builder.getInt(ArrayType->getSize()); llvm::Value *Size = Builder.getInt(ArrayType->getSize());
LValue EndOrLength = CGF.EmitLValueForFieldInitialization(DestLV, *Field); LValue EndOrLength = CGF.EmitLValueForFieldInitialization(DestLV, *Field);
if (Field->getType()->isPointerType() && if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) {
Ctx.hasSameType(Field->getType()->getPointeeType(), // Length.
ArrayType->getElementType())) { CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength);
} else {
// End pointer. // End pointer.
assert(Field->getType()->isPointerType() &&
Ctx.hasSameType(Field->getType()->getPointeeType(),
ArrayType->getElementType()) &&
"Expected std::initializer_list second field to be const E *");
llvm::Value *Zero = llvm::ConstantInt::get(CGF.PtrDiffTy, 0); llvm::Value *Zero = llvm::ConstantInt::get(CGF.PtrDiffTy, 0);
llvm::Value *IdxEnd[] = { Zero, Size }; llvm::Value *IdxEnd[] = { Zero, Size };
llvm::Value *ArrayEnd = Builder.CreateInBoundsGEP( llvm::Value *ArrayEnd = Builder.CreateInBoundsGEP(
ArrayPtr.getElementType(), ArrayPtr.emitRawPointer(CGF), IdxEnd, ArrayPtr.getElementType(), ArrayPtr.emitRawPointer(CGF), IdxEnd,
"arrayend"); "arrayend");
CGF.EmitStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength); CGF.EmitStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength);
} else if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) {
// Length.
CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength);
} else {
CGF.ErrorUnsupported(E, "weird std::initializer_list");
return;
} }
assert(++Field == Record->field_end() &&
"Expected std::initializer_list to only have two fields");
} }
/// Determine if E is a trivial array filler, that is, one that is /// Determine if E is a trivial array filler, that is, one that is

View File

@ -9479,6 +9479,57 @@ ExprResult InitializationSequence::Perform(Sema &S,
// Wrap it in a construction of a std::initializer_list<T>. // Wrap it in a construction of a std::initializer_list<T>.
CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE); CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE);
if (!Step->Type->isDependentType()) {
QualType ElementType;
[[maybe_unused]] bool IsStdInitializerList =
S.isStdInitializerList(Step->Type, &ElementType);
assert(IsStdInitializerList &&
"StdInitializerList step to non-std::initializer_list");
const CXXRecordDecl *Record =
Step->Type->getAsCXXRecordDecl()->getDefinition();
assert(Record && Record->isCompleteDefinition() &&
"std::initializer_list should have already be "
"complete/instantiated by this point");
auto InvalidType = [&] {
S.Diag(Record->getLocation(),
diag::err_std_initializer_list_malformed)
<< Step->Type.getUnqualifiedType();
return ExprError();
};
if (Record->isUnion() || Record->getNumBases() != 0 ||
Record->isPolymorphic())
return InvalidType();
RecordDecl::field_iterator Field = Record->field_begin();
if (Field == Record->field_end())
return InvalidType();
// Start pointer
if (!Field->getType()->isPointerType() ||
!S.Context.hasSameType(Field->getType()->getPointeeType(),
ElementType.withConst()))
return InvalidType();
if (++Field == Record->field_end())
return InvalidType();
// Size or end pointer
if (const auto *PT = Field->getType()->getAs<PointerType>()) {
if (!S.Context.hasSameType(PT->getPointeeType(),
ElementType.withConst()))
return InvalidType();
} else {
if (Field->isBitField() ||
!S.Context.hasSameType(Field->getType(), S.Context.getSizeType()))
return InvalidType();
}
if (++Field != Record->field_end())
return InvalidType();
}
// Bind the result, in case the library has given initializer_list a // Bind the result, in case the library has given initializer_list a
// non-trivial destructor. // non-trivial destructor.
if (shouldBindAsTemporary(Entity)) if (shouldBindAsTemporary(Entity))

View File

@ -43,7 +43,7 @@ struct S {
const int S::b; const int S::b;
const auto S::c = 0; const auto S::c = 0;
namespace std { template<typename T> struct initializer_list { initializer_list(); }; } namespace std { template<typename T> struct initializer_list { const T *a, *b; initializer_list(); }; }
// In an initializer of the form ( expression-list ), the expression-list // In an initializer of the form ( expression-list ), the expression-list
// shall be a single assigment-expression. // shall be a single assigment-expression.

View File

@ -4,9 +4,7 @@
namespace std { namespace std {
template<typename T> struct initializer_list { template<typename T> struct initializer_list {
const T *p; const T *a, *b;
unsigned long n;
initializer_list(const T *p, unsigned long n);
}; };
} }

View File

@ -17,7 +17,7 @@ void foo() {
} }
namespace std { namespace std {
template <typename> struct initializer_list {}; template <typename E> struct initializer_list { const E *a, *b; };
} // namespace std } // namespace std
struct Bar { struct Bar {

View File

@ -4,7 +4,7 @@
// GH62105 demonstrated a crash with this example code when calculating // GH62105 demonstrated a crash with this example code when calculating
// coverage mapping because some source location information was being dropped. // coverage mapping because some source location information was being dropped.
// Demonstrate that we do not crash on this code. // Demonstrate that we do not crash on this code.
namespace std { template <typename> class initializer_list {}; } namespace std { template <typename E> class initializer_list { const E *a, *b; }; }
template <typename> struct T { template <typename> struct T {
T(std::initializer_list<int>, int = int()); T(std::initializer_list<int>, int = int());

View File

@ -2,7 +2,7 @@ namespace std {
using size_t = decltype(sizeof(0)); using size_t = decltype(sizeof(0));
template<typename T> struct initializer_list { template<typename T> struct initializer_list {
initializer_list(T*, size_t); const T* ptr; size_t sz;
}; };
template<typename T> int min(initializer_list<T>); template<typename T> int min(initializer_list<T>);

View File

@ -29,9 +29,10 @@
namespace std { namespace std {
typedef decltype(sizeof(int)) size_t; typedef decltype(sizeof(int)) size_t;
template<typename T> struct initializer_list { template<typename T> struct initializer_list {
const T* ptr; size_t sz;
initializer_list(const T *, size_t); initializer_list(const T *, size_t);
T* begin(); const T* begin();
T* end(); const T* end();
}; };
} }

View File

@ -16,7 +16,7 @@
#ifndef HEADER #ifndef HEADER
#define HEADER #define HEADER
typedef long unsigned a; typedef decltype(sizeof 0) a;
namespace std { namespace std {
template <class> class initializer_list { template <class> class initializer_list {
const int *b; const int *b;

View File

@ -3,7 +3,7 @@
namespace std { namespace std {
template <class X> template <class X>
class initializer_list { class initializer_list {
public: public: const X *a, *b;
initializer_list(); initializer_list();
}; };
} }

View File

@ -1,23 +1,17 @@
// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s // RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98 // RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s
// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98 -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -fexperimental-new-constant-interpreter
namespace std { namespace std {
template <class _E> template <class _E>
class initializer_list class initializer_list {};
{}; // cxx11-error@-1 {{'std::initializer_list<int>' layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'}}
} }
template<class E> int f(std::initializer_list<E> il); template<class E> int f(std::initializer_list<E> il);
int F = f({1, 2, 3}); int F = f({1, 2, 3});
#ifdef CPP98 // cxx98-error@-1 {{expected expression}}
//expected-error@-2{{expected expression}}
#else
//expected-error@-4{{cannot compile}}
#endif

View File

@ -1,8 +1,8 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fno-recovery-ast -verify %s // RUN: %clang_cc1 -std=c++11 -fsyntax-only -fno-recovery-ast -verify %s
namespace std { namespace std {
template <typename> template <typename E>
class initializer_list{}; class initializer_list { const E *a, *b; };
int a; int a;
auto c = a, &d = {a}; // expected-error {{'auto' deduced as 'int'}} \ auto c = a, &d = {a}; // expected-error {{'auto' deduced as 'int'}} \
expected-error {{non-const lvalue reference to type}} expected-error {{non-const lvalue reference to type}}

View File

@ -1,5 +1,18 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -DUNION_TEST -verify %s
#ifdef UNION_TEST
namespace std {
template<class E>
union initializer_list {
// expected-error@-1 {{'std::initializer_list<int>' layout not recognized.}}
const E* begin;
decltype(sizeof 0) size;
};
auto x = { 1, 2, 3 };
};
#else
// This must obviously come before the definition of std::initializer_list. // This must obviously come before the definition of std::initializer_list.
void missing_initializerlist() { void missing_initializerlist() {
auto l = {1, 2, 3, 4}; // expected-error {{std::initializer_list was not found}} auto l = {1, 2, 3, 4}; // expected-error {{std::initializer_list was not found}}
@ -367,14 +380,49 @@ namespace designated_init {
} }
namespace weird_initlist { namespace weird_initlist {
template<int>
struct weird {}; struct weird {};
} }
template<> struct std::initializer_list<weird_initlist::weird> { int a, b, c; }; template<> struct std::initializer_list<weird_initlist::weird<0>> { int a, b, c; };
// expected-error@-1 2 {{'std::initializer_list<weird<0>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<1>> { std::size_t sz; const weird_initlist::weird<1>* p; };
// expected-error@-1 {{'std::initializer_list<weird<1>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<2>> { const weird_initlist::weird<2>* first; const weird_initlist::weird<2>* last; };
template<> struct std::initializer_list<weird_initlist::weird<3>> { weird_initlist::weird<3>* p; std::size_t sz; };
// expected-error@-1 {{'std::initializer_list<weird<3>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<4>> { const weird_initlist::weird<4>& p; std::size_t sz; };
// expected-error@-1 {{'std::initializer_list<weird<4>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<5>> { const weird_initlist::weird<5>* p; std::size_t : sizeof(std::size_t) * __CHAR_BIT__; };
// expected-error@-1 {{'std::initializer_list<weird<5>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<6>> { const weird_initlist::weird<6>* p; std::size_t sz : sizeof(std::size_t) * __CHAR_BIT__; };
// expected-error@-1 {{'std::initializer_list<weird<6>>' layout not recognized}}
struct empty_base {};
template<> struct std::initializer_list<weird_initlist::weird<7>> : empty_base { const weird_initlist::weird<7>* p; std::size_t sz; };
// expected-error@-1 {{'std::initializer_list<weird<7>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<8>> { const weird_initlist::weird<8>* p; std::size_t sz; ~initializer_list(); };
template<> struct std::initializer_list<weird_initlist::weird<9>> { const weird_initlist::weird<9>* p; std::size_t sz; virtual void f(); };
// expected-error@-1 {{'std::initializer_list<weird<9>>' layout not recognized}}
template<> struct std::initializer_list<weird_initlist::weird<10>> { const weird_initlist::weird<10>* p; alignas(64) std::size_t sz; };
template<> struct std::initializer_list<weird_initlist::weird<11>>;
// expected-note@-1 {{forward declaration of 'std::initializer_list<weird_initlist::weird<11>>'}}
namespace weird_initlist { namespace weird_initlist {
// We don't check the struct layout in Sema. auto _0 = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}};
auto x = {weird{}, weird{}, weird{}, weird{}, weird{}}; constexpr auto _0c = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}};
// ... but we do in constant evaluation. auto _1 = {weird<1>{}, weird<1>{}};
constexpr auto y = {weird{}, weird{}, weird{}, weird{}, weird{}}; // expected-error {{constant}} expected-note {{type 'const std::initializer_list<weird>' has unexpected layout}} auto _2 = {weird<2>{}, weird<2>{}, weird<2>{}};
constexpr auto _2c = {weird<2>{}, weird<2>{}, weird<2>{}}; // (Two pointer representation is supported)
static_assert(_2c.first + 3 == _2c.last, "");
auto _3 = {weird<3>{}, weird<3>{}};
auto _4 = {weird<4>{}, weird<4>{}};
auto _5 = {weird<5>{}, weird<5>{}};
auto _6 = {weird<6>{}, weird<6>{}};
auto _7 = {weird<7>{}, weird<7>{}};
auto _8 = {weird<8>{}, weird<8>{}};
auto _9 = {weird<9>{}, weird<9>{}};
auto _10 = {weird<10>{}, weird<10>{}};
constexpr auto _10c = {weird<10>{}, weird<10>{}, weird<10>{}};
static_assert(_10c.sz == 3, "");
const auto& _11 = {weird<11>{}, weird<11>{}}; // expected-error {{initialization of incomplete type 'const std::initializer_list<weird<11>>'}}
} }
auto v = std::initializer_list<int>{1,2,3}; // expected-warning {{array backing local initializer list 'v' will be destroyed at the end of the full-expression}} auto v = std::initializer_list<int>{1,2,3}; // expected-warning {{array backing local initializer list 'v' will be destroyed at the end of the full-expression}}
@ -386,3 +434,4 @@ std::initializer_list<int> get(int cond) {
return {1, 2, 3}; // expected-warning {{returning address of local temporary object}} return {1, 2, 3}; // expected-warning {{returning address of local temporary object}}
return std::initializer_list<int>{1, 2, 3}; // expected-warning {{returning address of local temporary object}} return std::initializer_list<int>{1, 2, 3}; // expected-warning {{returning address of local temporary object}}
} }
#endif // UNION_TEST

View File

@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
namespace std { namespace std {
template<class _Ep> class initializer_list { }; template<class _Ep> class initializer_list { const _Ep *a, *b; };
} }
namespace cva { namespace cva {

View File

@ -6,11 +6,11 @@ namespace std {
struct type_info; struct type_info;
using size_t = decltype(sizeof(0)); // expected-warning {{decltype}} expected-warning {{alias}} using size_t = decltype(sizeof(0)); // expected-warning {{decltype}} expected-warning {{alias}}
template<typename T> struct initializer_list { template<typename T> struct initializer_list {
initializer_list(T*, size_t); initializer_list(const T*, size_t);
T *p; const T *p;
size_t n; size_t n;
T *begin(); const T *begin();
T *end(); const T *end();
}; };
} }

View File

@ -26,7 +26,7 @@ void test2() {
// PR6327 // PR6327
namespace test3 { namespace test3 {
template <class A, class B> struct pair {}; template <class A, class B> struct pair {};
template <class _E> class initializer_list {}; template <class _E> class initializer_list { const _E *a, *b; };
template <typename _Tp> pair<_Tp, _Tp> minmax(initializer_list<_Tp> __l) {}; template <typename _Tp> pair<_Tp, _Tp> minmax(initializer_list<_Tp> __l) {};
void test0() { void test0() {

View File

@ -2,9 +2,9 @@
namespace std { namespace std {
template<typename T> struct initializer_list { template<typename T> struct initializer_list {
T *p; const T *p;
__SIZE_TYPE__ n; __SIZE_TYPE__ n;
initializer_list(T*, __SIZE_TYPE__); initializer_list(const T*, __SIZE_TYPE__);
}; };
} }

View File

@ -1427,8 +1427,8 @@ TEST_P(ASTMatchersTest,
return; return;
} }
StringRef code = "namespace std {" StringRef code = "namespace std {"
"template <typename> class initializer_list {" "template <typename E> class initializer_list {"
" public: initializer_list() noexcept {}" " public: const E *a, *b;"
"};" "};"
"}" "}"
"struct A {" "struct A {"

View File

@ -3265,7 +3265,7 @@ TEST(TransferTest, ResultObjectLocationForStdInitializerListExpr) {
std::string Code = R"( std::string Code = R"(
namespace std { namespace std {
template <typename T> template <typename T>
struct initializer_list {}; struct initializer_list { const T *a, *b; };
} // namespace std } // namespace std
void target() { void target() {

View File

@ -552,6 +552,7 @@ namespace std {
template <typename T> template <typename T>
class initializer_list { class initializer_list {
public: public:
const T *a, *b;
initializer_list() noexcept; initializer_list() noexcept;
}; };