Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -402,6 +402,19 @@ /// which have been declared but not yet defined. unsigned HasTrivialSpecialMembers : 6; + enum AllowConstDefInitKind { + ACDI_Unknown, + ACDI_Yes, + ACDI_No, + }; + unsigned AllowConstDefaultInit : 2; + AllowConstDefInitKind allowConstDefInitKind() { + return static_cast(AllowConstDefaultInit); + } + void setAllowConstDefInitKind(AllowConstDefInitKind Allow) { + AllowConstDefaultInit = Allow; + } + /// \brief The declared special members of this class which are known to be /// non-trivial. /// @@ -1270,6 +1283,15 @@ return !(data().HasTrivialSpecialMembers & SMF_Destructor); } + /// \brief Determine whether declaring a const variable with this type is ok + /// per core issue 253. + bool allowConstDefaultInitSlow() const; + bool allowConstDefaultInit() const { + if (data().allowConstDefInitKind() != DefinitionData::ACDI_Unknown) + return data().allowConstDefInitKind() == DefinitionData::ACDI_Yes; + return allowConstDefaultInitSlow(); + } + /// \brief Determine whether this class has a destructor which has no /// semantic effect. /// Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -2035,6 +2035,7 @@ = FromData.DefaultedMoveAssignmentIsDeleted; ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted; ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers; + ToData.AllowConstDefaultInit = FromData.AllowConstDefaultInit; ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor; ToData.HasConstexprNonCopyMoveConstructor = FromData.HasConstexprNonCopyMoveConstructor; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -59,6 +59,7 @@ DefaultedMoveAssignmentIsDeleted(false), DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All), + AllowConstDefaultInit(ACDI_Unknown), DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), @@ -392,6 +393,37 @@ return !forallBases([](const CXXRecordDecl *) { return true; }); } +bool CXXRecordDecl::allowConstDefaultInitSlow() const { + assert(getDefinition() && "only call this on completed records"); + if (hasUserProvidedDefaultConstructor()) { + data().setAllowConstDefInitKind(DefinitionData::ACDI_Yes); + return true; + } + for (const auto *F : fields()) { + if (F->hasInClassInitializer() || F->isMutable() || F->isUnnamedBitfield()) + continue; + if (CXXRecordDecl *FieldType = F->getType()->getAsCXXRecordDecl()) { + if (!FieldType->allowConstDefaultInit()) { + data().setAllowConstDefInitKind(DefinitionData::ACDI_No); + return false; + } + } else { + data().setAllowConstDefInitKind(DefinitionData::ACDI_No); + return false; + } + } + for (const auto& BI : bases()) { + const RecordType *RT = BI.getType()->getAs(); + CXXRecordDecl *Base = cast(RT->getDecl()); + if (!Base->allowConstDefaultInit()) { + data().setAllowConstDefInitKind(DefinitionData::ACDI_No); + return false; + } + } + data().setAllowConstDefInitKind(DefinitionData::ACDI_Yes); + return true; +} + bool CXXRecordDecl::isTriviallyCopyable() const { // C++0x [class]p5: // A trivially copyable class is a class that: Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -3515,18 +3515,23 @@ // If a program calls for the default initialization of an object // of a const-qualified type T, T shall be a class type with a // user-provided default constructor. + // C++ core issue 253 proposal: + // If the implicit default constructor initializes all subobjects, no + // initializer should be required. + // The 253 proposal is for example needed to process libstdc++ headers in 5.x. + CXXConstructorDecl *CtorDecl = cast(Best->Function); if (Kind.getKind() == InitializationKind::IK_Default && - Entity.getType().isConstQualified() && - !cast(Best->Function)->isUserProvided()) { - if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity)) - Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); - return; + Entity.getType().isConstQualified()) { + if (!CtorDecl->getParent()->allowConstDefaultInit()) { + if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity)) + Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + return; + } } // C++11 [over.match.list]p1: // In copy-list-initialization, if an explicit constructor is chosen, the // initializer is ill-formed. - CXXConstructorDecl *CtorDecl = cast(Best->Function); if (IsListInit && !Kind.AllowExplicit() && CtorDecl->isExplicit()) { Sequence.SetFailed(InitializationSequence::FK_ExplicitConstructor); return; Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1419,6 +1419,7 @@ Data.DefaultedMoveAssignmentIsDeleted = Record[Idx++]; Data.DefaultedDestructorIsDeleted = Record[Idx++]; Data.HasTrivialSpecialMembers = Record[Idx++]; + Data.AllowConstDefaultInit = Record[Idx++]; Data.DeclaredNonTrivialSpecialMembers = Record[Idx++]; Data.HasIrrelevantDestructor = Record[Idx++]; Data.HasConstexprNonCopyMoveConstructor = Record[Idx++]; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5554,6 +5554,7 @@ Record.push_back(Data.DefaultedMoveAssignmentIsDeleted); Record.push_back(Data.DefaultedDestructorIsDeleted); Record.push_back(Data.HasTrivialSpecialMembers); + Record.push_back(Data.AllowConstDefaultInit); Record.push_back(Data.DeclaredNonTrivialSpecialMembers); Record.push_back(Data.HasIrrelevantDestructor); Record.push_back(Data.HasConstexprNonCopyMoveConstructor); Index: test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp =================================================================== --- test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp +++ test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp @@ -116,6 +116,7 @@ namespace PR13492 { struct B { B() = default; + int field; }; void f() { Index: test/CXX/dcl.decl/dcl.init/p6.cpp =================================================================== --- test/CXX/dcl.decl/dcl.init/p6.cpp +++ test/CXX/dcl.decl/dcl.init/p6.cpp @@ -4,9 +4,9 @@ // If a program calls for the default initialization of an object of a // const-qualified type T, T shall be a class type with a -// user-provided default constructor. +// user-provided default constructor, except if T has no uninitialized fields. struct MakeNonPOD { MakeNonPOD(); }; -struct NoUserDefault : public MakeNonPOD { }; +struct NoUserDefault : public MakeNonPOD { int field; }; struct HasUserDefault { HasUserDefault(); }; void test_const_default_init() { @@ -16,7 +16,7 @@ } // rdar://8501008 -struct s0 {}; +struct s0 { int field; }; struct s1 { static const s0 foo; }; const struct s0 s1::foo; // expected-error{{default initialization of an object of const type 'const struct s0' without a user-provided default constructor}} Index: test/CXX/drs/dr4xx.cpp =================================================================== --- test/CXX/drs/dr4xx.cpp +++ test/CXX/drs/dr4xx.cpp @@ -1197,12 +1197,12 @@ int check6[ __is_trivially_assignable(B, const B&) ? 1 : -1]; } -namespace dr497 { // dr497: yes +namespace dr497 { // dr497: no, superseded by dr253 void before() { struct S { mutable int i; }; - const S cs; // expected-error {{default initialization}} + const S cs; int S::*pm = &S::i; cs.*pm = 88; // expected-error {{not assignable}} } Index: test/SemaCXX/attr-selectany.cpp =================================================================== --- test/SemaCXX/attr-selectany.cpp +++ test/SemaCXX/attr-selectany.cpp @@ -39,7 +39,9 @@ // The D3D11 headers do something like this. MSVC doesn't error on this at // all, even without the __declspec(selectany), in violation of the standard. // We fall back to a warning for selectany to accept headers. -struct SomeStruct {}; +struct SomeStruct { + int foo; +}; extern const __declspec(selectany) SomeStruct some_struct; // expected-warning {{default initialization of an object of const type 'const SomeStruct' without a user-provided default constructor is a Microsoft extension}} // It should be possible to redeclare variables that were defined Index: test/SemaCXX/constexpr-value-init.cpp =================================================================== --- test/SemaCXX/constexpr-value-init.cpp +++ test/SemaCXX/constexpr-value-init.cpp @@ -14,7 +14,7 @@ constexpr A a; // expected-error {{constant expression}} expected-note {{in call to 'A()'}} } -constexpr B b1; // expected-error {{without a user-provided default constructor}} +constexpr B b1; // ok constexpr B b2 = B(); // ok static_assert(b2.a.a == 1, ""); static_assert(b2.a.b == 2, ""); Index: test/SemaCXX/cxx0x-cursory-default-delete.cpp =================================================================== --- test/SemaCXX/cxx0x-cursory-default-delete.cpp +++ test/SemaCXX/cxx0x-cursory-default-delete.cpp @@ -11,6 +11,7 @@ non_const_copy& operator = (non_const_copy&) &; non_const_copy& operator = (non_const_copy&) &&; non_const_copy() = default; // expected-note {{not viable}} + int uninit_field; }; non_const_copy::non_const_copy(non_const_copy&) = default; // expected-note {{not viable}} non_const_copy& non_const_copy::operator = (non_const_copy&) & = default; // expected-note {{not viable}} @@ -30,6 +31,65 @@ ncc = cncc; // expected-error {{no viable overloaded}} }; +struct no_fields { }; +struct all_init { + int a = 0; + int b = 0; +}; +struct some_init { + int a = 0; + int b; + int c = 0; +}; +struct some_init_mutable { + int a = 0; + mutable int b; + int c = 0; +}; +struct some_init_def { + some_init_def() = default; + int a = 0; + int b; + int c = 0; +}; +struct some_init_ctor { + some_init_ctor(); + int a = 0; + int b; + int c = 0; +}; +struct sub_some_init : public some_init_def { }; +struct sub_some_init_ctor : public some_init_def { + sub_some_init_ctor(); +}; +struct sub_some_init_ctor2 : public some_init_ctor { +}; +struct some_init_container { + some_init_def sid; +}; +struct some_init_container_ctor { + some_init_container_ctor(); + some_init_def sid; +}; +struct no_fields_container { + no_fields nf; +}; + +void constobjs() { + const no_fields nf; // ok + const all_init ai; // ok + const some_init si; // expected-error {{default initialization of an object of const type 'const some_init' without a user-provided default constructor}} + const some_init_mutable sim; // ok + const some_init_def sid; // expected-error {{default initialization of an object of const type 'const some_init_def' without a user-provided default constructor}} + const some_init_ctor sic; // ok + const sub_some_init ssi; // expected-error {{default initialization of an object of const type 'const sub_some_init' without a user-provided default constructor}} + const sub_some_init_ctor ssic; // ok + const sub_some_init_ctor2 ssic2; // ok + const some_init_container sicon; // expected-error {{default initialization of an object of const type 'const some_init_container' without a user-provided default constructor}} + const some_init_container_ctor siconc; // ok + const no_fields_container nfc; // ok +} + struct non_const_derived : non_const_copy { non_const_derived(const non_const_derived&) = default; // expected-error {{requires it to be non-const}} non_const_derived& operator =(non_const_derived&) = default; Index: test/SemaCXX/illegal-member-initialization.cpp =================================================================== --- test/SemaCXX/illegal-member-initialization.cpp +++ test/SemaCXX/illegal-member-initialization.cpp @@ -7,6 +7,7 @@ }; struct B { + int field; }; struct X {