[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:
parent
6cea40400d
commit
482c41e992
@ -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};
|
||||||
|
@ -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};
|
||||||
|
@ -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);
|
||||||
|
@ -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};
|
||||||
|
@ -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 = "";
|
||||||
|
@ -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;}
|
||||||
|
@ -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 {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
@ -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<
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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());
|
||||||
|
@ -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>);
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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}}
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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__);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {"
|
||||||
|
@ -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() {
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user