зеркало из https://github.com/microsoft/clang-1.git
For C++11, do more checking of initializer lists up-front, enabling some subset of the final functionality. C just leaves the function early. C++98 runs through the same code path, but has no changed functionality either.
This is a first baby step towards supporting generalized initializer lists. This also removes an aggregate test case that was just plain wrong, assuming that non-aggregates couldn't be initialized with initializer lists in C++11 mode. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135177 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
dc998b4445
Коммит
ac420c5053
|
@ -7,9 +7,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements semantic analysis for initializers. The main entry
|
||||
// point is Sema::CheckInitList(), but all of the work is performed
|
||||
// within the InitListChecker class.
|
||||
// This file implements semantic analysis for initializers.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
@ -712,7 +710,7 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
|
|||
} else if (SemaRef.getLangOptions().CPlusPlus) {
|
||||
// C++ [dcl.init.aggr]p12:
|
||||
// All implicit type conversions (clause 4) are considered when
|
||||
// initializing the aggregate member with an ini- tializer from
|
||||
// initializing the aggregate member with an initializer from
|
||||
// an initializer-list. If the initializer can initialize a
|
||||
// member, the member is initialized. [...]
|
||||
|
||||
|
@ -2384,52 +2382,10 @@ static void MaybeProduceObjCObject(Sema &S,
|
|||
}
|
||||
}
|
||||
|
||||
/// \brief Attempt list initialization (C++0x [dcl.init.list])
|
||||
static void TryListInitialization(Sema &S,
|
||||
const InitializedEntity &Entity,
|
||||
const InitializationKind &Kind,
|
||||
InitListExpr *InitList,
|
||||
InitializationSequence &Sequence) {
|
||||
// FIXME: We only perform rudimentary checking of list
|
||||
// initializations at this point, then assume that any list
|
||||
// initialization of an array, aggregate, or scalar will be
|
||||
// well-formed. When we actually "perform" list initialization, we'll
|
||||
// do all of the necessary checking. C++0x initializer lists will
|
||||
// force us to perform more checking here.
|
||||
|
||||
QualType DestType = Entity.getType();
|
||||
|
||||
// C++ [dcl.init]p13:
|
||||
// If T is a scalar type, then a declaration of the form
|
||||
//
|
||||
// T x = { a };
|
||||
//
|
||||
// is equivalent to
|
||||
//
|
||||
// T x = a;
|
||||
if (DestType->isScalarType()) {
|
||||
if (InitList->getNumInits() > 1 && S.getLangOptions().CPlusPlus) {
|
||||
Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar);
|
||||
return;
|
||||
}
|
||||
|
||||
// Assume scalar initialization from a single value works.
|
||||
} else if (DestType->isAggregateType()) {
|
||||
// Assume aggregate initialization works.
|
||||
} else if (DestType->isVectorType()) {
|
||||
// Assume vector initialization works.
|
||||
} else if (DestType->isReferenceType()) {
|
||||
// FIXME: C++0x defines behavior for this.
|
||||
Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList);
|
||||
return;
|
||||
} else if (DestType->isRecordType()) {
|
||||
// FIXME: C++0x defines behavior for this
|
||||
Sequence.SetFailed(InitializationSequence::FK_InitListBadDestinationType);
|
||||
}
|
||||
|
||||
// Add a general "list initialization" step.
|
||||
Sequence.AddListInitializationStep(DestType);
|
||||
}
|
||||
static void SelectInitialization(Sema &S, const InitializedEntity &Entity,
|
||||
const InitializationKind &Kind,
|
||||
Expr **Args, unsigned NumArgs,
|
||||
InitializationSequence &Sequence);
|
||||
|
||||
/// \brief Try a reference initialization that involves calling a conversion
|
||||
/// function.
|
||||
|
@ -3288,6 +3244,185 @@ static void checkIndirectCopyRestoreSource(Sema &S, Expr *src) {
|
|||
<< src->getSourceRange();
|
||||
}
|
||||
|
||||
static bool hasDefaultConstructor(Sema &S, CXXRecordDecl *decl) {
|
||||
DeclContext::lookup_const_iterator Con, ConEnd;
|
||||
for (llvm::tie(Con, ConEnd) = S.LookupConstructors(decl);
|
||||
Con != ConEnd; ++Con) {
|
||||
// FIXME: A constructor template can be a default constructor, but we don't
|
||||
// handle this in other places as well.
|
||||
if (isa<FunctionTemplateDecl>(*Con))
|
||||
continue;
|
||||
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con);
|
||||
if (Constructor->isDefaultConstructor())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Attempt list initialization (C++0x [dcl.init.list])
|
||||
static void TryListInitialization(Sema &S,
|
||||
const InitializedEntity &Entity,
|
||||
const InitializationKind &Kind,
|
||||
InitListExpr *InitList,
|
||||
InitializationSequence &Sequence) {
|
||||
QualType DestType = Entity.getType();
|
||||
|
||||
// If we're not in C++ mode, defer everything to the init list checker.
|
||||
if (!S.getLangOptions().CPlusPlus) {
|
||||
Sequence.AddListInitializationStep(DestType);
|
||||
return;
|
||||
}
|
||||
|
||||
// Early error return for some C++11 features when we're in 98 mode.
|
||||
if (!S.getLangOptions().CPlusPlus0x) {
|
||||
if (DestType->isReferenceType()) {
|
||||
Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList);
|
||||
return;
|
||||
}
|
||||
if (DestType->isRecordType() && !DestType->isAggregateType()) {
|
||||
Sequence.SetFailed(InitializationSequence::FK_InitListBadDestinationType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a reference and not exactly one initializer (see below), unwrap
|
||||
// the reference.
|
||||
bool wasReference = false;
|
||||
if (InitList->getNumInits() != 1) {
|
||||
if (const ReferenceType *ref = DestType->getAs<ReferenceType>()) {
|
||||
wasReference = true;
|
||||
DestType = ref->getPointeeType();
|
||||
}
|
||||
}
|
||||
// Create an object that automatically adds a ref binding step on successful
|
||||
// return.
|
||||
class AddRefBinding {
|
||||
InitializationSequence &Sequence;
|
||||
bool Bind;
|
||||
QualType RefType;
|
||||
public:
|
||||
AddRefBinding(InitializationSequence &sequence, bool bind, QualType refType)
|
||||
: Sequence(sequence), Bind(bind), RefType(refType) {}
|
||||
~AddRefBinding() {
|
||||
if (Bind && Sequence) {
|
||||
Sequence.AddReferenceBindingStep(RefType, /*temporary*/true);
|
||||
}
|
||||
}
|
||||
} addRefBinding(Sequence, wasReference, Entity.getType());
|
||||
|
||||
// C++11 [dcl.init.list]p3:
|
||||
// List-initialization of an object or reference of type T is defined as
|
||||
// follows:
|
||||
//
|
||||
// - If the initializer list has no elements and T is a class type with
|
||||
// a default constructor, the object is value-initialized.
|
||||
//
|
||||
// See DR990. This case is handled specially because if we let it get to
|
||||
// overload resolution, std::initializer_list constructors would be chosen
|
||||
// over the default constructor. When there's more than one initlist ctor,
|
||||
// this would actually be ambiguous and fail.
|
||||
|
||||
const RecordType *recordType = DestType->getAs<RecordType>();
|
||||
CXXRecordDecl *recordDecl = recordType ?
|
||||
dyn_cast<CXXRecordDecl>(recordType->getDecl()) : 0;
|
||||
if (recordDecl && InitList->getNumInits() == 0 &&
|
||||
hasDefaultConstructor(S, recordDecl)) {
|
||||
TryValueInitialization(S, Entity, Kind, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// - Otherwise, if T is an aggregate, aggregate initialization is
|
||||
// performed.
|
||||
//
|
||||
// Aggregate initialization is the most complicated part. We delegate to
|
||||
// an InitListChecker to build a representation of what's happening.
|
||||
// We also treat vector types the same as aggregates.
|
||||
if (DestType->isAggregateType() || DestType->isVectorType()) {
|
||||
// FIXME: Deeper analysis necessary.
|
||||
Sequence.AddListInitializationStep(DestType);
|
||||
return;
|
||||
}
|
||||
|
||||
// - Otherwise, if T is a specialization of std::initializer_list<E>, an
|
||||
// initializer_list object is constructed as described below and used
|
||||
// to initialize the object according to the rules for initialization
|
||||
// of an object from a class of the same type.
|
||||
//
|
||||
// FIXME: Implement this case.
|
||||
|
||||
// - Otherwise, if T is a class type, constructors are considered. The
|
||||
// applicable constructors are enumerated and the best one is chosen
|
||||
// through overload resolution.
|
||||
if (recordDecl) {
|
||||
// FIXME: initializer_list constructors are applicable.
|
||||
TryConstructorInitialization(S, Entity, Kind, InitList->getInits(),
|
||||
InitList->getNumInits(), DestType, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, there is most likely a defect in the standard. The next
|
||||
// bullet grabs all reference targets and creates temporaries from the init
|
||||
// list. However, this means that code such as this doesn't work:
|
||||
// int i;
|
||||
// int &ri { i }; // error: non-const lvalue ref cannot bind to temporary.
|
||||
// This is rather startling, since this code works:
|
||||
// int &si ( i );
|
||||
//
|
||||
// DR934 (CD2 status) tried to address the problem by making the bullet about
|
||||
// references be only about references to class types, letting references to
|
||||
// other things fall through. This means the above works, but this still
|
||||
// doesn't:
|
||||
// string s;
|
||||
// string &rs { s }; // cannot bind to temporary
|
||||
// string &ss ( s ); // fine
|
||||
// And this works, but has different semantics:
|
||||
// const string &cs { s }; // binds to temporary copy
|
||||
// const string &ds ( s ); // binds directly to s
|
||||
// Also, the wording change from that DR somehow got lost in the FDIS.
|
||||
//
|
||||
// DR1095 (FDIS status) again discovered the problem, but didn't actually
|
||||
// fix it.
|
||||
//
|
||||
// GCC implements it this way. We swap the next two bullets instead, thus
|
||||
// always letting a reference bind to the single element of an initializer
|
||||
// list, and constructing a temporary only if the isn't exactly one element.
|
||||
// So in our order, the next bullet is:
|
||||
//
|
||||
// - Otherwise, if the initializer list has a single element, the object
|
||||
// or reference is initialized from that element;
|
||||
if (InitList->getNumInits() == 1) {
|
||||
SelectInitialization(S, Entity, Kind, InitList->getInits(),
|
||||
InitList->getNumInits(), Sequence);
|
||||
// Adjust the type of the whole init list to be the same as that of the
|
||||
// single initializer.
|
||||
InitList->setType(InitList->getInits()[0]->getType());
|
||||
return;
|
||||
}
|
||||
|
||||
// - Otherwise, if T is a reference type, a prvalue temporary of the type
|
||||
// referenced by T is list-initialized, and the reference is bound to
|
||||
// that temporary.
|
||||
//
|
||||
// We implement this by unwrapping references at the start of the function
|
||||
// and adding a reference binding step at the bottom.
|
||||
|
||||
// - Otherwise, if the initializer list has no elements, the object is
|
||||
// value-initialized.
|
||||
if (InitList->getNumInits() == 0) {
|
||||
TryValueInitialization(S, Entity, Kind, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// - Otherwise, the program is ill-formed.
|
||||
//
|
||||
// The only way to get here ought to be for scalar types with > 1 inits.
|
||||
assert(DestType->isScalarType() && "Something strange is list-initialized.");
|
||||
assert(InitList->getNumInits() > 1 && "Strange number of initializers.");
|
||||
|
||||
Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar);
|
||||
return;
|
||||
}
|
||||
|
||||
/// \brief Determine whether we have compatible array types for the
|
||||
/// purposes of GNU by-copy array initialization.
|
||||
static bool hasCompatibleArrayTypes(ASTContext &Context,
|
||||
|
@ -3360,7 +3495,6 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
Expr **Args,
|
||||
unsigned NumArgs)
|
||||
: FailedCandidateSet(Kind.getLocation()) {
|
||||
ASTContext &Context = S.Context;
|
||||
|
||||
// C++0x [dcl.init]p16:
|
||||
// The semantics of initializers are as follows. The destination type is
|
||||
|
@ -3368,9 +3502,8 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// type is the type of the initializer expression. The source type is not
|
||||
// defined when the initializer is a braced-init-list or when it is a
|
||||
// parenthesized list of expressions.
|
||||
QualType DestType = Entity.getType();
|
||||
|
||||
if (DestType->isDependentType() ||
|
||||
if (Entity.getType()->isDependentType() ||
|
||||
Expr::hasAnyTypeDependentArguments(Args, NumArgs)) {
|
||||
SequenceKind = DependentSequence;
|
||||
return;
|
||||
|
@ -3379,11 +3512,22 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// Almost everything is a normal sequence.
|
||||
setSequenceKind(NormalSequence);
|
||||
|
||||
SelectInitialization(S, Entity, Kind, Args, NumArgs, *this);
|
||||
}
|
||||
|
||||
static void SelectInitialization(Sema &S, const InitializedEntity &Entity,
|
||||
const InitializationKind &Kind,
|
||||
Expr **Args, unsigned NumArgs,
|
||||
InitializationSequence &Sequence) {
|
||||
ASTContext &Context = S.Context;
|
||||
QualType DestType = Entity.getType();
|
||||
|
||||
for (unsigned I = 0; I != NumArgs; ++I)
|
||||
if (Args[I]->getObjectKind() == OK_ObjCProperty) {
|
||||
ExprResult Result = S.ConvertPropertyForRValue(Args[I]);
|
||||
if (Result.isInvalid()) {
|
||||
SetFailed(FK_ConversionFromPropertyFailed);
|
||||
Sequence.SetFailed(
|
||||
InitializationSequence::FK_ConversionFromPropertyFailed);
|
||||
return;
|
||||
}
|
||||
Args[I] = Result.take();
|
||||
|
@ -3400,7 +3544,7 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// - If the initializer is a braced-init-list, the object is
|
||||
// list-initialized (8.5.4).
|
||||
if (InitListExpr *InitList = dyn_cast_or_null<InitListExpr>(Initializer)) {
|
||||
TryListInitialization(S, Entity, Kind, InitList, *this);
|
||||
TryListInitialization(S, Entity, Kind, InitList, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3412,22 +3556,22 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// by an object that can be converted into a T.
|
||||
// (Therefore, multiple arguments are not permitted.)
|
||||
if (NumArgs != 1)
|
||||
SetFailed(FK_TooManyInitsForReference);
|
||||
Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForReference);
|
||||
else
|
||||
TryReferenceInitialization(S, Entity, Kind, Args[0], *this);
|
||||
TryReferenceInitialization(S, Entity, Kind, Args[0], Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// - If the initializer is (), the object is value-initialized.
|
||||
if (Kind.getKind() == InitializationKind::IK_Value ||
|
||||
(Kind.getKind() == InitializationKind::IK_Direct && NumArgs == 0)) {
|
||||
TryValueInitialization(S, Entity, Kind, *this);
|
||||
TryValueInitialization(S, Entity, Kind, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle default initialization.
|
||||
if (Kind.getKind() == InitializationKind::IK_Default) {
|
||||
TryDefaultInitialization(S, Entity, Kind, *this);
|
||||
TryDefaultInitialization(S, Entity, Kind, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3438,7 +3582,7 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// ill-formed.
|
||||
if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) {
|
||||
if (Initializer && IsStringInit(Initializer, DestAT, Context)) {
|
||||
TryStringLiteralInitialization(S, Entity, Kind, Initializer, *this);
|
||||
TryStringLiteralInitialization(S, Entity, Kind, Initializer, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3451,16 +3595,17 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
const ArrayType *SourceAT
|
||||
= Context.getAsArrayType(Initializer->getType());
|
||||
if (!hasCompatibleArrayTypes(S.Context, DestAT, SourceAT))
|
||||
SetFailed(FK_ArrayTypeMismatch);
|
||||
Sequence.SetFailed(InitializationSequence::FK_ArrayTypeMismatch);
|
||||
else if (Initializer->HasSideEffects(S.Context))
|
||||
SetFailed(FK_NonConstantArrayInit);
|
||||
Sequence.SetFailed(InitializationSequence::FK_NonConstantArrayInit);
|
||||
else {
|
||||
AddArrayInitStep(DestType);
|
||||
Sequence.AddArrayInitStep(DestType);
|
||||
}
|
||||
} else if (DestAT->getElementType()->isAnyCharacterType())
|
||||
SetFailed(FK_ArrayNeedsInitListOrStringLiteral);
|
||||
Sequence.SetFailed(
|
||||
InitializationSequence::FK_ArrayNeedsInitListOrStringLiteral);
|
||||
else
|
||||
SetFailed(FK_ArrayNeedsInitList);
|
||||
Sequence.SetFailed(InitializationSequence::FK_ArrayNeedsInitList);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -3475,13 +3620,13 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
if (!S.getLangOptions().CPlusPlus) {
|
||||
// If allowed, check whether this is an Objective-C writeback conversion.
|
||||
if (allowObjCWritebackConversion &&
|
||||
tryObjCWritebackConversion(S, *this, Entity, Initializer)) {
|
||||
tryObjCWritebackConversion(S, Sequence, Entity, Initializer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle initialization in C
|
||||
AddCAssignmentStep(DestType);
|
||||
MaybeProduceObjCObject(S, *this, Entity);
|
||||
Sequence.AddCAssignmentStep(DestType);
|
||||
MaybeProduceObjCObject(S, Sequence, Entity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3498,7 +3643,7 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
(Context.hasSameUnqualifiedType(SourceType, DestType) ||
|
||||
S.IsDerivedFrom(SourceType, DestType))))
|
||||
TryConstructorInitialization(S, Entity, Kind, Args, NumArgs,
|
||||
Entity.getType(), *this);
|
||||
Entity.getType(), Sequence);
|
||||
// - Otherwise (i.e., for the remaining copy-initialization cases),
|
||||
// user-defined conversion sequences that can convert from the source
|
||||
// type to the destination type or (when a conversion function is
|
||||
|
@ -3506,12 +3651,12 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// 13.3.1.4, and the best one is chosen through overload resolution
|
||||
// (13.3).
|
||||
else
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, *this);
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, Sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NumArgs > 1) {
|
||||
SetFailed(FK_TooManyInitsForScalar);
|
||||
Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar);
|
||||
return;
|
||||
}
|
||||
assert(NumArgs == 1 && "Zero-argument case handled above");
|
||||
|
@ -3519,8 +3664,8 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
// - Otherwise, if the source type is a (possibly cv-qualified) class
|
||||
// type, conversion functions are considered.
|
||||
if (!SourceType.isNull() && SourceType->isRecordType()) {
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, *this);
|
||||
MaybeProduceObjCObject(S, *this, Entity);
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, Sequence);
|
||||
MaybeProduceObjCObject(S, Sequence, Entity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3556,22 +3701,22 @@ InitializationSequence::InitializationSequence(Sema &S,
|
|||
LvalueICS.Standard.setAsIdentityConversion();
|
||||
LvalueICS.Standard.setAllToTypes(ICS.Standard.getToType(0));
|
||||
LvalueICS.Standard.First = ICS.Standard.First;
|
||||
AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0));
|
||||
Sequence.AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0));
|
||||
}
|
||||
|
||||
AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy);
|
||||
|
||||
Sequence.AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy);
|
||||
} else if (ICS.isBad()) {
|
||||
DeclAccessPair dap;
|
||||
if (Initializer->getType() == Context.OverloadTy &&
|
||||
!S.ResolveAddressOfOverloadedFunction(Initializer
|
||||
, DestType, false, dap))
|
||||
SetFailed(InitializationSequence::FK_AddressOfOverloadFailed);
|
||||
Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed);
|
||||
else
|
||||
SetFailed(InitializationSequence::FK_ConversionFailed);
|
||||
Sequence.SetFailed(InitializationSequence::FK_ConversionFailed);
|
||||
} else {
|
||||
AddConversionSequenceStep(ICS, Entity.getType());
|
||||
Sequence.AddConversionSequenceStep(ICS, Entity.getType());
|
||||
|
||||
MaybeProduceObjCObject(S, *this, Entity);
|
||||
MaybeProduceObjCObject(S, Sequence, Entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
|
||||
// An aggregate is an array or a class...
|
||||
struct Aggr {
|
||||
private:
|
||||
static const int n;
|
||||
void f();
|
||||
protected:
|
||||
struct Inner { int m; };
|
||||
public:
|
||||
bool &br;
|
||||
};
|
||||
bool b;
|
||||
Aggr ag = { b };
|
||||
|
||||
// with no user-provided constructors, ...
|
||||
struct NonAggr1a {
|
||||
NonAggr1a(int, int);
|
||||
int k;
|
||||
};
|
||||
// In C++03, this is {{non-aggregate type 'NonAggr1a'}}.
|
||||
// In C++0x, 'user-provided' is only defined for special member functions, so
|
||||
// this type is considered to be an aggregate. This is probably a langauge
|
||||
// defect.
|
||||
NonAggr1a na1a = { 42 };
|
||||
|
||||
struct NonAggr1b {
|
||||
NonAggr1b(const NonAggr1b &);
|
||||
int k;
|
||||
};
|
||||
NonAggr1b na1b = { 42 }; // expected-error {{non-aggregate type 'NonAggr1b'}}
|
||||
|
||||
// no brace-or-equal-initializers for non-static data members, ...
|
||||
struct NonAggr2 {
|
||||
int m = { 123 };
|
||||
};
|
||||
NonAggr2 na2 = { 42 }; // expected-error {{non-aggregate type 'NonAggr2'}}
|
||||
|
||||
// no private...
|
||||
struct NonAggr3 {
|
||||
private:
|
||||
int n;
|
||||
};
|
||||
NonAggr3 na3 = { 42 }; // expected-error {{non-aggregate type 'NonAggr3'}}
|
||||
|
||||
// or protected non-static data members, ...
|
||||
struct NonAggr4 {
|
||||
protected:
|
||||
int n;
|
||||
};
|
||||
NonAggr4 na4 = { 42 }; // expected-error {{non-aggregate type 'NonAggr4'}}
|
||||
|
||||
// no base classes, ...
|
||||
struct NonAggr5 : Aggr {
|
||||
};
|
||||
NonAggr5 na5 = { b }; // expected-error {{non-aggregate type 'NonAggr5'}}
|
||||
|
||||
// and no virtual functions.
|
||||
struct NonAggr6 {
|
||||
virtual void f();
|
||||
int n;
|
||||
};
|
||||
NonAggr6 na6 = { 42 }; // expected-error {{non-aggregate type 'NonAggr6'}}
|
|
@ -1,9 +1,7 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
// Verify that we can't initialize non-aggregates with an initializer
|
||||
// list.
|
||||
// FIXME: Note that due to a (likely) standard bug, this is technically an
|
||||
// aggregate.
|
||||
struct NonAggr1 {
|
||||
NonAggr1(int) { }
|
||||
|
||||
|
@ -24,7 +22,7 @@ struct NonAggr4 {
|
|||
virtual void f();
|
||||
};
|
||||
|
||||
NonAggr1 na1 = { 17 };
|
||||
NonAggr1 na1 = { 17 }; // expected-error{{non-aggregate type 'NonAggr1' cannot be initialized with an initializer list}}
|
||||
NonAggr2 na2 = { 17 }; // expected-error{{non-aggregate type 'NonAggr2' cannot be initialized with an initializer list}}
|
||||
NonAggr3 na3 = { 17 }; // expected-error{{non-aggregate type 'NonAggr3' cannot be initialized with an initializer list}}
|
||||
NonAggr4 na4 = { 17 }; // expected-error{{non-aggregate type 'NonAggr4' cannot be initialized with an initializer list}}
|
||||
|
@ -48,8 +46,9 @@ struct A {
|
|||
A();
|
||||
A(int);
|
||||
~A();
|
||||
|
||||
A(const A&) = delete; // expected-note 2 {{function has been explicitly marked deleted here}}
|
||||
|
||||
private:
|
||||
A(const A&) {} // expected-note 4 {{declared private here}}
|
||||
};
|
||||
|
||||
struct B {
|
||||
|
@ -62,10 +61,10 @@ struct C {
|
|||
|
||||
void f() {
|
||||
A as1[1] = { };
|
||||
A as2[1] = { 1 }; // expected-error {{copying array element of type 'A' invokes deleted constructor}}
|
||||
A as2[1] = { 1 }; // expected-error {{calling a private constructor of class 'A'}} expected-warning {{requires an accessible copy constructor}}
|
||||
|
||||
B b1 = { };
|
||||
B b2 = { 1 }; // expected-error {{copying member subobject of type 'A' invokes deleted constructor}}
|
||||
B b2 = { 1 }; // expected-error {{field of type 'A' has private copy constructor}} expected-warning {{requires an accessible copy constructor}}
|
||||
|
||||
C c1 = { 1 };
|
||||
}
|
||||
|
|
|
@ -224,5 +224,39 @@ namespace aggregate {
|
|||
S s3{ 1, 2, 3, 4, 5, 6 }; // xpected-error
|
||||
S s4{ {1, 2}, {3, 4}, {5, 6}, { {7, 8} } }; // xpected-error
|
||||
S s5{ {1, 2}, {3, 4}, { {5}, {6} }, {7, 8} }; // xpected-error
|
||||
// May still omit stuff, though.
|
||||
S s6{ {1}, {}, { {}, {} } };
|
||||
}
|
||||
}
|
||||
|
||||
namespace references {
|
||||
// From [dcl.init.list]p3 bullet 5:
|
||||
struct S {
|
||||
S(std::initializer_list<double>);
|
||||
S(const std::string&);
|
||||
};
|
||||
void test() {
|
||||
const S &r1 = { 1, 2, 3.0 }; // no-error (constructor #1)
|
||||
const S &r2{ "Spinach" }; // no-error (constructor #2)
|
||||
S &r3 = { 1, 2, 3 }; // xpected-error (binding to non-const)
|
||||
const int &i1 = { 1 }; // no-error
|
||||
const int &i2 = { 1.1 }; // xpected-error {{narrowing}}
|
||||
const int (&iar)[2] = { 1, 2 }; // no-error
|
||||
|
||||
// Edge case: the standard says this must create a temporary and thus
|
||||
// fail to bind, but that's almost certainly a defect.
|
||||
int i;
|
||||
int &ri1{ i };
|
||||
int &ri2 = { i };
|
||||
S s{ "Spinach" };
|
||||
S &rs1{ s };
|
||||
S &rs2 = { s };
|
||||
}
|
||||
}
|
||||
|
||||
namespace incomplete {
|
||||
// Just to make sure it doesn't crash.
|
||||
struct S;
|
||||
S s { 1, 2, 3 }; // expected-error {{incomplete}}
|
||||
S t = { 1, 2, 3 }; // expected-error {{incomplete}}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче