diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index d2122a45aecda392fc313d8c6bf8116ca984ecc0..835d837e4c0ae21edef984a7b00e8a652e8915c5 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7,7 +7,9 @@ // //===----------------------------------------------------------------------===// // -// This file implements semantic analysis for initializers. +// 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. // //===----------------------------------------------------------------------===// @@ -710,7 +712,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 initializer from + // initializing the aggregate member with an ini- tializer from // an initializer-list. If the initializer can initialize a // member, the member is initialized. [...] @@ -2382,10 +2384,52 @@ static void MaybeProduceObjCObject(Sema &S, } } -static void SelectInitialization(Sema &S, const InitializedEntity &Entity, - const InitializationKind &Kind, - Expr **Args, unsigned NumArgs, - InitializationSequence &Sequence); +/// \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); +} /// \brief Try a reference initialization that involves calling a conversion /// function. @@ -3244,185 +3288,6 @@ 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(*Con)) - continue; - CXXConstructorDecl *Constructor = cast(*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()) { - 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(); - CXXRecordDecl *recordDecl = recordType ? - dyn_cast(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, 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, @@ -3495,6 +3360,7 @@ 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 @@ -3502,8 +3368,9 @@ 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 (Entity.getType()->isDependentType() || + if (DestType->isDependentType() || Expr::hasAnyTypeDependentArguments(Args, NumArgs)) { SequenceKind = DependentSequence; return; @@ -3512,22 +3379,11 @@ 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()) { - Sequence.SetFailed( - InitializationSequence::FK_ConversionFromPropertyFailed); + SetFailed(FK_ConversionFromPropertyFailed); return; } Args[I] = Result.take(); @@ -3544,7 +3400,7 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, // - If the initializer is a braced-init-list, the object is // list-initialized (8.5.4). if (InitListExpr *InitList = dyn_cast_or_null(Initializer)) { - TryListInitialization(S, Entity, Kind, InitList, Sequence); + TryListInitialization(S, Entity, Kind, InitList, *this); return; } @@ -3556,22 +3412,22 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, // by an object that can be converted into a T. // (Therefore, multiple arguments are not permitted.) if (NumArgs != 1) - Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForReference); + SetFailed(FK_TooManyInitsForReference); else - TryReferenceInitialization(S, Entity, Kind, Args[0], Sequence); + TryReferenceInitialization(S, Entity, Kind, Args[0], *this); 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, Sequence); + TryValueInitialization(S, Entity, Kind, *this); return; } // Handle default initialization. if (Kind.getKind() == InitializationKind::IK_Default) { - TryDefaultInitialization(S, Entity, Kind, Sequence); + TryDefaultInitialization(S, Entity, Kind, *this); return; } @@ -3582,7 +3438,7 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, // ill-formed. if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) { if (Initializer && IsStringInit(Initializer, DestAT, Context)) { - TryStringLiteralInitialization(S, Entity, Kind, Initializer, Sequence); + TryStringLiteralInitialization(S, Entity, Kind, Initializer, *this); return; } @@ -3595,17 +3451,16 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, const ArrayType *SourceAT = Context.getAsArrayType(Initializer->getType()); if (!hasCompatibleArrayTypes(S.Context, DestAT, SourceAT)) - Sequence.SetFailed(InitializationSequence::FK_ArrayTypeMismatch); + SetFailed(FK_ArrayTypeMismatch); else if (Initializer->HasSideEffects(S.Context)) - Sequence.SetFailed(InitializationSequence::FK_NonConstantArrayInit); + SetFailed(FK_NonConstantArrayInit); else { - Sequence.AddArrayInitStep(DestType); + AddArrayInitStep(DestType); } } else if (DestAT->getElementType()->isAnyCharacterType()) - Sequence.SetFailed( - InitializationSequence::FK_ArrayNeedsInitListOrStringLiteral); + SetFailed(FK_ArrayNeedsInitListOrStringLiteral); else - Sequence.SetFailed(InitializationSequence::FK_ArrayNeedsInitList); + SetFailed(FK_ArrayNeedsInitList); return; } @@ -3620,13 +3475,13 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, if (!S.getLangOptions().CPlusPlus) { // If allowed, check whether this is an Objective-C writeback conversion. if (allowObjCWritebackConversion && - tryObjCWritebackConversion(S, Sequence, Entity, Initializer)) { + tryObjCWritebackConversion(S, *this, Entity, Initializer)) { return; } // Handle initialization in C - Sequence.AddCAssignmentStep(DestType); - MaybeProduceObjCObject(S, Sequence, Entity); + AddCAssignmentStep(DestType); + MaybeProduceObjCObject(S, *this, Entity); return; } @@ -3643,7 +3498,7 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, (Context.hasSameUnqualifiedType(SourceType, DestType) || S.IsDerivedFrom(SourceType, DestType)))) TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, - Entity.getType(), Sequence); + Entity.getType(), *this); // - 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 @@ -3651,12 +3506,12 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, // 13.3.1.4, and the best one is chosen through overload resolution // (13.3). else - TryUserDefinedConversion(S, Entity, Kind, Initializer, Sequence); + TryUserDefinedConversion(S, Entity, Kind, Initializer, *this); return; } if (NumArgs > 1) { - Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar); + SetFailed(FK_TooManyInitsForScalar); return; } assert(NumArgs == 1 && "Zero-argument case handled above"); @@ -3664,8 +3519,8 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, // - 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, Sequence); - MaybeProduceObjCObject(S, Sequence, Entity); + TryUserDefinedConversion(S, Entity, Kind, Initializer, *this); + MaybeProduceObjCObject(S, *this, Entity); return; } @@ -3701,22 +3556,22 @@ static void SelectInitialization(Sema &S, const InitializedEntity &Entity, LvalueICS.Standard.setAsIdentityConversion(); LvalueICS.Standard.setAllToTypes(ICS.Standard.getToType(0)); LvalueICS.Standard.First = ICS.Standard.First; - Sequence.AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0)); + AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0)); } - - Sequence.AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); + + AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); } else if (ICS.isBad()) { DeclAccessPair dap; if (Initializer->getType() == Context.OverloadTy && !S.ResolveAddressOfOverloadedFunction(Initializer , DestType, false, dap)) - Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); + SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else - Sequence.SetFailed(InitializationSequence::FK_ConversionFailed); + SetFailed(InitializationSequence::FK_ConversionFailed); } else { - Sequence.AddConversionSequenceStep(ICS, Entity.getType()); + AddConversionSequenceStep(ICS, Entity.getType()); - MaybeProduceObjCObject(S, Sequence, Entity); + MaybeProduceObjCObject(S, *this, Entity); } } diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b92340fa457d456f0550bb64ca61b8872dee981 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp @@ -0,0 +1,63 @@ +// 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'}} diff --git a/clang/test/SemaCXX/aggregate-initialization.cpp b/clang/test/SemaCXX/aggregate-initialization.cpp index d4896918982936e2bd02b28ab3b41ae2902457d0..b9e69b00b7fefacdb9e15899af28c3a3534bb56e 100644 --- a/clang/test/SemaCXX/aggregate-initialization.cpp +++ b/clang/test/SemaCXX/aggregate-initialization.cpp @@ -1,7 +1,9 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %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) { } @@ -22,7 +24,7 @@ struct NonAggr4 { virtual void f(); }; -NonAggr1 na1 = { 17 }; // expected-error{{non-aggregate type 'NonAggr1' cannot be initialized with an initializer list}} +NonAggr1 na1 = { 17 }; 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}} @@ -46,9 +48,8 @@ struct A { A(); A(int); ~A(); - -private: - A(const A&) {} // expected-note 4 {{declared private here}} + + A(const A&) = delete; // expected-note 2 {{function has been explicitly marked deleted here}} }; struct B { @@ -61,10 +62,10 @@ struct C { void f() { A as1[1] = { }; - A as2[1] = { 1 }; // expected-error {{calling a private constructor of class 'A'}} expected-warning {{requires an accessible copy constructor}} + A as2[1] = { 1 }; // expected-error {{copying array element of type 'A' invokes deleted constructor}} B b1 = { }; - B b2 = { 1 }; // expected-error {{field of type 'A' has private copy constructor}} expected-warning {{requires an accessible copy constructor}} + B b2 = { 1 }; // expected-error {{copying member subobject of type 'A' invokes deleted constructor}} C c1 = { 1 }; } diff --git a/clang/test/SemaCXX/generalized-initializers.cpp b/clang/test/SemaCXX/generalized-initializers.cpp index 738dc5cf2c7223d1fe6938c5408e975000c44f3b..6e2bee7e301d153284c6d22adbe2a844c4b31d54 100644 --- a/clang/test/SemaCXX/generalized-initializers.cpp +++ b/clang/test/SemaCXX/generalized-initializers.cpp @@ -224,39 +224,5 @@ 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); - 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}} -}