diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-12-08 02:01:17 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-12-08 02:01:17 +0000 |
commit | d5bc867f6597ee8d4eb31ea217934e436fc7c7e3 (patch) | |
tree | a37962c0e4a97a1448f104cdebc7b1c54f20c79d /lib | |
parent | 0ee69cc6d4fc547671303afbd715cb990e5c3af6 (diff) |
Implement C++03 [dcl.init]p5's checking for value-initialization of references
properly, rather than faking it up by pretending that a reference member makes
the default constructor non-trivial. That leads to rejects-valids when putting
such types inside unions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@169662 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/ASTImporter.cpp | 2 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 68 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderDecl.cpp | 1 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 1 |
5 files changed, 78 insertions, 6 deletions
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 9f9a1b98ce..35bc993f73 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1921,6 +1921,8 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.HasMutableFields = FromData.HasMutableFields; ToData.HasOnlyCMembers = FromData.HasOnlyCMembers; ToData.HasInClassInitializer = FromData.HasInClassInitializer; + ToData.HasUninitializedReferenceMember + = FromData.HasUninitializedReferenceMember; ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers; ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor; ToData.HasConstexprNonCopyMoveConstructor diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 210dcb6d33..d3a0c1ad4c 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -41,7 +41,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), HasMutableFields(false), HasOnlyCMembers(true), - HasInClassInitializer(false), + HasInClassInitializer(false), HasUninitializedReferenceMember(false), HasTrivialSpecialMembers(SMF_All), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), @@ -295,6 +295,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // Keep track of the presence of mutable fields. if (BaseClassDecl->hasMutableFields()) data().HasMutableFields = true; + + if (BaseClassDecl->hasUninitializedReferenceMember()) + data().HasUninitializedReferenceMember = true; } if (VBases.empty()) @@ -727,7 +730,8 @@ void CXXRecordDecl::addedMember(Decl *D) { data().PlainOldData = false; if (T->isReferenceType()) { - data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor; + if (!Field->hasInClassInitializer()) + data().HasUninitializedReferenceMember = true; // C++0x [class]p7: // A standard-layout class is a class that: @@ -866,6 +870,10 @@ void CXXRecordDecl::addedMember(Decl *D) { // parameter is of type 'const M&', 'const volatile M&' or 'M'. if (!FieldRec->hasCopyAssignmentWithConstParam()) data().ImplicitCopyAssignmentHasConstParam = false; + + if (FieldRec->hasUninitializedReferenceMember() && + !Field->hasInClassInitializer()) + data().HasUninitializedReferenceMember = true; } } else { // Base element type of field is a non-class type. diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 48b0d1f896..dcfd18b122 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -3623,6 +3623,22 @@ static void TryValueInitialization(Sema &S, if (NeedZeroInitialization) Sequence.AddZeroInitializationStep(Entity.getType()); + // C++03: + // -- if T is a non-union class type without a user-declared constructor, + // then every non-static data member and base class component of T is + // value-initialized; + // [...] A program that calls for [...] value-initialization of an + // entity of reference type is ill-formed. + // + // C++11 doesn't need this handling, because value-initialization does not + // occur recursively there, and the implicit default constructor is + // defined as deleted in the problematic cases. + if (!S.getLangOpts().CPlusPlus0x && + ClassDecl->hasUninitializedReferenceMember()) { + Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForReference); + return; + } + // If this is list-value-initialization, pass the empty init list on when // building the constructor call. This affects the semantics of a few // things (such as whether an explicit default constructor can be called). @@ -5443,6 +5459,43 @@ InitializationSequence::Perform(Sema &S, return CurInit; } +/// Somewhere within T there is an uninitialized reference subobject. +/// Dig it out and diagnose it. +bool DiagnoseUninitializedReference(Sema &S, SourceLocation Loc, QualType T) { + if (T->isReferenceType()) { + S.Diag(Loc, diag::err_reference_without_init) + << T.getNonReferenceType(); + return true; + } + + CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || !RD->hasUninitializedReferenceMember()) + return false; + + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); FI != FE; ++FI) { + if (FI->isUnnamedBitfield()) + continue; + + if (DiagnoseUninitializedReference(S, FI->getLocation(), FI->getType())) { + S.Diag(Loc, diag::note_value_initialization_here) << RD; + return true; + } + } + + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); + BI != BE; ++BI) { + if (DiagnoseUninitializedReference(S, BI->getLocStart(), BI->getType())) { + S.Diag(Loc, diag::note_value_initialization_here) << RD; + return true; + } + } + + return false; +} + + //===----------------------------------------------------------------------===// // Diagnose initialization failures //===----------------------------------------------------------------------===// @@ -5457,10 +5510,17 @@ bool InitializationSequence::Diagnose(Sema &S, switch (Failure) { case FK_TooManyInitsForReference: // FIXME: Customize for the initialized entity? - if (NumArgs == 0) - S.Diag(Kind.getLocation(), diag::err_reference_without_init) - << DestType.getNonReferenceType(); - else // FIXME: diagnostic below could be better! + if (NumArgs == 0) { + // Dig out the reference subobject which is uninitialized and diagnose it. + // If this is value-initialization, this could be nested some way within + // the target type. + assert(Kind.getKind() == InitializationKind::IK_Value || + DestType->isReferenceType()); + bool Diagnosed = + DiagnoseUninitializedReference(S, Kind.getLocation(), DestType); + assert(Diagnosed && "couldn't find uninitialized reference to diagnose"); + (void)Diagnosed; + } else // FIXME: diagnostic below could be better! S.Diag(Kind.getLocation(), diag::err_reference_has_multiple_inits) << SourceRange(Args[0]->getLocStart(), Args[NumArgs - 1]->getLocEnd()); break; diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index d201f539fa..8c1058e396 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1099,6 +1099,7 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasMutableFields = Record[Idx++]; Data.HasOnlyCMembers = Record[Idx++]; Data.HasInClassInitializer = Record[Idx++]; + Data.HasUninitializedReferenceMember = Record[Idx++]; Data.HasTrivialSpecialMembers = Record[Idx++]; Data.HasIrrelevantDestructor = Record[Idx++]; Data.HasConstexprNonCopyMoveConstructor = Record[Idx++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index b6f798921d..f89ae2ef12 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4588,6 +4588,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.HasMutableFields); Record.push_back(Data.HasOnlyCMembers); Record.push_back(Data.HasInClassInitializer); + Record.push_back(Data.HasUninitializedReferenceMember); Record.push_back(Data.HasTrivialSpecialMembers); Record.push_back(Data.HasIrrelevantDestructor); Record.push_back(Data.HasConstexprNonCopyMoveConstructor); |