Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ 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: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6185,9 +6185,9 @@ "'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 default initialize non-static data member %0 before the end of the " + "outermost containing class %1">; def ext_in_class_initializer_non_constant : Extension< "in-class initializer for static data member is not a constant expression; " Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3916,6 +3916,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, @@ -6731,6 +6733,10 @@ const MultiLevelTemplateArgumentList &TemplateArgs, TemplateSpecializationKind TSK); + bool InstantiateField(SourceLocation PointOfInstantiation, + FieldDecl *Instantiation, FieldDecl *Pattern, + const MultiLevelTemplateArgumentList &TemplateArgs); + struct LateInstantiatedAttribute { const Attr *TmplAttr; LocalInstantiationScope *Scope; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ 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 @@ -3610,19 +3611,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); } @@ -8417,22 +8418,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()); @@ -8500,9 +8485,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()); @@ -10967,6 +10949,57 @@ 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); + + // 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 evaluate 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. + + // 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())) { + auto *InstantiatedClass = cast(ParentRD); + CXXRecordDecl *ClassPattern = + InstantiatedClass->getSpecializedTemplate()->getTemplatedDecl(); + DeclContext::lookup_result Lookup = + ClassPattern->lookup(Field->getDeclName()); + assert(Lookup.size() == 1); + FieldDecl *Pattern = cast(Lookup[0]); + if (InstantiateField(Loc, Field, Pattern, + getTemplateInstantiationArgs(Field))) + return ExprError(); + return CXXDefaultInitExpr::Create(Context, Loc, Field); + } + RecordDecl *OutermostClass = ParentRD; + for (DeclContext *DC = OutermostClass; !DC->isTranslationUnit(); + DC = DC->getParent()) { + if (auto *RD = dyn_cast(DC)) + OutermostClass = RD; + } + Diag(Field->getLocEnd(), diag::err_in_class_initializer_not_yet_parsed) + << Field << OutermostClass; + + return ExprError(); +} + void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { if (VD->isInvalidDecl()) return; Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ 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: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -1976,7 +1976,7 @@ if (FieldDecl *Field = dyn_cast(NewMember)) { Fields.push_back(Field); FieldDecl *OldField = cast(Member); - if (OldField->getInClassInitializer()) + if (OldField->hasInClassInitializer()) FieldsWithMemberInitializers.push_back(std::make_pair(OldField, Field)); } else if (EnumDecl *Enum = dyn_cast(NewMember)) { @@ -2030,14 +2030,14 @@ 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); + // If we haven't parsed the initializer yet, defer instantiation until + // later. + // FIXME: If we can tolerate defering some initializers, it shouldn't be + // hard to defer *all* field instantiation until later as described above. + if (OldInit) { + InstantiateField(Instantiation->getLocation(), NewField, OldField, + TemplateArgs); + } } } // Instantiate late parsed attributes, and attach them to their decls. @@ -2180,6 +2180,61 @@ 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::InstantiateField(SourceLocation PointOfInstantiation, + FieldDecl *Instantiation, FieldDecl *Pattern, + const MultiLevelTemplateArgumentList &TemplateArgs) { + // We only need to instantiate the initializer, the rest of the field was + // instantiated during InstantiateClass. + 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) { + // FIXME: Deduplicate code with SemaInit + RecordDecl *OutermostClass = Pattern->getParent(); + for (DeclContext *DC = OutermostClass; !DC->isTranslationUnit(); + DC = DC->getParent()) { + if (auto *RD = dyn_cast(DC)) + OutermostClass = RD; + } + Diag(Pattern->getLocEnd(), diag::err_in_class_initializer_not_yet_parsed) + << Pattern << OutermostClass; + Instantiation->setInvalidDecl(); + return true; + } + + // Instantiate the initializer. + ActOnStartCXXInClassMemberInitializer(); + 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); + return Init == nullptr; +} + namespace { /// \brief A partial specialization whose template arguments have matched /// a given template-id. Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -548,6 +548,24 @@ return nullptr; } +#if 0 + // Instantiate the in-class initializer if present. + llvm::errs() << "VisitFieldDecl\n"; + D->dump(); + if (D->hasInClassInitializer()) { + Expr *Init = D->getInClassInitializer(); + llvm::errs() << "Init\n"; + Init->dump(); + SemaRef.ActOnStartCXXInClassMemberInitializer(); + bool IsDirectInit = D->getInClassInitStyle() == ICIS_ListInit; + Init = SemaRef.SubstInitializer(Init, TemplateArgs, IsDirectInit).get(); + llvm::errs() << "instantiated Init\n"; + Init->dump(); + SemaRef.ActOnFinishCXXInClassMemberInitializer(Field, Init->getLocStart(), + Init); + } +#endif + SemaRef.InstantiateAttrs(TemplateArgs, D, Field, LateAttrs, StartingScope); if (Field->hasAttrs()) Index: test/SemaCXX/constant-expression-cxx11.cpp =================================================================== --- test/SemaCXX/constant-expression-cxx11.cpp +++ test/SemaCXX/constant-expression-cxx11.cpp @@ -1842,8 +1842,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 default initialize non-static data member 'k' before the end of the outermost containing class 'A'}} + 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: test/SemaCXX/implicit-exception-spec.cpp =================================================================== --- test/SemaCXX/implicit-exception-spec.cpp +++ test/SemaCXX/implicit-exception-spec.cpp @@ -17,31 +17,32 @@ // 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 default initialize non-static data member}} + // 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 default initialize non-static data member}} + // 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 default initialize non-static data member}} 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 = ExceptionIf::f(); - } inner; + int n = Inner().n; // expected-note {{implicit default constructor for 'InClassInitializers::Nested2::Inner' first required here}} + struct Inner { // expected-note {{declared here}} + int n = ExceptionIf::f(); // expected-error {{cannot default initialize non-static data member}} + // expected-note@-1 {{implicit default constructor for 'InClassInitializers::Nested2' first required here}} + } inner; // expected-note {{member is declared here}} }; } Index: test/SemaCXX/member-init.cpp =================================================================== --- test/SemaCXX/member-init.cpp +++ 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 default initialize non-static data member 'n' before the end of the outermost containing class 'Recurse'}} + b() ? + Recurse().n : // expected-note {{implicit default constructor for 'Recurse' first required here}} + k; }; struct UnknownBound { @@ -110,3 +113,35 @@ 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 default initialize non-static data member 'm1' before the end of the outermost containing class 'A'}} + }; + // 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 default initialize non-static data member 'm1' before the end of the outermost containing class 'A'}} + }; + // expected-note@+1 {{implicit default constructor for 'default_ctor::A::B' first required here}} + enum { NOE = noexcept(B()) }; +}; +}