DeclsTy Decls;
- ASTUnresolvedSet(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
- void operator=(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
-
friend class LazyASTUnresolvedSet;
public:
// Default ctor - Initialize to empty.
ASTVector() : Begin(0), End(0), Capacity(0, false) {}
+ ASTVector(ASTVector &&O) : Begin(O.Begin), End(O.End), Capacity(O.Capacity) {
+ O.Begin = O.End = 0;
+ O.Capacity.setPointer(0);
+ O.Capacity.setInt(false);
+ }
+
ASTVector(const ASTContext &C, unsigned N)
: Begin(0), End(0), Capacity(0, false) {
reserve(C, N);
}
+ ASTVector &operator=(ASTVector O) {
+ using std::swap;
+ swap(Begin, O.Begin);
+ swap(End, O.End);
+ swap(Capacity, O.Capacity);
+ return *this;
+ }
+
~ASTVector() {
if (std::is_class<T>::value) {
// Destroy the constructed elements in the vector.
"definition has no member %0">;
def note_module_odr_violation_possible_decl : Note<
"declaration of %0 does not match">;
+def err_module_odr_violation_different_definitions : Error<
+ "%q0 has different definitions in different modules; "
+ "%select{definition in module '%2' is here|defined here}1">;
+def note_module_odr_violation_different_definitions : Note<
+ "definition in module '%0' is here">;
+def err_module_odr_violation_different_instantiations : Error<
+ "instantiation of %q0 is different in different modules">;
} // let CategoryName
} // let Component
/// in the chain.
DeclUpdateOffsetsMap DeclUpdateOffsets;
+ /// \brief Declaration updates for already-loaded declarations that we need
+ /// to apply once we finish processing an import.
+ llvm::SmallVector<std::pair<serialization::GlobalDeclID, Decl*>, 16>
+ PendingUpdateRecords;
+
struct ReplacedDeclInfo {
ModuleFile *Mod;
uint64_t Offset;
/// once recursing loading has been completed.
llvm::SmallVector<NamedDecl *, 16> PendingOdrMergeChecks;
+ /// \brief Record definitions in which we found an ODR violation.
+ llvm::SmallDenseMap<CXXRecordDecl*, llvm::SmallVector<CXXRecordDecl*, 1>, 2>
+ PendingOdrMergeFailures;
+
+ /// \brief DeclContexts in which we have diagnosed an ODR violation.
+ llvm::SmallPtrSet<DeclContext*, 2> DiagnosedOdrMergeFailures;
+
/// \brief The set of Objective-C categories that have been deserialized
/// since the last time the declaration chains were linked.
llvm::SmallPtrSet<ObjCCategoryDecl *, 16> CategoriesDeserialized;
/// loaded, for which we will need to check for categories whenever a new
/// module is loaded.
SmallVector<ObjCInterfaceDecl *, 16> ObjCClassesLoaded;
-
+
+ /// \brief A mapping from a primary context for a declaration chain to the
+ /// other declarations of that entity that also have name lookup tables.
+ /// Used when we merge together two class definitions that have different
+ /// sets of declared special member functions.
+ llvm::DenseMap<const DeclContext*, SmallVector<const DeclContext*, 2>>
+ MergedLookups;
+
typedef llvm::DenseMap<Decl *, SmallVector<serialization::DeclID, 2> >
MergedDeclsMap;
/// \brief Retrieve the module file that owns the given declaration, or NULL
/// if the declaration is not from a module file.
ModuleFile *getOwningModuleFile(const Decl *D);
-
+
+ /// \brief Get the best name we know for the module that owns the given
+ /// declaration, or an empty string if the declaration is not from a module.
+ std::string getOwningModuleNameForDiagnostic(const Decl *D);
+
/// \brief Returns the source location for the decl \p ID.
SourceLocation getSourceLocationForDeclID(serialization::GlobalDeclID ID);
Decl *GetDecl(serialization::DeclID ID);
Decl *GetExternalDecl(uint32_t ID) override;
+ /// \brief Resolve a declaration ID into a declaration. Return 0 if it's not
+ /// been loaded yet.
+ Decl *GetExistingDecl(serialization::DeclID ID);
+
/// \brief Reads a declaration with the given local ID in the given module.
Decl *GetLocalDecl(ModuleFile &F, uint32_t LocalID) {
return GetDecl(getGlobalDeclID(F, LocalID));
void AddVersionTuple(const VersionTuple &Version, RecordDataImpl &Record);
/// \brief Mark a declaration context as needing an update.
- void AddUpdatedDeclContext(const DeclContext *DC) {
- UpdatedDeclContexts.insert(DC);
- }
+ void AddUpdatedDeclContext(const DeclContext *DC);
void RewriteDecl(const Decl *D) {
DeclsToRewrite.insert(D);
/// \brief Return llvm::StructType element number that corresponds to the
/// field FD.
unsigned getLLVMFieldNo(const FieldDecl *FD) const {
+ FD = FD->getCanonicalDecl();
assert(FieldInfo.count(FD) && "Invalid field for record!");
return FieldInfo.lookup(FD);
}
/// \brief Return the BitFieldInfo that corresponds to the field FD.
const CGBitFieldInfo &getBitFieldInfo(const FieldDecl *FD) const {
+ FD = FD->getCanonicalDecl();
assert(FD->isBitField() && "Invalid call for non-bit-field decl!");
llvm::DenseMap<const FieldDecl *, CGBitFieldInfo>::const_iterator
it = BitFields.find(FD);
void CGRecordLowering::setBitFieldInfo(
const FieldDecl *FD, CharUnits StartOffset, llvm::Type *StorageType) {
- CGBitFieldInfo &Info = BitFields[FD];
+ CGBitFieldInfo &Info = BitFields[FD->getCanonicalDecl()];
Info.IsSigned = FD->getType()->isSignedIntegerOrEnumerationType();
Info.Offset = (unsigned)(getFieldBitOffset(FD) - Context.toBits(StartOffset));
Info.Size = FD->getBitWidthValue(Context);
FieldType = getByteArrayType(LayoutSize);
setBitFieldInfo(Field, CharUnits::Zero(), FieldType);
}
- Fields[Field] = 0;
+ Fields[Field->getCanonicalDecl()] = 0;
llvm::Type *FieldType = getStorageType(Field);
// Conditionally update our storage type if we've got a new "better" one.
if (!StorageType ||
FieldTypes.push_back(Member->Data);
if (Member->Kind == MemberInfo::Field) {
if (Member->FD)
- Fields[Member->FD] = FieldTypes.size() - 1;
+ Fields[Member->FD->getCanonicalDecl()] = FieldTypes.size() - 1;
// A field without storage must be a bitfield.
if (!Member->Data)
setBitFieldInfo(Member->FD, Member->Offset, FieldTypes.back());
return false;
// Overwhelmingly common case: we have a direct initializer for this field.
- if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field))
+ if (CXXCtorInitializer *Init =
+ Info.AllBaseFields.lookup(Field->getCanonicalDecl()))
return Info.addFieldInitializer(Init);
// C++11 [class.base.init]p8:
if (Member->isBaseInitializer())
Info.AllBaseFields[Member->getBaseClass()->getAs<RecordType>()] = Member;
else {
- Info.AllBaseFields[Member->getAnyMember()] = Member;
+ Info.AllBaseFields[Member->getAnyMember()->getCanonicalDecl()] = Member;
if (IndirectFieldDecl *F = Member->getIndirectMember()) {
for (auto *C : F->chain()) {
return;
}
}
- IdealInits.push_back(Field);
+ IdealInits.push_back(Field->getCanonicalDecl());
}
static const void *GetKeyForBase(ASTContext &Context, QualType BaseType) {
if (!Member->isAnyMemberInitializer())
return GetKeyForBase(Context, QualType(Member->getBaseClass(), 0));
- return Member->getAnyMember();
+ return Member->getAnyMember()->getCanonicalDecl();
}
static void DiagnoseBaseOrMemInitializerOrder(
Init->setSourceOrder(i);
if (Init->isAnyMemberInitializer()) {
- FieldDecl *Field = Init->getAnyMember();
- if (CheckRedundantInit(*this, Init, Members[Field]) ||
+ const void *Key = GetKeyForMember(Context, Init);
+ if (CheckRedundantInit(*this, Init, Members[Key]) ||
CheckRedundantUnionInit(*this, Init, MemberUnions))
HadError = true;
} else if (Init->isBaseInitializer()) {
- const void *Key =
- GetKeyForBase(Context, QualType(Init->getBaseClass(), 0));
+ const void *Key = GetKeyForMember(Context, Init);
if (CheckRedundantInit(*this, Init, Members[Key]))
HadError = true;
} else {
UPD_CXX_ADDED_ANONYMOUS_NAMESPACE,
UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER,
UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION,
+ UPD_CXX_INSTANTIATED_CLASS_DEFINITION,
UPD_CXX_RESOLVED_EXCEPTION_SPEC,
UPD_CXX_DEDUCED_RETURN_TYPE,
UPD_DECL_MARKED_USED,
(const unsigned char *)Blob.data() + sizeof(uint32_t),
(const unsigned char *)Blob.data(),
ASTDeclContextNameLookupTrait(*this, F));
- if (ID == PREDEF_DECL_TRANSLATION_UNIT_ID) { // Is it the TU?
- DeclContext *TU = Context.getTranslationUnitDecl();
- F.DeclContextInfos[TU].NameLookupTableData = Table;
- TU->setHasExternalVisibleStorage(true);
- } else if (Decl *D = DeclsLoaded[ID - NUM_PREDEF_DECL_IDS]) {
+ if (Decl *D = GetExistingDecl(ID)) {
auto *DC = cast<DeclContext>(D);
DC->getPrimaryContext()->setHasExternalVisibleStorage(true);
auto *&LookupTable = F.DeclContextInfos[DC].NameLookupTableData;
+ // FIXME: There should never be an existing lookup table.
delete LookupTable;
LookupTable = Table;
} else
Error("invalid DECL_UPDATE_OFFSETS block in AST file");
return Failure;
}
- // FIXME: If we've already loaded the decl, perform the updates now.
- for (unsigned I = 0, N = Record.size(); I != N; I += 2)
- DeclUpdateOffsets[getGlobalDeclID(F, Record[I])]
- .push_back(std::make_pair(&F, Record[I+1]));
+ for (unsigned I = 0, N = Record.size(); I != N; I += 2) {
+ GlobalDeclID ID = getGlobalDeclID(F, Record[I]);
+ DeclUpdateOffsets[ID].push_back(std::make_pair(&F, Record[I + 1]));
+
+ // If we've already loaded the decl, perform the updates when we finish
+ // loading this block.
+ if (Decl *D = GetExistingDecl(ID))
+ PendingUpdateRecords.push_back(std::make_pair(ID, D));
+ }
break;
}
DeserializationListener->DeclRead(PREDEF_DECL_TRANSLATION_UNIT_ID,
Context.getTranslationUnitDecl());
- // Make sure we load the declaration update records for the translation unit,
- // if there are any.
- loadDeclUpdateRecords(PREDEF_DECL_TRANSLATION_UNIT_ID,
- Context.getTranslationUnitDecl());
-
+ // For any declarations we have already loaded, load any update records.
+ {
+ // We're not back to a consistent state until all our pending update
+ // records have been loaded. There can be interdependencies between them.
+ Deserializing SomeUpdateRecords(this);
+ ReadingKindTracker ReadingKind(Read_Decl, *this);
+
+ // Make sure we load the declaration update records for the translation
+ // unit, if there are any.
+ // FIXME: Is this necessary any more?
+ loadDeclUpdateRecords(PREDEF_DECL_TRANSLATION_UNIT_ID,
+ Context.getTranslationUnitDecl());
+
+ for (auto &Update : PendingUpdateRecords)
+ loadDeclUpdateRecords(Update.first, Update.second);
+ PendingUpdateRecords.clear();
+ }
+
// FIXME: Find a better way to deal with collisions between these
// built-in types. Right now, we just ignore the problem.
return GetDecl(ID);
}
-uint64_t ASTReader::readCXXBaseSpecifiers(ModuleFile &M, const RecordData &Record,
- unsigned &Idx){
- if (Idx >= Record.size())
+uint64_t ASTReader::readCXXBaseSpecifiers(ModuleFile &M,
+ const RecordData &Record,
+ unsigned &Idx) {
+ if (Idx >= Record.size() || Record[Idx] > M.LocalNumCXXBaseSpecifiers) {
+ Error("malformed AST file: missing C++ base specifier");
return 0;
-
+ }
+
unsigned LocalID = Record[Idx++];
return getGlobalBitOffset(M, M.CXXBaseSpecifiersOffsets[LocalID - 1]);
}
unsigned Code = Cursor.ReadCode();
unsigned RecCode = Cursor.readRecord(Code, Record);
if (RecCode != DECL_CXX_BASE_SPECIFIERS) {
- Error("Malformed AST file: missing C++ base specifiers");
+ Error("malformed AST file: missing C++ base specifiers");
return 0;
}
SourceLocation ASTReader::getSourceLocationForDeclID(GlobalDeclID ID) {
if (ID < NUM_PREDEF_DECL_IDS)
return SourceLocation();
-
+
unsigned Index = ID - NUM_PREDEF_DECL_IDS;
if (Index > DeclsLoaded.size()) {
Error("declaration ID out-of-range for AST file");
return SourceLocation();
}
-
+
if (Decl *D = DeclsLoaded[Index])
return D->getLocation();
return ReadSourceLocation(*Rec.F, RawLocation);
}
-Decl *ASTReader::GetDecl(DeclID ID) {
- if (ID < NUM_PREDEF_DECL_IDS) {
+Decl *ASTReader::GetExistingDecl(DeclID ID) {
+ if (ID < NUM_PREDEF_DECL_IDS) {
switch ((PredefinedDeclIDs)ID) {
case PREDEF_DECL_NULL_ID:
return 0;
-
+
case PREDEF_DECL_TRANSLATION_UNIT_ID:
return Context.getTranslationUnitDecl();
-
+
case PREDEF_DECL_OBJC_ID_ID:
return Context.getObjCIdDecl();
case PREDEF_DECL_OBJC_CLASS_ID:
return Context.getObjCClassDecl();
-
+
case PREDEF_DECL_OBJC_PROTOCOL_ID:
return Context.getObjCProtocolDecl();
-
+
case PREDEF_DECL_INT_128_ID:
return Context.getInt128Decl();
case PREDEF_DECL_UNSIGNED_INT_128_ID:
return Context.getUInt128Decl();
-
+
case PREDEF_DECL_OBJC_INSTANCETYPE_ID:
return Context.getObjCInstanceTypeDecl();
return Context.getBuiltinVaListDecl();
}
}
-
+
unsigned Index = ID - NUM_PREDEF_DECL_IDS;
if (Index >= DeclsLoaded.size()) {
Error("declaration ID out-of-range for AST file");
return 0;
}
-
+
+ return DeclsLoaded[Index];
+}
+
+Decl *ASTReader::GetDecl(DeclID ID) {
+ if (ID < NUM_PREDEF_DECL_IDS)
+ return GetExistingDecl(ID);
+
+ unsigned Index = ID - NUM_PREDEF_DECL_IDS;
+
+ if (Index >= DeclsLoaded.size()) {
+ assert(0 && "declaration ID out-of-range for AST file");
+ Error("declaration ID out-of-range for AST file");
+ return 0;
+ }
+
if (!DeclsLoaded[Index]) {
ReadDeclRecord(ID);
if (DeserializationListener)
Contexts.push_back(DC);
if (DC->isNamespace()) {
- MergedDeclsMap::iterator Merged
- = MergedDecls.find(const_cast<Decl *>(cast<Decl>(DC)));
+ auto Merged = MergedDecls.find(const_cast<Decl *>(cast<Decl>(DC)));
if (Merged != MergedDecls.end()) {
for (unsigned I = 0, N = Merged->second.size(); I != N; ++I)
Contexts.push_back(cast<DeclContext>(GetDecl(Merged->second[I])));
}
}
-
+ if (isa<CXXRecordDecl>(DC)) {
+ auto Merged = MergedLookups.find(DC);
+ if (Merged != MergedLookups.end())
+ Contexts.insert(Contexts.end(), Merged->second.begin(),
+ Merged->second.end());
+ }
+
DeclContextNameLookupVisitor Visitor(*this, Contexts, Name, Decls);
// If we can definitively determine which module file to look into,
}
}
+std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) {
+ // If we know the owning module, use it.
+ if (Module *M = D->getOwningModule())
+ return M->getFullModuleName();
+
+ // Otherwise, use the name of the top-level module the decl is within.
+ if (ModuleFile *M = getOwningModuleFile(D))
+ return M->ModuleName;
+
+ // Not from a module.
+ return "";
+}
+
void ASTReader::finishPendingActions() {
while (!PendingIdentifierInfos.empty() || !PendingDeclChains.empty() ||
!PendingMacroIDs.empty() || !PendingDeclContextInfos.empty() ||
Info.D->setDeclContextsImpl(SemaDC, LexicalDC, getContext());
}
+ // Trigger the import of the full definition of each class that had any
+ // odr-merging problems, so we can produce better diagnostics for them.
+ for (auto &Merge : PendingOdrMergeFailures) {
+ Merge.first->buildLookup();
+ Merge.first->decls_begin();
+ Merge.first->bases_begin();
+ Merge.first->vbases_begin();
+ for (auto *RD : Merge.second) {
+ RD->decls_begin();
+ RD->bases_begin();
+ RD->vbases_begin();
+ }
+ }
+
// For each declaration from a merged context, check that the canonical
// definition of that context also contains a declaration of the same
// entity.
if (!Found) {
D->setInvalidDecl();
- Module *CanonDefModule = cast<Decl>(CanonDef)->getOwningModule();
+ std::string CanonDefModule =
+ getOwningModuleNameForDiagnostic(cast<Decl>(CanonDef));
Diag(D->getLocation(), diag::err_module_odr_violation_missing_decl)
- << D << D->getOwningModule()->getFullModuleName()
- << CanonDef << !CanonDefModule
- << (CanonDefModule ? CanonDefModule->getFullModuleName() : "");
+ << D << getOwningModuleNameForDiagnostic(D)
+ << CanonDef << CanonDefModule.empty() << CanonDefModule;
if (Candidates.empty())
Diag(cast<Decl>(CanonDef)->getLocation(),
diag::note_module_odr_violation_possible_decl)
<< Candidates[I];
}
+
+ DiagnosedOdrMergeFailures.insert(CanonDef);
}
}
}
MD->setLazyBody(PB->second);
}
PendingBodies.clear();
+
+ // Issue any pending ODR-failure diagnostics.
+ for (auto &Merge : PendingOdrMergeFailures) {
+ if (!DiagnosedOdrMergeFailures.insert(Merge.first))
+ continue;
+
+ bool Diagnosed = false;
+ for (auto *RD : Merge.second) {
+ // Multiple different declarations got merged together; tell the user
+ // where they came from.
+ if (Merge.first != RD) {
+ // FIXME: Walk the definition, figure out what's different,
+ // and diagnose that.
+ if (!Diagnosed) {
+ std::string Module = getOwningModuleNameForDiagnostic(Merge.first);
+ Diag(Merge.first->getLocation(),
+ diag::err_module_odr_violation_different_definitions)
+ << Merge.first << Module.empty() << Module;
+ Diagnosed = true;
+ }
+
+ Diag(RD->getLocation(),
+ diag::note_module_odr_violation_different_definitions)
+ << getOwningModuleNameForDiagnostic(RD);
+ }
+ }
+
+ if (!Diagnosed) {
+ // All definitions are updates to the same declaration. This happens if a
+ // module instantiates the declaration of a class template specialization
+ // and two or more other modules instantiate its definition.
+ //
+ // FIXME: Indicate which modules had instantiations of this definition.
+ // FIXME: How can this even happen?
+ Diag(Merge.first->getLocation(),
+ diag::err_module_odr_violation_different_instantiations)
+ << Merge.first;
+ }
+ }
+ PendingOdrMergeFailures.clear();
}
void ASTReader::FinishedDeserializing() {
Module *readModule(const RecordData &R, unsigned &I) {
return Reader.getSubmodule(readSubmoduleID(R, I));
}
-
+
+ void ReadCXXRecordDefinition(CXXRecordDecl *D);
void ReadCXXDefinitionData(struct CXXRecordDecl::DefinitionData &Data,
const RecordData &R, unsigned &I);
+ void MergeDefinitionData(CXXRecordDecl *D,
+ struct CXXRecordDecl::DefinitionData &NewDD);
/// \brief RAII class used to capture the first ID within a redeclaration
/// chain and to introduce it into the list of pending redeclaration chains
} else
TD->NamedDeclOrQualifier = ReadDeclAs<NamedDecl>(Record, Idx);
- mergeRedeclarable(TD, Redecl);
+ if (!isa<CXXRecordDecl>(TD))
+ mergeRedeclarable(TD, Redecl);
return Redecl;
}
switch ((FunctionDecl::TemplatedKind)Record[Idx++]) {
case FunctionDecl::TK_NonTemplate:
- mergeRedeclarable(FD, Redecl);
+ mergeRedeclarable(FD, Redecl);
break;
case FunctionDecl::TK_FunctionTemplate:
+ // Merged when we merge the template.
FD->setDescribedFunctionTemplate(ReadDeclAs<FunctionTemplateDecl>(Record,
Idx));
break;
SourceLocation POI = ReadSourceLocation(Record, Idx);
FD->setInstantiationOfMemberFunction(Reader.getContext(), InstFD, TSK);
FD->getMemberSpecializationInfo()->setPointOfInstantiation(POI);
+ mergeRedeclarable(FD, Redecl);
break;
}
case FunctionDecl::TK_FunctionTemplateSpecialization: {
FD->setDependentTemplateSpecialization(Reader.getContext(),
TemplDecls, TemplArgs);
+
+ // FIXME: Merging.
break;
}
}
}
}
+void ASTDeclReader::MergeDefinitionData(
+ CXXRecordDecl *D, struct CXXRecordDecl::DefinitionData &MergeDD) {
+ assert(D->DefinitionData && "merging class definition into non-definition");
+ auto &DD = *D->DefinitionData;
+
+ // If the new definition has new special members, let the name lookup
+ // code know that it needs to look in the new definition too.
+ if ((MergeDD.DeclaredSpecialMembers & ~DD.DeclaredSpecialMembers) &&
+ DD.Definition != MergeDD.Definition) {
+ Reader.MergedLookups[DD.Definition].push_back(MergeDD.Definition);
+ DD.Definition->setHasExternalVisibleStorage();
+ }
+
+ // FIXME: Move this out into a .def file?
+ // FIXME: Issue a diagnostic on a mismatched MATCH_FIELD, rather than
+ // asserting; this can happen in the case of an ODR violation.
+ bool DetectedOdrViolation = false;
+#define OR_FIELD(Field) DD.Field |= MergeDD.Field;
+#define MATCH_FIELD(Field) \
+ DetectedOdrViolation |= DD.Field != MergeDD.Field; \
+ OR_FIELD(Field)
+ MATCH_FIELD(UserDeclaredConstructor)
+ MATCH_FIELD(UserDeclaredSpecialMembers)
+ MATCH_FIELD(Aggregate)
+ MATCH_FIELD(PlainOldData)
+ MATCH_FIELD(Empty)
+ MATCH_FIELD(Polymorphic)
+ MATCH_FIELD(Abstract)
+ MATCH_FIELD(IsStandardLayout)
+ MATCH_FIELD(HasNoNonEmptyBases)
+ MATCH_FIELD(HasPrivateFields)
+ MATCH_FIELD(HasProtectedFields)
+ MATCH_FIELD(HasPublicFields)
+ MATCH_FIELD(HasMutableFields)
+ MATCH_FIELD(HasVariantMembers)
+ MATCH_FIELD(HasOnlyCMembers)
+ MATCH_FIELD(HasInClassInitializer)
+ MATCH_FIELD(HasUninitializedReferenceMember)
+ MATCH_FIELD(NeedOverloadResolutionForMoveConstructor)
+ MATCH_FIELD(NeedOverloadResolutionForMoveAssignment)
+ MATCH_FIELD(NeedOverloadResolutionForDestructor)
+ MATCH_FIELD(DefaultedMoveConstructorIsDeleted)
+ MATCH_FIELD(DefaultedMoveAssignmentIsDeleted)
+ MATCH_FIELD(DefaultedDestructorIsDeleted)
+ OR_FIELD(HasTrivialSpecialMembers)
+ OR_FIELD(DeclaredNonTrivialSpecialMembers)
+ MATCH_FIELD(HasIrrelevantDestructor)
+ OR_FIELD(HasConstexprNonCopyMoveConstructor)
+ MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr)
+ OR_FIELD(HasConstexprDefaultConstructor)
+ MATCH_FIELD(HasNonLiteralTypeFieldsOrBases)
+ // ComputedVisibleConversions is handled below.
+ MATCH_FIELD(UserProvidedDefaultConstructor)
+ OR_FIELD(DeclaredSpecialMembers)
+ MATCH_FIELD(ImplicitCopyConstructorHasConstParam)
+ MATCH_FIELD(ImplicitCopyAssignmentHasConstParam)
+ OR_FIELD(HasDeclaredCopyConstructorWithConstParam)
+ OR_FIELD(HasDeclaredCopyAssignmentWithConstParam)
+ MATCH_FIELD(IsLambda)
+#undef OR_FIELD
+#undef MATCH_FIELD
+
+ if (DD.NumBases != MergeDD.NumBases || DD.NumVBases != MergeDD.NumVBases)
+ DetectedOdrViolation = true;
+ // FIXME: Issue a diagnostic if the base classes don't match when we come
+ // to lazily load them.
+
+ // FIXME: Issue a diagnostic if the list of conversion functions doesn't
+ // match when we come to lazily load them.
+ if (MergeDD.ComputedVisibleConversions && !DD.ComputedVisibleConversions) {
+ DD.VisibleConversions = std::move(MergeDD.VisibleConversions);
+ DD.ComputedVisibleConversions = true;
+ }
+
+ // FIXME: Issue a diagnostic if FirstFriend doesn't match when we come to
+ // lazily load it.
+
+ if (DD.IsLambda) {
+ // FIXME: ODR-checking for merging lambdas (this happens, for instance,
+ // when they occur within the body of a function template specialization).
+ }
+
+ if (DetectedOdrViolation)
+ Reader.PendingOdrMergeFailures[DD.Definition].push_back(MergeDD.Definition);
+}
+
+void ASTDeclReader::ReadCXXRecordDefinition(CXXRecordDecl *D) {
+ struct CXXRecordDecl::DefinitionData *DD;
+ ASTContext &C = Reader.getContext();
+
+ // Determine whether this is a lambda closure type, so that we can
+ // allocate the appropriate DefinitionData structure.
+ bool IsLambda = Record[Idx++];
+ if (IsLambda)
+ DD = new (C) CXXRecordDecl::LambdaDefinitionData(D, 0, false, false,
+ LCD_None);
+ else
+ DD = new (C) struct CXXRecordDecl::DefinitionData(D);
+
+ ReadCXXDefinitionData(*DD, Record, Idx);
+
+ // If we're reading an update record, we might already have a definition for
+ // this record. If so, just merge into it.
+ if (D->DefinitionData) {
+ MergeDefinitionData(D, *DD);
+ return;
+ }
+
+ // Propagate the DefinitionData pointer to the canonical declaration, so
+ // that all other deserialized declarations will see it.
+ CXXRecordDecl *Canon = D->getCanonicalDecl();
+ if (Canon == D) {
+ D->DefinitionData = DD;
+ D->IsCompleteDefinition = true;
+ } else if (!Canon->DefinitionData) {
+ Canon->DefinitionData = D->DefinitionData = DD;
+ D->IsCompleteDefinition = true;
+
+ // Note that we have deserialized a definition. Any declarations
+ // deserialized before this one will be be given the DefinitionData
+ // pointer at the end.
+ Reader.PendingDefinitions.insert(D);
+ } else {
+ // We have already deserialized a definition of this record. This
+ // definition is no longer really a definition. Note that the pre-existing
+ // definition is the *real* definition.
+ Reader.MergedDeclContexts.insert(
+ std::make_pair(D, Canon->DefinitionData->Definition));
+ D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
+ D->IsCompleteDefinition = false;
+ MergeDefinitionData(D, *DD);
+ }
+}
+
ASTDeclReader::RedeclarableResult
ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
RedeclarableResult Redecl = VisitRecordDeclImpl(D);
ASTContext &C = Reader.getContext();
- bool WasDefinition = Record[Idx++];
- if (WasDefinition) {
- // Determine whether this is a lambda closure type, so that we can
- // allocate the appropriate DefinitionData structure.
- bool IsLambda = Record[Idx++];
- if (IsLambda)
- D->DefinitionData = new (C) CXXRecordDecl::LambdaDefinitionData(D, 0,
- false,
- false, LCD_None);
- else
- D->DefinitionData = new (C) struct CXXRecordDecl::DefinitionData(D);
-
- ReadCXXDefinitionData(*D->DefinitionData, Record, Idx);
-
- // Propagate the DefinitionData pointer to the canonical declaration, so
- // that all other deserialized declarations will see it.
- CXXRecordDecl *Canon = D->getCanonicalDecl();
- if (Canon == D) {
- // Nothing to do.
- } else if (!Canon->DefinitionData) {
- Canon->DefinitionData = D->DefinitionData;
-
- // Note that we have deserialized a definition. Any declarations
- // deserialized before this one will be be given the DefinitionData
- // pointer at the end.
- Reader.PendingDefinitions.insert(D);
- } else {
- // We have already deserialized a definition of this record. This
- // definition is no longer really a definition. Note that the pre-existing
- // definition is the *real* definition.
- // FIXME: Check DefinitionData for consistency with prior definition.
- Reader.MergedDeclContexts.insert(
- std::make_pair(D, D->getCanonicalDecl()->DefinitionData->Definition));
- D->IsCompleteDefinition = false;
- D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
- }
- } else {
- // Propagate DefinitionData pointer from the canonical declaration.
- D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
- }
enum CXXRecKind {
CXXRecNotTemplate = 0, CXXRecTemplate, CXXRecMemberSpecialization
};
switch ((CXXRecKind)Record[Idx++]) {
case CXXRecNotTemplate:
+ mergeRedeclarable(D, Redecl);
break;
case CXXRecTemplate:
D->TemplateOrInstantiation = ReadDeclAs<ClassTemplateDecl>(Record, Idx);
MemberSpecializationInfo *MSI = new (C) MemberSpecializationInfo(RD, TSK);
MSI->setPointOfInstantiation(POI);
D->TemplateOrInstantiation = MSI;
+ mergeRedeclarable(D, Redecl);
break;
}
}
+ bool WasDefinition = Record[Idx++];
+ if (WasDefinition)
+ ReadCXXRecordDefinition(D);
+ else
+ // Propagate DefinitionData pointer from the canonical declaration.
+ D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
+
// Lazily load the key function to avoid deserializing every method so we can
// compute it.
if (WasDefinition) {
if (auto *CD = ReadDeclAs<CXXConstructorDecl>(Record, Idx))
D->setInheritedConstructor(CD);
D->IsExplicitSpecified = Record[Idx++];
+ // FIXME: We should defer loading this until we need the constructor's body.
std::tie(D->CtorInitializers, D->NumCtorInitializers) =
Reader.ReadCXXCtorInitializers(F, Record, Idx);
}
if (!CanonSpec->DefinitionData) {
CanonSpec->DefinitionData = D->DefinitionData;
} else {
- // FIXME: Check DefinitionData for consistency with prior definition
+ MergeDefinitionData(CanonSpec, *D->DefinitionData);
Reader.PendingDefinitions.erase(D);
Reader.MergedDeclContexts.insert(
std::make_pair(D, CanonSpec->DefinitionData->Definition));
// Fields with the same name and the same type match.
if (FieldDecl *FDX = dyn_cast<FieldDecl>(X)) {
FieldDecl *FDY = cast<FieldDecl>(Y);
- // FIXME: Diagnose if the types don't match. More generally, diagnose if we
- // get a declaration in a class definition that isn't in the canonical class
- // definition.
+ // FIXME: Diagnose if the types don't match.
// FIXME: Also check the bitwidth is odr-equivalent, if any.
return X->getASTContext().hasSameType(FDX->getType(), FDY->getType());
}
// If this declaration is from a merged context, make a note that we need to
// check that the canonical definition of that context contains the decl.
+ //
+ // FIXME: We should do something similar if we merge two definitions of the
+ // same template specialization into the same CXXRecordDecl.
if (Reader.MergedDeclContexts.count(D->getLexicalDeclContext()))
Reader.PendingOdrMergeChecks.push_back(D);
void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
const RecordData &Record) {
- unsigned Idx = 0;
while (Idx < Record.size()) {
switch ((DeclUpdateKind)Record[Idx++]) {
- case UPD_CXX_ADDED_IMPLICIT_MEMBER:
- cast<CXXRecordDecl>(D)->addedMember(Reader.ReadDecl(ModuleFile, Record, Idx));
+ case UPD_CXX_ADDED_IMPLICIT_MEMBER: {
+ Decl *MD = Reader.ReadDecl(ModuleFile, Record, Idx);
+ assert(MD && "couldn't read decl from update record");
+ cast<CXXRecordDecl>(D)->addedMember(MD);
break;
+ }
case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
// It will be added to the template's specializations set when loaded.
case UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION: {
FunctionDecl *FD = cast<FunctionDecl>(D);
- if (Reader.PendingBodies[FD])
+ if (Reader.PendingBodies[FD]) {
// FIXME: Maybe check for ODR violations.
- break;
+ // It's safe to stop now because this update record is always last.
+ return;
+ }
if (Record[Idx++])
FD->setImplicitlyInline();
break;
}
+ case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: {
+ auto *RD = cast<CXXRecordDecl>(D);
+ bool HadDefinition = RD->getDefinition();
+ ReadCXXRecordDefinition(RD);
+ // Visible update is handled separately.
+ uint64_t LexicalOffset = Record[Idx++];
+ if (!HadDefinition && LexicalOffset) {
+ RD->setHasExternalLexicalStorage(true);
+ Reader.ReadDeclContextStorage(ModuleFile, ModuleFile.DeclsCursor,
+ std::make_pair(LexicalOffset, 0),
+ ModuleFile.DeclContextInfos[RD]);
+ }
+
+ auto TSK = (TemplateSpecializationKind)Record[Idx++];
+ SourceLocation POI = Reader.ReadSourceLocation(ModuleFile, Record, Idx);
+ if (MemberSpecializationInfo *MSInfo =
+ RD->getMemberSpecializationInfo()) {
+ MSInfo->setTemplateSpecializationKind(TSK);
+ MSInfo->setPointOfInstantiation(POI);
+ } else {
+ ClassTemplateSpecializationDecl *Spec =
+ cast<ClassTemplateSpecializationDecl>(RD);
+ Spec->setTemplateSpecializationKind(TSK);
+ Spec->setPointOfInstantiation(POI);
+ }
+
+ RD->setTagKind((TagTypeKind)Record[Idx++]);
+ RD->setLocation(Reader.ReadSourceLocation(ModuleFile, Record, Idx));
+ RD->setLocStart(Reader.ReadSourceLocation(ModuleFile, Record, Idx));
+ RD->setRBraceLoc(Reader.ReadSourceLocation(ModuleFile, Record, Idx));
+
+ if (Record[Idx++]) {
+ AttrVec Attrs;
+ Reader.ReadAttributes(F, Attrs, Record, Idx);
+ D->setAttrsImpl(Attrs, Reader.getContext());
+ }
+ break;
+ }
+
case UPD_CXX_RESOLVED_EXCEPTION_SPEC: {
auto *FD = cast<FunctionDecl>(D);
auto *FPT = FD->getType()->castAs<FunctionProtoType>();
};
} // end anonymous namespace
+template<typename Visitor>
+static void visitLocalLookupResults(const DeclContext *ConstDC,
+ bool NeedToReconcileExternalVisibleStorage,
+ Visitor AddLookupResult) {
+ // FIXME: We need to build the lookups table, which is logically const.
+ DeclContext *DC = const_cast<DeclContext*>(ConstDC);
+ assert(DC == DC->getPrimaryContext() && "only primary DC has lookup table");
+
+ SmallVector<DeclarationName, 16> ExternalNames;
+ for (auto &Lookup : *DC->buildLookup()) {
+ if (Lookup.second.hasExternalDecls() ||
+ NeedToReconcileExternalVisibleStorage) {
+ // We don't know for sure what declarations are found by this name,
+ // because the external source might have a different set from the set
+ // that are in the lookup map, and we can't update it now without
+ // risking invalidating our lookup iterator. So add it to a queue to
+ // deal with later.
+ ExternalNames.push_back(Lookup.first);
+ continue;
+ }
+
+ AddLookupResult(Lookup.first, Lookup.second.getLookupResult());
+ }
+
+ // Add the names we needed to defer. Note, this shouldn't add any new decls
+ // to the list we need to serialize: any new declarations we find here should
+ // be imported from an external source.
+ // FIXME: What if the external source isn't an ASTReader?
+ for (const auto &Name : ExternalNames)
+ AddLookupResult(Name, DC->lookup(Name));
+}
+
+void ASTWriter::AddUpdatedDeclContext(const DeclContext *DC) {
+ if (UpdatedDeclContexts.insert(DC) && WritingAST) {
+ // Ensure we emit all the visible declarations.
+ visitLocalLookupResults(DC, DC->NeedToReconcileExternalVisibleStorage,
+ [&](DeclarationName Name,
+ DeclContext::lookup_const_result Result) {
+ for (auto *Decl : Result)
+ GetDeclRef(Decl);
+ });
+ }
+}
+
uint32_t
ASTWriter::GenerateNameLookupTable(const DeclContext *DC,
llvm::SmallVectorImpl<char> &LookupTable) {
assert(!DC->LookupPtr.getInt() && "must call buildLookups first");
- assert(DC == DC->getPrimaryContext() && "only primary DC has lookup table");
llvm::OnDiskChainedHashTableGenerator<ASTDeclContextNameLookupTrait>
Generator;
SmallVector<NamedDecl *, 8> ConstructorDecls;
SmallVector<NamedDecl *, 4> ConversionDecls;
- auto AddLookupResult = [&](DeclarationName Name,
- DeclContext::lookup_result Result) {
+ visitLocalLookupResults(DC, DC->NeedToReconcileExternalVisibleStorage,
+ [&](DeclarationName Name,
+ DeclContext::lookup_result Result) {
if (Result.empty())
return;
ConstructorName = Name;
ConstructorDecls.append(Result.begin(), Result.end());
return;
+
case DeclarationName::CXXConversionFunctionName:
if (!ConversionName)
ConversionName = Name;
ConversionDecls.append(Result.begin(), Result.end());
return;
+
default:
break;
}
Generator.insert(Name, Result, Trait);
- };
-
- SmallVector<DeclarationName, 16> ExternalNames;
- for (auto &Lookup : *DC->getLookupPtr()) {
- if (Lookup.second.hasExternalDecls() ||
- DC->NeedToReconcileExternalVisibleStorage) {
- // We don't know for sure what declarations are found by this name,
- // because the external source might have a different set from the set
- // that are in the lookup map, and we can't update it now without
- // risking invalidating our lookup iterator. So add it to a queue to
- // deal with later.
- ExternalNames.push_back(Lookup.first);
- continue;
- }
-
- AddLookupResult(Lookup.first, Lookup.second.getLookupResult());
- }
-
- // Add the names we needed to defer. Note, this shouldn't add any new decls
- // to the list we need to serialize: any new declarations we find here should
- // be imported from an external source.
- // FIXME: What if the external source isn't an ASTReader?
- for (const auto &Name : ExternalNames)
- // FIXME: const_cast since OnDiskHashTable wants a non-const lookup result.
- AddLookupResult(Name, const_cast<DeclContext*>(DC)->lookup(Name));
+ });
// Add the constructors.
if (!ConstructorDecls.empty()) {
ConstructorDecls.end()),
Trait);
}
+
// Add the conversion functions.
if (!ConversionDecls.empty()) {
Generator.insert(ConversionName,
IEnd = Chain->MergedDecls.end();
I != IEnd; ++I) {
DeclID CanonID = I->first->isFromASTFile()? I->first->getGlobalID()
- : getDeclID(I->first);
+ : GetDeclRef(I->first);
assert(CanonID && "Merged declaration not known?");
Record.push_back(CanonID);
}
}
+ // If we saw any DeclContext updates before we started writing the AST file,
+ // make sure all visible decls in those DeclContexts are written out.
+ if (!UpdatedDeclContexts.empty()) {
+ auto OldUpdatedDeclContexts = std::move(UpdatedDeclContexts);
+ UpdatedDeclContexts.clear();
+ for (auto *DC : OldUpdatedDeclContexts)
+ AddUpdatedDeclContext(DC);
+ }
+
// Build a record containing all of the tentative definitions in this file, in
// TentativeDefinitions order. Generally, this record will be empty for
// headers.
Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed);
// Write the visible updates to DeclContexts.
- for (llvm::SmallPtrSet<const DeclContext *, 16>::iterator
- I = UpdatedDeclContexts.begin(),
- E = UpdatedDeclContexts.end();
- I != E; ++I)
- WriteDeclContextVisibleUpdate(*I);
+ for (auto *DC : UpdatedDeclContexts)
+ WriteDeclContextVisibleUpdate(DC);
if (!WritingModule) {
// Write the submodules that were imported, if any.
if (isRewritten(D))
continue; // The decl will be written completely,no need to store updates.
- OffsetsRecord.push_back(GetDeclRef(D));
- OffsetsRecord.push_back(Stream.GetCurrentBitNo());
-
bool HasUpdatedBody = false;
RecordData Record;
for (auto &Update : DeclUpdate.second) {
case UPD_CXX_ADDED_IMPLICIT_MEMBER:
case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE:
+ assert(Update.getDecl() && "no decl to add?");
Record.push_back(GetDeclRef(Update.getDecl()));
break;
HasUpdatedBody = true;
break;
+ case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: {
+ auto *RD = cast<CXXRecordDecl>(D);
+ AddUpdatedDeclContext(RD->getPrimaryContext());
+ AddCXXDefinitionData(RD, Record);
+ Record.push_back(WriteDeclContextLexicalBlock(
+ *Context, const_cast<CXXRecordDecl *>(RD)));
+
+ // This state is sometimes updated by template instantiation, when we
+ // switch from the specialization referring to the template declaration
+ // to it referring to the template definition.
+ if (auto *MSInfo = RD->getMemberSpecializationInfo()) {
+ Record.push_back(MSInfo->getTemplateSpecializationKind());
+ AddSourceLocation(MSInfo->getPointOfInstantiation(), Record);
+ } else {
+ auto *Spec = cast<ClassTemplateSpecializationDecl>(RD);
+ Record.push_back(Spec->getTemplateSpecializationKind());
+ AddSourceLocation(Spec->getPointOfInstantiation(), Record);
+ }
+ Record.push_back(RD->getTagKind());
+ AddSourceLocation(RD->getLocation(), Record);
+ AddSourceLocation(RD->getLocStart(), Record);
+ AddSourceLocation(RD->getRBraceLoc(), Record);
+
+ // Instantiation may change attributes; write them all out afresh.
+ Record.push_back(D->hasAttrs());
+ if (Record.back())
+ WriteAttributes(ArrayRef<const Attr*>(D->getAttrs().begin(),
+ D->getAttrs().size()), Record);
+
+ // FIXME: Ensure we don't get here for explicit instantiations.
+ break;
+ }
+
case UPD_CXX_RESOLVED_EXCEPTION_SPEC:
addExceptionSpec(
*this,
AddFunctionDefinition(Def, Record);
}
+ OffsetsRecord.push_back(GetDeclRef(D));
+ OffsetsRecord.push_back(Stream.GetCurrentBitNo());
+
Stream.EmitRecord(DECL_UPDATES, Record);
// Flush any statements that were written as part of this update record.
FlushStmts();
+
+ // Flush C++ base specifiers, if there are any.
+ FlushCXXBaseSpecifiers();
}
}
// A forward reference was mutated into a definition. Rewrite it.
// FIXME: This happens during template instantiation, should we
// have created a new definition decl instead ?
- RewriteDecl(RD);
+ assert(isTemplateInstantiation(RD->getTemplateSpecializationKind()) &&
+ "completed a tag from another module but not by instantiation?");
+ DeclUpdates[RD].push_back(
+ DeclUpdate(UPD_CXX_INSTANTIATED_CLASS_DEFINITION));
}
}
}
void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
VisitRecordDecl(D);
- Record.push_back(D->isThisDeclarationADefinition());
- if (D->isThisDeclarationADefinition())
- Writer.AddCXXDefinitionData(D, Record);
enum {
CXXRecNotTemplate = 0, CXXRecTemplate, CXXRecMemberSpecialization
Record.push_back(CXXRecNotTemplate);
}
+ Record.push_back(D->isThisDeclarationADefinition());
+ if (D->isThisDeclarationADefinition())
+ Writer.AddCXXDefinitionData(D, Record);
+
// Store (what we currently believe to be) the key function to avoid
// deserializing every method so we can compute it.
if (D->IsCompleteDefinition)
inline int instantiate_min() {
return min(1, 2);
}
+
+inline int instantiate_CtorInit(CtorInit<int> i = CtorInit<int>()) {
+ return i.a;
+}
template<typename T> T min(T a, T b) { return a < b ? a : b; }
extern decltype(min(1, 2)) instantiate_min_decl;
+
+template<typename T> struct CtorInit {
+ static int f() { return 0; }
+ int a;
+ CtorInit() : a(f()) {}
+};
void redeclDefinitionEmit(){}
typedef Outer<int>::Inner OuterIntInner_left;
+
+int defineListDoubleLeft() {
+ List<double> ld;
+ ld.push_back(0.0);
+ return ld.size;
+}
void redeclDefinitionEmit(){}
typedef Outer<int>::Inner OuterIntInner_right;
+
+int defineListDoubleRight() {
+ List<double> ld;
+ ld.push_back(0.0);
+ return ld.size;
+}
unsigned size;
};
+extern List<double> *instantiateListDoubleDeclaration;
+
namespace A {
class Y {
template <typename T> friend class WhereAmI;
// FIXME: When we have a syntax for modules in C++, use that.
@import cxx_irgen_top;
+
+// CHECK-DAG: call i32 @_ZN8CtorInitIiE1fEv(
+CtorInit<int> x;
+
@import cxx_irgen_left;
@import cxx_irgen_right;
N::Set<char> set_char;
set_char.insert('A');
+
+ List<double> list_double;
+ list_double.push_back(0.0);
}
void testPendingInstantiations() {