Index: cfe/trunk/include/clang/AST/DeclBase.h =================================================================== --- cfe/trunk/include/clang/AST/DeclBase.h +++ cfe/trunk/include/clang/AST/DeclBase.h @@ -48,6 +48,7 @@ class ObjCMethodDecl; class ObjCProtocolDecl; struct PrintingPolicy; +class RecordDecl; class Stmt; class StoredDeclsMap; class TranslationUnitDecl; @@ -1238,6 +1239,12 @@ return const_cast(this)->getEnclosingNamespaceContext(); } + /// \brief Retrieve the outermost lexically enclosing record context. + RecordDecl *getOuterLexicalRecordContext(); + const RecordDecl *getOuterLexicalRecordContext() const { + return const_cast(this)->getOuterLexicalRecordContext(); + } + /// \brief Test if this context is part of the enclosing namespace set of /// the context NS, as defined in C++0x [namespace.def]p9. If either context /// isn't a namespace, this is equivalent to Equals(). Index: cfe/trunk/include/clang/AST/ExprCXX.h =================================================================== --- cfe/trunk/include/clang/AST/ExprCXX.h +++ cfe/trunk/include/clang/AST/ExprCXX.h @@ -967,8 +967,14 @@ const FieldDecl *getField() const { return Field; } /// \brief Get the initialization expression that will be used. - const Expr *getExpr() const { return Field->getInClassInitializer(); } - Expr *getExpr() { return Field->getInClassInitializer(); } + const Expr *getExpr() const { + assert(Field->getInClassInitializer() && "initializer hasn't been parsed"); + return Field->getInClassInitializer(); + } + Expr *getExpr() { + assert(Field->getInClassInitializer() && "initializer hasn't been parsed"); + return Field->getInClassInitializer(); + } SourceLocation getLocStart() const LLVM_READONLY { return Loc; } SourceLocation getLocEnd() const LLVM_READONLY { return Loc; } Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -3536,6 +3536,8 @@ "in instantiation of variable template specialization %q0 requested here">; def note_template_enum_def_here : Note< "in instantiation of enumeration %q0 requested here">; +def note_template_nsdmi_here : Note< + "in instantiation of default member initializer %q0 requested here">; def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; def note_template_exception_spec_instantiation_here : Note< @@ -6213,9 +6215,12 @@ "'constexpr' specifier">; def err_in_class_initializer_non_constant : Error< "in-class initializer for static data member is not a constant expression">; -def err_in_class_initializer_references_def_ctor : Error< - "defaulted default constructor of %0 cannot be used by non-static data " - "member initializer which appears before end of class definition">; +def err_in_class_initializer_not_yet_parsed + : Error<"cannot use defaulted default constructor of %0 within the class " + "outside of member functions because %1 has an initializer">; +def err_in_class_initializer_not_yet_parsed_outer_class + : Error<"cannot use defaulted default constructor of %0 within " + "%1 outside of member functions because %2 has an initializer">; def ext_in_class_initializer_non_constant : Extension< "in-class initializer for static data member is not a constant expression; " Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -3982,6 +3982,8 @@ bool IsStdInitListInitialization, bool RequiresZeroInit, unsigned ConstructKind, SourceRange ParenRange); + ExprResult BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field); + /// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating /// the default expr if needed. ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc, @@ -6831,6 +6833,10 @@ const MultiLevelTemplateArgumentList &TemplateArgs, TemplateSpecializationKind TSK); + bool InstantiateInClassInitializer( + SourceLocation PointOfInstantiation, FieldDecl *Instantiation, + FieldDecl *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs); + struct LateInstantiatedAttribute { const Attr *TmplAttr; LocalInstantiationScope *Scope; Index: cfe/trunk/lib/AST/DeclBase.cpp =================================================================== --- cfe/trunk/lib/AST/DeclBase.cpp +++ cfe/trunk/lib/AST/DeclBase.cpp @@ -1441,6 +1441,17 @@ return Ctx->getPrimaryContext(); } +RecordDecl *DeclContext::getOuterLexicalRecordContext() { + // Loop until we find a non-record context. + RecordDecl *OutermostRD = nullptr; + DeclContext *DC = this; + while (DC->isRecord()) { + OutermostRD = cast(DC); + DC = DC->getLexicalParent(); + } + return OutermostRD; +} + bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const { // For non-file contexts, this is equivalent to Equals. if (!isFileContext()) Index: cfe/trunk/lib/AST/Expr.cpp =================================================================== --- cfe/trunk/lib/AST/Expr.cpp +++ cfe/trunk/lib/AST/Expr.cpp @@ -2985,11 +2985,13 @@ case CXXDefaultArgExprClass: return cast(this)->getExpr()->HasSideEffects(Ctx); - case CXXDefaultInitExprClass: - if (const Expr *E = cast(this)->getExpr()) + case CXXDefaultInitExprClass: { + const FieldDecl *FD = cast(this)->getField(); + if (const Expr *E = FD->getInClassInitializer()) return E->HasSideEffects(Ctx); // If we've not yet parsed the initializer, assume it has side-effects. return true; + } case CXXDynamicCastExprClass: { // A dynamic_cast expression has side-effects if it can throw. Index: cfe/trunk/lib/Sema/SemaDeclCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp @@ -36,6 +36,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Template.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include @@ -3730,19 +3731,19 @@ return false; if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) { - Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, - Info.Ctor->getLocation(), Field); + ExprResult DIE = + SemaRef.BuildCXXDefaultInitExpr(Info.Ctor->getLocation(), Field); + if (DIE.isInvalid()) + return true; CXXCtorInitializer *Init; if (Indirect) - Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Indirect, - SourceLocation(), - SourceLocation(), DIE, - SourceLocation()); + Init = new (SemaRef.Context) + CXXCtorInitializer(SemaRef.Context, Indirect, SourceLocation(), + SourceLocation(), DIE.get(), SourceLocation()); else - Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, - SourceLocation(), - SourceLocation(), DIE, - SourceLocation()); + Init = new (SemaRef.Context) + CXXCtorInitializer(SemaRef.Context, Field, SourceLocation(), + SourceLocation(), DIE.get(), SourceLocation()); return Info.addFieldInitializer(Init); } @@ -8579,22 +8580,6 @@ if (F->hasInClassInitializer()) { if (Expr *E = F->getInClassInitializer()) ExceptSpec.CalledExpr(E); - else if (!F->isInvalidDecl()) - // DR1351: - // If the brace-or-equal-initializer of a non-static data member - // invokes a defaulted default constructor of its class or of an - // enclosing class in a potentially evaluated subexpression, the - // program is ill-formed. - // - // This resolution is unworkable: the exception specification of the - // default constructor can be needed in an unevaluated context, in - // particular, in the operand of a noexcept-expression, and we can be - // unable to compute an exception specification for an enclosed class. - // - // We do not allow an in-class initializer to require the evaluation - // of the exception specification for any in-class initializer whose - // definition is not lexically complete. - Diag(Loc, diag::err_in_class_initializer_references_def_ctor) << MD; } else if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) { CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); @@ -8662,9 +8647,6 @@ if (F->hasInClassInitializer()) { if (Expr *E = F->getInClassInitializer()) ExceptSpec.CalledExpr(E); - else if (!F->isInvalidDecl()) - Diag(CD->getLocation(), - diag::err_in_class_initializer_references_def_ctor) << CD; } else if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) { CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); @@ -11129,6 +11111,56 @@ ParenRange); } +ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { + assert(Field->hasInClassInitializer()); + + // If we already have the in-class initializer nothing needs to be done. + if (Field->getInClassInitializer()) + return CXXDefaultInitExpr::Create(Context, Loc, Field); + + // Maybe we haven't instantiated the in-class initializer. Go check the + // pattern FieldDecl to see if it has one. + CXXRecordDecl *ParentRD = cast(Field->getParent()); + + if (isTemplateInstantiation(ParentRD->getTemplateSpecializationKind())) { + CXXRecordDecl *ClassPattern = ParentRD->getTemplateInstantiationPattern(); + DeclContext::lookup_result Lookup = + ClassPattern->lookup(Field->getDeclName()); + assert(Lookup.size() == 1); + FieldDecl *Pattern = cast(Lookup[0]); + if (InstantiateInClassInitializer(Loc, Field, Pattern, + getTemplateInstantiationArgs(Field))) + return ExprError(); + return CXXDefaultInitExpr::Create(Context, Loc, Field); + } + + // DR1351: + // If the brace-or-equal-initializer of a non-static data member + // invokes a defaulted default constructor of its class or of an + // enclosing class in a potentially evaluated subexpression, the + // program is ill-formed. + // + // This resolution is unworkable: the exception specification of the + // default constructor can be needed in an unevaluated context, in + // particular, in the operand of a noexcept-expression, and we can be + // unable to compute an exception specification for an enclosed class. + // + // Any attempt to resolve the exception specification of a defaulted default + // constructor before the initializer is lexically complete will ultimately + // come here at which point we can diagnose it. + RecordDecl *OutermostClass = ParentRD->getOuterLexicalRecordContext(); + if (OutermostClass == ParentRD) { + Diag(Field->getLocEnd(), diag::err_in_class_initializer_not_yet_parsed) + << ParentRD << Field; + } else { + Diag(Field->getLocEnd(), + diag::err_in_class_initializer_not_yet_parsed_outer_class) + << ParentRD << OutermostClass << Field; + } + + return ExprError(); +} + void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { if (VD->isInvalidDecl()) return; Index: cfe/trunk/lib/Sema/SemaInit.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaInit.cpp +++ cfe/trunk/lib/Sema/SemaInit.cpp @@ -466,11 +466,15 @@ // members in the aggregate, then each member not explicitly initialized // shall be initialized from its brace-or-equal-initializer [...] if (Field->hasInClassInitializer()) { - Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, Loc, Field); + ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + if (DIE.isInvalid()) { + hadError = true; + return; + } if (Init < NumInits) - ILE->setInit(Init, DIE); + ILE->setInit(Init, DIE.get()); else { - ILE->updateInit(SemaRef.Context, Init, DIE); + ILE->updateInit(SemaRef.Context, Init, DIE.get()); RequiresSecondPass = true; } return; Index: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp @@ -448,6 +448,10 @@ diag::note_template_enum_def_here) << ED << Active->InstantiationRange; + } else if (FieldDecl *FD = dyn_cast(D)) { + Diags.Report(Active->PointOfInstantiation, + diag::note_template_nsdmi_here) + << FD << Active->InstantiationRange; } else { Diags.Report(Active->PointOfInstantiation, diag::note_template_type_alias_instantiation_here) @@ -1997,8 +2001,6 @@ TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs); SmallVector Fields; - SmallVector, 4> - FieldsWithMemberInitializers; // Delay instantiation of late parsed attributes. LateInstantiatedAttrVec LateAttrs; Instantiator.enableLateAttributeInstantiation(&LateAttrs); @@ -2025,10 +2027,6 @@ if (NewMember) { if (FieldDecl *Field = dyn_cast(NewMember)) { Fields.push_back(Field); - FieldDecl *OldField = cast(Member); - if (OldField->getInClassInitializer()) - FieldsWithMemberInitializers.push_back(std::make_pair(OldField, - Field)); } else if (EnumDecl *Enum = dyn_cast(NewMember)) { // C++11 [temp.inst]p1: The implicit instantiation of a class template // specialization causes the implicit instantiation of the definitions @@ -2065,31 +2063,6 @@ SourceLocation(), SourceLocation(), nullptr); CheckCompletedCXXClass(Instantiation); - // Attach any in-class member initializers now the class is complete. - // FIXME: We are supposed to defer instantiating these until they are needed. - if (!FieldsWithMemberInitializers.empty()) { - // C++11 [expr.prim.general]p4: - // Otherwise, if a member-declarator declares a non-static data member - // (9.2) of a class X, the expression this is a prvalue of type "pointer - // to X" within the optional brace-or-equal-initializer. It shall not - // appear elsewhere in the member-declarator. - CXXThisScopeRAII ThisScope(*this, Instantiation, (unsigned)0); - - for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) { - FieldDecl *OldField = FieldsWithMemberInitializers[I].first; - FieldDecl *NewField = FieldsWithMemberInitializers[I].second; - Expr *OldInit = OldField->getInClassInitializer(); - - ActOnStartCXXInClassMemberInitializer(); - ExprResult NewInit = SubstInitializer(OldInit, TemplateArgs, - /*CXXDirectInit=*/false); - Expr *Init = NewInit.get(); - assert((!Init || !isa(Init)) && - "call-style init in class"); - ActOnFinishCXXInClassMemberInitializer(NewField, - Init ? Init->getLocStart() : SourceLocation(), Init); - } - } // Instantiate late parsed attributes, and attach them to their decls. // See Sema::InstantiateAttrs for (LateInstantiatedAttrVec::iterator I = LateAttrs.begin(), @@ -2230,6 +2203,80 @@ return Instantiation->isInvalidDecl(); } + +/// \brief Instantiate the definition of a field from the given pattern. +/// +/// \param PointOfInstantiation The point of instantiation within the +/// source code. +/// \param Instantiation is the declaration whose definition is being +/// instantiated. This will be a class of a class temploid +/// specialization, or a local enumeration within a function temploid +/// specialization. +/// \param Pattern The templated declaration from which the instantiation +/// occurs. +/// \param TemplateArgs The template arguments to be substituted into +/// the pattern. +/// +/// \return \c true if an error occurred, \c false otherwise. +bool Sema::InstantiateInClassInitializer( + SourceLocation PointOfInstantiation, FieldDecl *Instantiation, + FieldDecl *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs) { + // If there is no initializer, we don't need to do anything. + if (!Pattern->hasInClassInitializer()) + return false; + + assert(Instantiation->getInClassInitStyle() == + Pattern->getInClassInitStyle() && + "pattern and instantiation disagree about init style"); + + // Error out if we haven't parsed the initializer of the pattern yet because + // we are waiting for the closing brace of the outer class. + Expr *OldInit = Pattern->getInClassInitializer(); + if (!OldInit) { + RecordDecl *PatternRD = Pattern->getParent(); + RecordDecl *OutermostClass = PatternRD->getOuterLexicalRecordContext(); + if (OutermostClass == PatternRD) { + Diag(Pattern->getLocEnd(), diag::err_in_class_initializer_not_yet_parsed) + << PatternRD << Pattern; + } else { + Diag(Pattern->getLocEnd(), + diag::err_in_class_initializer_not_yet_parsed_outer_class) + << PatternRD << OutermostClass << Pattern; + } + Instantiation->setInvalidDecl(); + return true; + } + + InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation); + if (Inst.isInvalid()) + return true; + + // Enter the scope of this instantiation. We don't use PushDeclContext because + // we don't have a scope. + ContextRAII SavedContext(*this, Instantiation->getParent()); + EnterExpressionEvaluationContext EvalContext(*this, + Sema::PotentiallyEvaluated); + + LocalInstantiationScope Scope(*this); + + // Instantiate the initializer. + ActOnStartCXXInClassMemberInitializer(); + CXXThisScopeRAII ThisScope(*this, Instantiation->getParent(), /*TypeQuals=*/0); + + ExprResult NewInit = SubstInitializer(OldInit, TemplateArgs, + /*CXXDirectInit=*/false); + Expr *Init = NewInit.get(); + assert((!Init || !isa(Init)) && "call-style init in class"); + ActOnFinishCXXInClassMemberInitializer( + Instantiation, Init ? Init->getLocStart() : SourceLocation(), Init); + + // Exit the scope of this instantiation. + SavedContext.pop(); + + // Return true if the in-class initializer is still missing. + return !Instantiation->getInClassInitializer(); +} + namespace { /// \brief A partial specialization whose template arguments have matched /// a given template-id. @@ -2591,6 +2638,19 @@ MSInfo->setTemplateSpecializationKind(TSK); MSInfo->setPointOfInstantiation(PointOfInstantiation); } + } else if (auto *Field = dyn_cast(D)) { + // No need to instantiate in-class initializers during explicit + // instantiation. + if (Field->hasInClassInitializer() && TSK == TSK_ImplicitInstantiation) { + CXXRecordDecl *ClassPattern = + Instantiation->getTemplateInstantiationPattern(); + DeclContext::lookup_result Lookup = + ClassPattern->lookup(Field->getDeclName()); + assert(Lookup.size() == 1); + FieldDecl *Pattern = cast(Lookup[0]); + InstantiateInClassInitializer(PointOfInstantiation, Field, Pattern, + TemplateArgs); + } } } } Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp @@ -140,11 +140,11 @@ } template - struct X2 { + struct X2 { // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2::x' requested here}} int x = []{ return T(); }(); // expected-error{{cannot initialize a member subobject of type 'int' with an rvalue of type 'int *'}} }; X2 x2i; X2 x2f; - X2 x2ip; // expected-note{{in instantiation of template class 'NonLocalLambdaInstantation::X2' requested here}} + X2 x2ip; // expected-note{{implicit default constructor for 'NonLocalLambdaInstantation::X2' first required here}} } Index: cfe/trunk/test/CXX/temp/temp.param/p5.cpp =================================================================== --- cfe/trunk/test/CXX/temp/temp.param/p5.cpp +++ cfe/trunk/test/CXX/temp/temp.param/p5.cpp @@ -1,13 +1,13 @@ // RUN: %clang_cc1 -verify %s -std=c++11 -template struct S { +template struct S { // expected-note {{instantiation}} decltype(I) n; int &&r = I; // expected-warning 2{{binding reference member 'r' to a temporary value}} expected-note 2{{declared here}} }; -S<5> s; // expected-note {{instantiation}} +S<5> s; -template struct U { +template struct U { // expected-note {{instantiation}} decltype(v) n; int &&r = v; // expected-warning {{binding reference member 'r' to a temporary value}} expected-note {{declared here}} }; -U u; // expected-note {{instantiation}} +U u; Index: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp =================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp +++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp @@ -1844,8 +1844,9 @@ namespace BadDefaultInit { template struct X { static const int n = N; }; - struct A { // expected-note {{subexpression}} - int k = X::n; // expected-error {{defaulted default constructor of 'A' cannot be used}} expected-error {{not a constant expression}} expected-note {{in call to 'A()'}} + struct A { + int k = // expected-error {{cannot use defaulted default constructor of 'A' within the class outside of member functions because 'k' has an initializer}} + X::n; // expected-error {{not a constant expression}} expected-note {{implicit default constructor for 'BadDefaultInit::A' first required here}} }; // FIXME: The "constexpr constructor must initialize all members" diagnostic Index: cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp =================================================================== --- cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp +++ cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp @@ -17,31 +17,33 @@ // is false. bool ThrowSomething() noexcept(false); struct ConstExpr { - bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{cannot be used by non-static data member initializer}} + bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{cannot use defaulted default constructor of 'ConstExpr' within the class outside of member functions}} + // expected-note@-1 {{implicit default constructor for 'InClassInitializers::ConstExpr' first required here}} }; - // We can use it now. - bool w = noexcept(ConstExpr()); // Much more obviously broken: we can't parse the initializer without already // knowing whether it produces a noexcept expression. struct TemplateArg { - int n = ExceptionIf::f(); // expected-error {{cannot be used by non-static data member initializer}} + int n = ExceptionIf::f(); // expected-error {{cannot use defaulted default constructor of 'TemplateArg' within the class outside of member functions}} + // expected-note@-1 {{implicit default constructor for 'InClassInitializers::TemplateArg' first required here}} }; - bool x = noexcept(TemplateArg()); // And within a nested class. - struct Nested { // expected-error {{cannot be used by non-static data member initializer}} + struct Nested { // expected-note {{implicit default constructor for 'InClassInitializers::Nested::Inner' first required here}} struct Inner { + // expected-error@+1 {{cannot use defaulted default constructor of 'Inner' within 'Nested' outside of member functions}} int n = ExceptionIf::f(); // expected-note {{implicit default constructor for 'InClassInitializers::Nested' first required here}} } inner; }; - struct Nested2 { + struct Nested2 { // expected-error {{implicit default constructor for 'InClassInitializers::Nested2' must explicitly initialize the member 'inner' which does not have a default constructor}} struct Inner; - int n = Inner().n; // expected-error {{cannot be used by non-static data member initializer}} - struct Inner { + int n = Inner().n; // expected-note {{implicit default constructor for 'InClassInitializers::Nested2::Inner' first required here}} + struct Inner { // expected-note {{declared here}} + // expected-error@+1 {{cannot use defaulted default constructor of 'Inner' within 'Nested2' outside of member functions}} int n = ExceptionIf::f(); - } inner; + // expected-note@-1 {{implicit default constructor for 'InClassInitializers::Nested2' first required here}} + } inner; // expected-note {{member is declared here}} }; } Index: cfe/trunk/test/SemaCXX/member-init.cpp =================================================================== --- cfe/trunk/test/SemaCXX/member-init.cpp +++ cfe/trunk/test/SemaCXX/member-init.cpp @@ -14,7 +14,10 @@ bool b(); int k; struct Recurse { - int &n = b() ? Recurse().n : k; // expected-error {{defaulted default constructor of 'Recurse' cannot be used by non-static data member initializer which appears before end of class definition}} + int &n = // expected-error {{cannot use defaulted default constructor of 'Recurse' within the class outside of member functions because 'n' has an initializer}} + b() ? + Recurse().n : // expected-note {{implicit default constructor for 'Recurse' first required here}} + k; }; struct UnknownBound { @@ -110,3 +113,75 @@ struct Y { int b = f(); }; } + +namespace template_valid { +// Valid, we shouldn't build a CXXDefaultInitExpr until A's ctor definition. +struct A { + A(); + template + struct B { int m1 = sizeof(A) + sizeof(T); }; + B m2; +}; +A::A() {} +} + +namespace template_default_ctor { +struct A { + template + struct B { + int m1 = 0; // expected-error {{cannot use defaulted default constructor of 'B' within 'A' outside of member functions because 'm1' has an initializer}} + }; + // expected-note@+1 {{implicit default constructor for 'template_default_ctor::A::B' first required here}} + enum { NOE = noexcept(B()) }; +}; +} + +namespace default_ctor { +struct A { + struct B { + int m1 = 0; // expected-error {{cannot use defaulted default constructor of 'B' within 'A' outside of member functions because 'm1' has an initializer}} + }; + // expected-note@+1 {{implicit default constructor for 'default_ctor::A::B' first required here}} + enum { NOE = noexcept(B()) }; +}; +} + +namespace member_template { +struct A { + template + struct B { + struct C { + int m1 = 0; // expected-error {{cannot use defaulted default constructor of 'C' within 'A' outside of member functions because 'm1' has an initializer}} + }; + template + struct D { + int m1 = 0; // expected-error {{cannot use defaulted default constructor of 'D' within 'A' outside of member functions because 'm1' has an initializer}} + }; + }; + enum { + // expected-note@+1 {{implicit default constructor for 'member_template::A::B::C' first required here}} + NOE1 = noexcept(B::C()), + // expected-note@+1 {{implicit default constructor for 'member_template::A::B::D' first required here}} + NOE2 = noexcept(B::D()) + }; +}; +} + +namespace explicit_instantiation { +template struct X { + X(); // expected-note {{in instantiation of default member initializer 'explicit_instantiation::X::n' requested here}} + int n = T::error; // expected-error {{type 'float' cannot be used prior to '::' because it has no members}} +}; +template struct X; // ok +template X::X() {} +template struct X; // expected-note {{in instantiation of member function 'explicit_instantiation::X::X' requested here}} +} + +namespace local_class { +template void f() { + struct X { // expected-note {{in instantiation of default member initializer 'local_class::f()::X::n' requested here}} + int n = T::error; // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} + }; +} +void g() { f(); } // expected-note {{in instantiation of function template specialization 'local_class::f' requested here}} +} Index: cfe/trunk/test/SemaTemplate/instantiate-init.cpp =================================================================== --- cfe/trunk/test/SemaTemplate/instantiate-init.cpp +++ cfe/trunk/test/SemaTemplate/instantiate-init.cpp @@ -115,6 +115,7 @@ struct A { explicit A(int); }; // expected-note{{here}} template struct B { T a { 0 }; }; B b; + // expected-note@+1 {{in instantiation of default member initializer}} template struct C { T a = { 0 }; }; // expected-error{{explicit}} C c; // expected-note{{here}} }