Changeset View
Changeset View
Standalone View
Standalone View
clang/lib/Serialization/ASTReader.cpp
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
Show First 20 Lines • Show All 9,182 Lines • ▼ Show 20 Lines | void ASTReader::visitTopLevelModuleMaps( | ||||
} | } | ||||
} | } | ||||
std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) { | std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) { | ||||
// If we know the owning module, use it. | // If we know the owning module, use it. | ||||
if (Module *M = D->getImportedOwningModule()) | if (Module *M = D->getImportedOwningModule()) | ||||
return M->getFullModuleName(); | return M->getFullModuleName(); | ||||
// Otherwise, use the name of the top-level module the decl is within. | // Otherwise, use the name of the top-level module the decl is within. | ||||
if (ModuleFile *M = getOwningModuleFile(D)) | if (ModuleFile *M = getOwningModuleFile(D)) | ||||
return M->ModuleName; | return M->ModuleName; | ||||
vsapsai: At first I though this part was important for certain tests. But I guess I've messed up… | |||||
// Not from a module. | // Not from a module. | ||||
return {}; | return {}; | ||||
} | } | ||||
void ASTReader::finishPendingActions() { | void ASTReader::finishPendingActions() { | ||||
while (!PendingIdentifierInfos.empty() || !PendingFunctionTypes.empty() || | while (!PendingIdentifierInfos.empty() || !PendingFunctionTypes.empty() || | ||||
!PendingIncompleteDeclChains.empty() || !PendingDeclChains.empty() || | !PendingIncompleteDeclChains.empty() || !PendingDeclChains.empty() || | ||||
▲ Show 20 Lines • Show All 236 Lines • ▼ Show 20 Lines | void ASTReader::finishPendingActions() { | ||||
PendingBodies.clear(); | PendingBodies.clear(); | ||||
// Do some cleanup. | // Do some cleanup. | ||||
for (auto *ND : PendingMergedDefinitionsToDeduplicate) | for (auto *ND : PendingMergedDefinitionsToDeduplicate) | ||||
getContext().deduplicateMergedDefinitonsFor(ND); | getContext().deduplicateMergedDefinitonsFor(ND); | ||||
PendingMergedDefinitionsToDeduplicate.clear(); | PendingMergedDefinitionsToDeduplicate.clear(); | ||||
} | } | ||||
namespace clang { | |||||
class ODRDiagsEmitter { | |||||
public: | |||||
ODRDiagsEmitter(DiagnosticsEngine &Diags, const ASTContext &Context, | |||||
const LangOptions &LangOpts) | |||||
: Diags(Diags), Context(Context), LangOpts(LangOpts) {} | |||||
bool diagnoseMismatch(const FunctionDecl *FirstFunction, | |||||
const FunctionDecl *SecondFunction) const; | |||||
bool diagnoseMismatch(const EnumDecl *FirstEnum, | |||||
const EnumDecl *SecondEnum) const; | |||||
bool | |||||
diagnoseMismatch(const CXXRecordDecl *FirstRecord, | |||||
const CXXRecordDecl *SecondRecord, | |||||
const struct CXXRecordDecl::DefinitionData *SecondDD) const; | |||||
private: | |||||
using DeclHashes = llvm::SmallVector<std::pair<const Decl *, unsigned>, 4>; | |||||
// Used with err_module_odr_violation_mismatch_decl and | |||||
// note_module_odr_violation_mismatch_decl | |||||
// This list should be the same Decl's as in ODRHash::isDeclToBeProcessed | |||||
enum ODRMismatchDecl { | |||||
EndOfClass, | |||||
PublicSpecifer, | |||||
PrivateSpecifer, | |||||
ProtectedSpecifer, | |||||
StaticAssert, | |||||
Field, | |||||
CXXMethod, | |||||
TypeAlias, | |||||
TypeDef, | |||||
Var, | |||||
Friend, | |||||
FunctionTemplate, | |||||
Other | |||||
}; | |||||
struct DiffResult { | |||||
const Decl *FirstDecl = nullptr, *SecondDecl = nullptr; | |||||
ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; | |||||
}; | |||||
// If there is a diagnoseable difference, FirstDiffType and | |||||
// SecondDiffType will not be Other and FirstDecl and SecondDecl will be | |||||
// filled in if not EndOfClass. | |||||
static DiffResult FindTypeDiffs(DeclHashes &FirstHashes, | |||||
DeclHashes &SecondHashes); | |||||
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) const { | |||||
return Diags.Report(Loc, DiagID); | |||||
} | |||||
// Use this to diagnose that an unexpected Decl was encountered | |||||
// or no difference was detected. This causes a generic error | |||||
// message to be emitted. | |||||
void diagnoseSubMismatchUnexpected(DiffResult &DR, | |||||
const NamedDecl *FirstRecord, | |||||
StringRef FirstModule, | |||||
const NamedDecl *SecondRecord, | |||||
StringRef SecondModule) const; | |||||
void diagnoseSubMismatchDifferentDeclKinds(DiffResult &DR, | |||||
const NamedDecl *FirstRecord, | |||||
StringRef FirstModule, | |||||
const NamedDecl *SecondRecord, | |||||
StringRef SecondModule) const; | |||||
bool diagnoseSubMismatchField(const NamedDecl *FirstRecord, | |||||
StringRef FirstModule, StringRef SecondModule, | |||||
const FieldDecl *FirstField, | |||||
const FieldDecl *SecondField) const; | |||||
bool diagnoseSubMismatchTypedef(const NamedDecl *FirstRecord, | |||||
StringRef FirstModule, StringRef SecondModule, | |||||
const TypedefNameDecl *FirstTD, | |||||
const TypedefNameDecl *SecondTD, | |||||
bool IsTypeAlias) const; | |||||
bool diagnoseSubMismatchVar(const NamedDecl *FirstRecord, | |||||
StringRef FirstModule, StringRef SecondModule, | |||||
const VarDecl *FirstVD, | |||||
const VarDecl *SecondVD) const; | |||||
private: | |||||
DiagnosticsEngine &Diags; | |||||
const ASTContext &Context; | |||||
const LangOptions &LangOpts; | |||||
}; | |||||
} // namespace clang | |||||
static unsigned computeODRHash(QualType Ty) { | static unsigned computeODRHash(QualType Ty) { | ||||
ODRHash Hasher; | ODRHash Hasher; | ||||
Hasher.AddQualType(Ty); | Hasher.AddQualType(Ty); | ||||
return Hasher.CalculateHash(); | return Hasher.CalculateHash(); | ||||
} | } | ||||
static unsigned computeODRHash(const Stmt *S) { | static unsigned computeODRHash(const Stmt *S) { | ||||
ODRHash Hasher; | ODRHash Hasher; | ||||
Show All 9 Lines | |||||
} | } | ||||
static unsigned computeODRHash(const TemplateArgument &TA) { | static unsigned computeODRHash(const TemplateArgument &TA) { | ||||
ODRHash Hasher; | ODRHash Hasher; | ||||
Hasher.AddTemplateArgument(TA); | Hasher.AddTemplateArgument(TA); | ||||
return Hasher.CalculateHash(); | return Hasher.CalculateHash(); | ||||
} | } | ||||
static unsigned computeODRHash(const TemplateParameterList *TPL) { | static unsigned computeODRHash(const TemplateParameterList *TPL) { | ||||
assert(TPL); | assert(TPL); | ||||
ODRHash Hasher; | ODRHash Hasher; | ||||
Hasher.AddTemplateParameterList(TPL); | Hasher.AddTemplateParameterList(TPL); | ||||
return Hasher.CalculateHash(); | return Hasher.CalculateHash(); | ||||
} | } | ||||
static std::string getOwningModuleNameForDiagnostic(const Decl *D) { | |||||
We add a static method with the same name of the original one but we don't remove the original one. It looks odd. Could we remove the odd one? ChuanqiXu: We add a static method with the same name of the original one but we don't remove the original… | |||||
Hmm, I've tried removing the old one ASTReader::getOwningModuleNameForDiagnostic but some tests were failing. Now all the tests seem to be fine. I'll remove ASTReader::getOwningModuleNameForDiagnostic and we'll see if pre-commit tests are happy. vsapsai: Hmm, I've tried removing the old one `ASTReader::getOwningModuleNameForDiagnostic` but some… | |||||
// If we know the owning module, use it. | |||||
if (Module *M = D->getImportedOwningModule()) | |||||
return M->getFullModuleName(); | |||||
// Not from a module. | |||||
return {}; | |||||
} | |||||
void ASTReader::diagnoseOdrViolations() { | void ASTReader::diagnoseOdrViolations() { | ||||
if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && | if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && | ||||
PendingFunctionOdrMergeFailures.empty() && | PendingFunctionOdrMergeFailures.empty() && | ||||
PendingEnumOdrMergeFailures.empty()) | PendingEnumOdrMergeFailures.empty()) | ||||
return; | return; | ||||
// Trigger the import of the full definition of each class that had any | // Trigger the import of the full definition of each class that had any | ||||
// odr-merging problems, so we can produce better diagnostics for them. | // odr-merging problems, so we can produce better diagnostics for them. | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | void ASTReader::diagnoseOdrViolations() { | ||||
if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && | if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && | ||||
EnumOdrMergeFailures.empty()) | EnumOdrMergeFailures.empty()) | ||||
return; | return; | ||||
// Ensure we don't accidentally recursively enter deserialization while | // Ensure we don't accidentally recursively enter deserialization while | ||||
// we're producing our diagnostics. | // we're producing our diagnostics. | ||||
Deserializing RecursionGuard(this); | Deserializing RecursionGuard(this); | ||||
// Used with err_module_odr_violation_mismatch_decl and | // Issue any pending ODR-failure diagnostics. | ||||
// note_module_odr_violation_mismatch_decl | for (auto &Merge : OdrMergeFailures) { | ||||
Could we hoist these construction? ChuanqiXu: Could we hoist these construction? | |||||
Sure. I see your point that ODRDiagsEmitter is stateless regarding diagnoseMismatch methods, so repeating the same construction looks excessive and verbose. But if anybody later has any reasons to change that, it should be possible as ODRDiagsEmitter construction is cheap. vsapsai: Sure. I see your point that `ODRDiagsEmitter` is stateless regarding `diagnoseMismatch` methods… | |||||
// This list should be the same Decl's as in ODRHash::isDeclToBeProcessed | // If we've already pointed out a specific problem with this class, don't | ||||
enum ODRMismatchDecl { | // bother issuing a general "something's different" diagnostic. | ||||
EndOfClass, | if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) | ||||
PublicSpecifer, | continue; | ||||
PrivateSpecifer, | |||||
ProtectedSpecifer, | |||||
StaticAssert, | |||||
Field, | |||||
CXXMethod, | |||||
TypeAlias, | |||||
TypeDef, | |||||
Var, | |||||
Friend, | |||||
FunctionTemplate, | |||||
Other | |||||
}; | |||||
// Used with err_module_odr_violation_record and | bool Diagnosed = false; | ||||
// note_module_odr_violation_record | ODRDiagsEmitter DiagsEmitter(Diags, getContext(), | ||||
enum ODRCXXRecordDifference { | getPreprocessor().getLangOpts()); | ||||
StaticAssertCondition, | CXXRecordDecl *FirstRecord = Merge.first; | ||||
StaticAssertMessage, | for (auto &RecordPair : Merge.second) { | ||||
StaticAssertOnlyMessage, | if (DiagsEmitter.diagnoseMismatch(FirstRecord, RecordPair.first, | ||||
MethodName, | RecordPair.second)) { | ||||
MethodDeleted, | Diagnosed = true; | ||||
MethodDefaulted, | break; | ||||
MethodVirtual, | } | ||||
MethodStatic, | } | ||||
MethodVolatile, | |||||
MethodConst, | if (!Diagnosed) { | ||||
MethodInline, | // All definitions are updates to the same declaration. This happens if a | ||||
MethodNumberParameters, | // module instantiates the declaration of a class template specialization | ||||
MethodParameterType, | // and two or more other modules instantiate its definition. | ||||
MethodParameterName, | // | ||||
MethodParameterSingleDefaultArgument, | // FIXME: Indicate which modules had instantiations of this definition. | ||||
MethodParameterDifferentDefaultArgument, | // FIXME: How can this even happen? | ||||
MethodNoTemplateArguments, | Diag(Merge.first->getLocation(), | ||||
MethodDifferentNumberTemplateArguments, | diag::err_module_odr_violation_different_instantiations) | ||||
MethodDifferentTemplateArgument, | << Merge.first; | ||||
MethodSingleBody, | } | ||||
MethodDifferentBody, | } | ||||
FriendTypeFunction, | |||||
FriendType, | // Issue ODR failures diagnostics for functions. | ||||
FriendFunction, | for (auto &Merge : FunctionOdrMergeFailures) { | ||||
FunctionTemplateDifferentNumberParameters, | ODRDiagsEmitter DiagsEmitter(Diags, getContext(), | ||||
FunctionTemplateParameterDifferentKind, | getPreprocessor().getLangOpts()); | ||||
FunctionTemplateParameterName, | FunctionDecl *FirstFunction = Merge.first; | ||||
FunctionTemplateParameterSingleDefaultArgument, | bool Diagnosed = false; | ||||
FunctionTemplateParameterDifferentDefaultArgument, | for (auto &SecondFunction : Merge.second) { | ||||
FunctionTemplateParameterDifferentType, | if (DiagsEmitter.diagnoseMismatch(FirstFunction, SecondFunction)) { | ||||
FunctionTemplatePackParameter, | Diagnosed = true; | ||||
}; | break; | ||||
} | |||||
} | |||||
(void)Diagnosed; | |||||
assert(Diagnosed && "Unable to emit ODR diagnostic."); | |||||
} | |||||
// Issue ODR failures diagnostics for enums. | |||||
for (auto &Merge : EnumOdrMergeFailures) { | |||||
// If we've already pointed out a specific problem with this enum, don't | |||||
// bother issuing a general "something's different" diagnostic. | |||||
if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) | |||||
continue; | |||||
ODRDiagsEmitter DiagsEmitter(Diags, getContext(), | |||||
getPreprocessor().getLangOpts()); | |||||
EnumDecl *FirstEnum = Merge.first; | |||||
bool Diagnosed = false; | |||||
for (auto &SecondEnum : Merge.second) { | |||||
if (DiagsEmitter.diagnoseMismatch(FirstEnum, SecondEnum)) { | |||||
Diagnosed = true; | |||||
break; | |||||
} | |||||
} | |||||
(void)Diagnosed; | |||||
assert(Diagnosed && "Unable to emit ODR diagnostic."); | |||||
} | |||||
} | |||||
// These lambdas have the common portions of the ODR diagnostics. This | bool ODRDiagsEmitter::diagnoseSubMismatchField( | ||||
// has the same return as Diag(), so addition parameters can be passed | const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, | ||||
// in with operator<< | const FieldDecl *FirstField, const FieldDecl *SecondField) const { | ||||
auto ODRDiagField = [this](NamedDecl *FirstRecord, StringRef FirstModule, | |||||
StringRef SecondModule, FieldDecl *FirstField, | |||||
FieldDecl *SecondField) { | |||||
enum ODRFieldDifference { | enum ODRFieldDifference { | ||||
FieldName, | FieldName, | ||||
FieldTypeName, | FieldTypeName, | ||||
FieldSingleBitField, | FieldSingleBitField, | ||||
FieldDifferentWidthBitField, | FieldDifferentWidthBitField, | ||||
FieldSingleMutable, | FieldSingleMutable, | ||||
FieldSingleInitializer, | FieldSingleInitializer, | ||||
FieldDifferentInitializers, | FieldDifferentInitializers, | ||||
}; | }; | ||||
auto DiagError = [FirstRecord, FirstField, FirstModule, | auto DiagError = [FirstRecord, FirstField, FirstModule, | ||||
this](ODRFieldDifference DiffType) { | this](ODRFieldDifference DiffType) { | ||||
return Diag(FirstField->getLocation(), | return Diag(FirstField->getLocation(), diag::err_module_odr_violation_field) | ||||
diag::err_module_odr_violation_field) | |||||
<< FirstRecord << FirstModule.empty() << FirstModule | << FirstRecord << FirstModule.empty() << FirstModule | ||||
<< FirstField->getSourceRange() << DiffType; | << FirstField->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto DiagNote = [SecondField, SecondModule, | auto DiagNote = [SecondField, SecondModule, | ||||
this](ODRFieldDifference DiffType) { | this](ODRFieldDifference DiffType) { | ||||
return Diag(SecondField->getLocation(), | return Diag(SecondField->getLocation(), | ||||
diag::note_module_odr_violation_field) | diag::note_module_odr_violation_field) | ||||
<< SecondModule << SecondField->getSourceRange() << DiffType; | << SecondModule << SecondField->getSourceRange() << DiffType; | ||||
}; | }; | ||||
IdentifierInfo *FirstII = FirstField->getIdentifier(); | IdentifierInfo *FirstII = FirstField->getIdentifier(); | ||||
IdentifierInfo *SecondII = SecondField->getIdentifier(); | IdentifierInfo *SecondII = SecondField->getIdentifier(); | ||||
if (FirstII->getName() != SecondII->getName()) { | if (FirstII->getName() != SecondII->getName()) { | ||||
DiagError(FieldName) << FirstII; | DiagError(FieldName) << FirstII; | ||||
DiagNote(FieldName) << SecondII; | DiagNote(FieldName) << SecondII; | ||||
return true; | return true; | ||||
} | } | ||||
assert(getContext().hasSameType(FirstField->getType(), | assert(Context.hasSameType(FirstField->getType(), SecondField->getType())); | ||||
SecondField->getType())); | |||||
QualType FirstType = FirstField->getType(); | QualType FirstType = FirstField->getType(); | ||||
QualType SecondType = SecondField->getType(); | QualType SecondType = SecondField->getType(); | ||||
if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | ||||
DiagError(FieldTypeName) << FirstII << FirstType; | DiagError(FieldTypeName) << FirstII << FirstType; | ||||
DiagNote(FieldTypeName) << SecondII << SecondType; | DiagNote(FieldTypeName) << SecondII << SecondType; | ||||
return true; | return true; | ||||
} | } | ||||
const bool IsFirstBitField = FirstField->isBitField(); | const bool IsFirstBitField = FirstField->isBitField(); | ||||
const bool IsSecondBitField = SecondField->isBitField(); | const bool IsSecondBitField = SecondField->isBitField(); | ||||
if (IsFirstBitField != IsSecondBitField) { | if (IsFirstBitField != IsSecondBitField) { | ||||
DiagError(FieldSingleBitField) << FirstII << IsFirstBitField; | DiagError(FieldSingleBitField) << FirstII << IsFirstBitField; | ||||
DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField; | DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField; | ||||
return true; | return true; | ||||
} | } | ||||
if (IsFirstBitField && IsSecondBitField) { | if (IsFirstBitField && IsSecondBitField) { | ||||
unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth()); | unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth()); | ||||
unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth()); | unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth()); | ||||
if (FirstBitWidthHash != SecondBitWidthHash) { | if (FirstBitWidthHash != SecondBitWidthHash) { | ||||
DiagError(FieldDifferentWidthBitField) | DiagError(FieldDifferentWidthBitField) | ||||
<< FirstII << FirstField->getBitWidth()->getSourceRange(); | << FirstII << FirstField->getBitWidth()->getSourceRange(); | ||||
DiagNote(FieldDifferentWidthBitField) | DiagNote(FieldDifferentWidthBitField) | ||||
<< SecondII << SecondField->getBitWidth()->getSourceRange(); | << SecondII << SecondField->getBitWidth()->getSourceRange(); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
if (!PP.getLangOpts().CPlusPlus) | if (!LangOpts.CPlusPlus) | ||||
return false; | return false; | ||||
const bool IsFirstMutable = FirstField->isMutable(); | const bool IsFirstMutable = FirstField->isMutable(); | ||||
const bool IsSecondMutable = SecondField->isMutable(); | const bool IsSecondMutable = SecondField->isMutable(); | ||||
if (IsFirstMutable != IsSecondMutable) { | if (IsFirstMutable != IsSecondMutable) { | ||||
DiagError(FieldSingleMutable) << FirstII << IsFirstMutable; | DiagError(FieldSingleMutable) << FirstII << IsFirstMutable; | ||||
DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable; | DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable; | ||||
return true; | return true; | ||||
} | } | ||||
const Expr *FirstInitializer = FirstField->getInClassInitializer(); | const Expr *FirstInitializer = FirstField->getInClassInitializer(); | ||||
const Expr *SecondInitializer = SecondField->getInClassInitializer(); | const Expr *SecondInitializer = SecondField->getInClassInitializer(); | ||||
if ((!FirstInitializer && SecondInitializer) || | if ((!FirstInitializer && SecondInitializer) || | ||||
(FirstInitializer && !SecondInitializer)) { | (FirstInitializer && !SecondInitializer)) { | ||||
DiagError(FieldSingleInitializer) | DiagError(FieldSingleInitializer) | ||||
<< FirstII << (FirstInitializer != nullptr); | << FirstII << (FirstInitializer != nullptr); | ||||
DiagNote(FieldSingleInitializer) | DiagNote(FieldSingleInitializer) | ||||
<< SecondII << (SecondInitializer != nullptr); | << SecondII << (SecondInitializer != nullptr); | ||||
return true; | return true; | ||||
} | } | ||||
if (FirstInitializer && SecondInitializer) { | if (FirstInitializer && SecondInitializer) { | ||||
unsigned FirstInitHash = computeODRHash(FirstInitializer); | unsigned FirstInitHash = computeODRHash(FirstInitializer); | ||||
unsigned SecondInitHash = computeODRHash(SecondInitializer); | unsigned SecondInitHash = computeODRHash(SecondInitializer); | ||||
if (FirstInitHash != SecondInitHash) { | if (FirstInitHash != SecondInitHash) { | ||||
DiagError(FieldDifferentInitializers) | DiagError(FieldDifferentInitializers) | ||||
<< FirstII << FirstInitializer->getSourceRange(); | << FirstII << FirstInitializer->getSourceRange(); | ||||
DiagNote(FieldDifferentInitializers) | DiagNote(FieldDifferentInitializers) | ||||
<< SecondII << SecondInitializer->getSourceRange(); | << SecondII << SecondInitializer->getSourceRange(); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
}; | } | ||||
auto ODRDiagTypeDefOrAlias = | bool ODRDiagsEmitter::diagnoseSubMismatchTypedef( | ||||
[this](NamedDecl *FirstRecord, StringRef FirstModule, | const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, | ||||
StringRef SecondModule, TypedefNameDecl *FirstTD, | const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD, | ||||
TypedefNameDecl *SecondTD, bool IsTypeAlias) { | bool IsTypeAlias) const { | ||||
enum ODRTypedefDifference { | enum ODRTypedefDifference { | ||||
TypedefName, | TypedefName, | ||||
TypedefType, | TypedefType, | ||||
}; | }; | ||||
auto DiagError = [FirstRecord, FirstTD, FirstModule, | auto DiagError = [FirstRecord, FirstTD, FirstModule, | ||||
this](ODRTypedefDifference DiffType) { | this](ODRTypedefDifference DiffType) { | ||||
return Diag(FirstTD->getLocation(), | return Diag(FirstTD->getLocation(), diag::err_module_odr_violation_typedef) | ||||
diag::err_module_odr_violation_typedef) | |||||
<< FirstRecord << FirstModule.empty() << FirstModule | << FirstRecord << FirstModule.empty() << FirstModule | ||||
<< FirstTD->getSourceRange() << DiffType; | << FirstTD->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto DiagNote = [SecondTD, SecondModule, | auto DiagNote = [SecondTD, SecondModule, | ||||
this](ODRTypedefDifference DiffType) { | this](ODRTypedefDifference DiffType) { | ||||
return Diag(SecondTD->getLocation(), | return Diag(SecondTD->getLocation(), | ||||
diag::note_module_odr_violation_typedef) | diag::note_module_odr_violation_typedef) | ||||
<< SecondModule << SecondTD->getSourceRange() << DiffType; | << SecondModule << SecondTD->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto FirstName = FirstTD->getDeclName(); | DeclarationName FirstName = FirstTD->getDeclName(); | ||||
auto SecondName = SecondTD->getDeclName(); | DeclarationName SecondName = SecondTD->getDeclName(); | ||||
if (FirstName != SecondName) { | if (FirstName != SecondName) { | ||||
DiagError(TypedefName) << IsTypeAlias << FirstName; | DiagError(TypedefName) << IsTypeAlias << FirstName; | ||||
DiagNote(TypedefName) << IsTypeAlias << SecondName; | DiagNote(TypedefName) << IsTypeAlias << SecondName; | ||||
return true; | return true; | ||||
} | } | ||||
QualType FirstType = FirstTD->getUnderlyingType(); | QualType FirstType = FirstTD->getUnderlyingType(); | ||||
QualType SecondType = SecondTD->getUnderlyingType(); | QualType SecondType = SecondTD->getUnderlyingType(); | ||||
if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | ||||
DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType; | DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType; | ||||
DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType; | DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType; | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | } | ||||
auto ODRDiagVar = [this](NamedDecl *FirstRecord, StringRef FirstModule, | bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord, | ||||
StringRef SecondModule, VarDecl *FirstVD, | StringRef FirstModule, | ||||
VarDecl *SecondVD) { | StringRef SecondModule, | ||||
const VarDecl *FirstVD, | |||||
const VarDecl *SecondVD) const { | |||||
enum ODRVarDifference { | enum ODRVarDifference { | ||||
VarName, | VarName, | ||||
VarType, | VarType, | ||||
VarSingleInitializer, | VarSingleInitializer, | ||||
VarDifferentInitializer, | VarDifferentInitializer, | ||||
VarConstexpr, | VarConstexpr, | ||||
}; | }; | ||||
auto DiagError = [FirstRecord, FirstVD, FirstModule, | auto DiagError = [FirstRecord, FirstVD, FirstModule, | ||||
this](ODRVarDifference DiffType) { | this](ODRVarDifference DiffType) { | ||||
return Diag(FirstVD->getLocation(), | return Diag(FirstVD->getLocation(), diag::err_module_odr_violation_variable) | ||||
diag::err_module_odr_violation_variable) | |||||
<< FirstRecord << FirstModule.empty() << FirstModule | << FirstRecord << FirstModule.empty() << FirstModule | ||||
<< FirstVD->getSourceRange() << DiffType; | << FirstVD->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) { | auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) { | ||||
return Diag(SecondVD->getLocation(), | return Diag(SecondVD->getLocation(), | ||||
diag::note_module_odr_violation_variable) | diag::note_module_odr_violation_variable) | ||||
<< SecondModule << SecondVD->getSourceRange() << DiffType; | << SecondModule << SecondVD->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto FirstName = FirstVD->getDeclName(); | DeclarationName FirstName = FirstVD->getDeclName(); | ||||
auto SecondName = SecondVD->getDeclName(); | DeclarationName SecondName = SecondVD->getDeclName(); | ||||
if (FirstName != SecondName) { | if (FirstName != SecondName) { | ||||
DiagError(VarName) << FirstName; | DiagError(VarName) << FirstName; | ||||
DiagNote(VarName) << SecondName; | DiagNote(VarName) << SecondName; | ||||
return true; | return true; | ||||
} | } | ||||
QualType FirstType = FirstVD->getType(); | QualType FirstType = FirstVD->getType(); | ||||
QualType SecondType = SecondVD->getType(); | QualType SecondType = SecondVD->getType(); | ||||
if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | ||||
DiagError(VarType) << FirstName << FirstType; | DiagError(VarType) << FirstName << FirstType; | ||||
DiagNote(VarType) << SecondName << SecondType; | DiagNote(VarType) << SecondName << SecondType; | ||||
return true; | return true; | ||||
} | } | ||||
if (!PP.getLangOpts().CPlusPlus) | if (!LangOpts.CPlusPlus) | ||||
return false; | return false; | ||||
const Expr *FirstInit = FirstVD->getInit(); | const Expr *FirstInit = FirstVD->getInit(); | ||||
const Expr *SecondInit = SecondVD->getInit(); | const Expr *SecondInit = SecondVD->getInit(); | ||||
if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | ||||
DiagError(VarSingleInitializer) | DiagError(VarSingleInitializer) | ||||
<< FirstName << (FirstInit == nullptr) | << FirstName << (FirstInit == nullptr) | ||||
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | ||||
DiagNote(VarSingleInitializer) | DiagNote(VarSingleInitializer) | ||||
<< SecondName << (SecondInit == nullptr) | << SecondName << (SecondInit == nullptr) | ||||
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | ||||
return true; | return true; | ||||
} | } | ||||
if (FirstInit && SecondInit && | if (FirstInit && SecondInit && | ||||
computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | ||||
DiagError(VarDifferentInitializer) | DiagError(VarDifferentInitializer) | ||||
<< FirstName << FirstInit->getSourceRange(); | << FirstName << FirstInit->getSourceRange(); | ||||
DiagNote(VarDifferentInitializer) | DiagNote(VarDifferentInitializer) | ||||
<< SecondName << SecondInit->getSourceRange(); | << SecondName << SecondInit->getSourceRange(); | ||||
return true; | return true; | ||||
} | } | ||||
const bool FirstIsConstexpr = FirstVD->isConstexpr(); | const bool FirstIsConstexpr = FirstVD->isConstexpr(); | ||||
const bool SecondIsConstexpr = SecondVD->isConstexpr(); | const bool SecondIsConstexpr = SecondVD->isConstexpr(); | ||||
if (FirstIsConstexpr != SecondIsConstexpr) { | if (FirstIsConstexpr != SecondIsConstexpr) { | ||||
DiagError(VarConstexpr) << FirstName << FirstIsConstexpr; | DiagError(VarConstexpr) << FirstName << FirstIsConstexpr; | ||||
DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr; | DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr; | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | } | ||||
auto DifferenceSelector = [](Decl *D) { | ODRDiagsEmitter::DiffResult | ||||
ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes, | |||||
DeclHashes &SecondHashes) { | |||||
auto DifferenceSelector = [](const Decl *D) { | |||||
assert(D && "valid Decl required"); | assert(D && "valid Decl required"); | ||||
switch (D->getKind()) { | switch (D->getKind()) { | ||||
default: | default: | ||||
return Other; | return Other; | ||||
case Decl::AccessSpec: | case Decl::AccessSpec: | ||||
switch (D->getAccess()) { | switch (D->getAccess()) { | ||||
case AS_public: | case AS_public: | ||||
return PublicSpecifer; | return PublicSpecifer; | ||||
Show All 21 Lines | case Decl::Var: | ||||
return Var; | return Var; | ||||
case Decl::Friend: | case Decl::Friend: | ||||
return Friend; | return Friend; | ||||
case Decl::FunctionTemplate: | case Decl::FunctionTemplate: | ||||
return FunctionTemplate; | return FunctionTemplate; | ||||
} | } | ||||
}; | }; | ||||
using DeclHashes = llvm::SmallVector<std::pair<Decl *, unsigned>, 4>; | |||||
auto PopulateHashes = [](DeclHashes &Hashes, RecordDecl *Record, | |||||
const DeclContext *DC) { | |||||
for (auto *D : Record->decls()) { | |||||
if (!ODRHash::isDeclToBeProcessed(D, DC)) | |||||
continue; | |||||
Hashes.emplace_back(D, computeODRHash(D)); | |||||
} | |||||
}; | |||||
struct DiffResult { | |||||
Decl *FirstDecl = nullptr, *SecondDecl = nullptr; | |||||
ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; | |||||
}; | |||||
// If there is a diagnoseable difference, FirstDiffType and | |||||
// SecondDiffType will not be Other and FirstDecl and SecondDecl will be | |||||
// filled in if not EndOfClass. | |||||
auto FindTypeDiffs = [&DifferenceSelector](DeclHashes &FirstHashes, | |||||
DeclHashes &SecondHashes) { | |||||
DiffResult DR; | DiffResult DR; | ||||
auto FirstIt = FirstHashes.begin(); | auto FirstIt = FirstHashes.begin(); | ||||
auto SecondIt = SecondHashes.begin(); | auto SecondIt = SecondHashes.begin(); | ||||
while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { | while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { | ||||
if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && | if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && | ||||
FirstIt->second == SecondIt->second) { | FirstIt->second == SecondIt->second) { | ||||
++FirstIt; | ++FirstIt; | ||||
++SecondIt; | ++SecondIt; | ||||
continue; | continue; | ||||
} | } | ||||
DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; | DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; | ||||
DR.SecondDecl = | DR.SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; | ||||
SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; | |||||
DR.FirstDiffType = | DR.FirstDiffType = | ||||
DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; | DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; | ||||
DR.SecondDiffType = | DR.SecondDiffType = | ||||
DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; | DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; | ||||
return DR; | return DR; | ||||
} | } | ||||
return DR; | return DR; | ||||
}; | } | ||||
// Use this to diagnose that an unexpected Decl was encountered | void ODRDiagsEmitter::diagnoseSubMismatchUnexpected( | ||||
// or no difference was detected. This causes a generic error | DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, | ||||
// message to be emitted. | const NamedDecl *SecondRecord, StringRef SecondModule) const { | ||||
auto DiagnoseODRUnexpected = [this](DiffResult &DR, NamedDecl *FirstRecord, | |||||
StringRef FirstModule, | |||||
NamedDecl *SecondRecord, | |||||
StringRef SecondModule) { | |||||
Diag(FirstRecord->getLocation(), | Diag(FirstRecord->getLocation(), | ||||
diag::err_module_odr_violation_different_definitions) | diag::err_module_odr_violation_different_definitions) | ||||
<< FirstRecord << FirstModule.empty() << FirstModule; | << FirstRecord << FirstModule.empty() << FirstModule; | ||||
if (DR.FirstDecl) { | if (DR.FirstDecl) { | ||||
Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) | Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) | ||||
<< FirstRecord << DR.FirstDecl->getSourceRange(); | << FirstRecord << DR.FirstDecl->getSourceRange(); | ||||
} | } | ||||
Diag(SecondRecord->getLocation(), | Diag(SecondRecord->getLocation(), | ||||
diag::note_module_odr_violation_different_definitions) | diag::note_module_odr_violation_different_definitions) | ||||
<< SecondModule; | << SecondModule; | ||||
if (DR.SecondDecl) { | if (DR.SecondDecl) { | ||||
Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) | Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) | ||||
<< DR.SecondDecl->getSourceRange(); | << DR.SecondDecl->getSourceRange(); | ||||
} | } | ||||
}; | } | ||||
auto DiagnoseODRMismatch = [this](DiffResult &DR, NamedDecl *FirstRecord, | void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds( | ||||
StringRef FirstModule, | DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, | ||||
NamedDecl *SecondRecord, | const NamedDecl *SecondRecord, StringRef SecondModule) const { | ||||
StringRef SecondModule) { | |||||
auto GetMismatchedDeclLoc = [](const NamedDecl *Container, | auto GetMismatchedDeclLoc = [](const NamedDecl *Container, | ||||
ODRMismatchDecl DiffType, const Decl *D) { | ODRMismatchDecl DiffType, const Decl *D) { | ||||
SourceLocation Loc; | SourceLocation Loc; | ||||
SourceRange Range; | SourceRange Range; | ||||
auto *Tag = dyn_cast<TagDecl>(Container); | auto *Tag = dyn_cast<TagDecl>(Container); | ||||
if (DiffType == EndOfClass && Tag) { | if (DiffType == EndOfClass && Tag) { | ||||
Loc = Tag->getBraceRange().getEnd(); | Loc = Tag->getBraceRange().getEnd(); | ||||
} else { | } else { | ||||
Loc = D->getLocation(); | Loc = D->getLocation(); | ||||
Range = D->getSourceRange(); | Range = D->getSourceRange(); | ||||
} | } | ||||
return std::make_pair(Loc, Range); | return std::make_pair(Loc, Range); | ||||
}; | }; | ||||
auto FirstDiagInfo = | auto FirstDiagInfo = | ||||
GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); | GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); | ||||
Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) | Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) | ||||
<< FirstRecord << FirstModule.empty() << FirstModule | << FirstRecord << FirstModule.empty() << FirstModule | ||||
<< FirstDiagInfo.second << DR.FirstDiffType; | << FirstDiagInfo.second << DR.FirstDiffType; | ||||
auto SecondDiagInfo = | auto SecondDiagInfo = | ||||
GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); | GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); | ||||
Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) | Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) | ||||
<< SecondModule << SecondDiagInfo.second << DR.SecondDiffType; | << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; | ||||
}; | } | ||||
// Issue any pending ODR-failure diagnostics. | |||||
for (auto &Merge : OdrMergeFailures) { | |||||
// If we've already pointed out a specific problem with this class, don't | |||||
// bother issuing a general "something's different" diagnostic. | |||||
if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) | |||||
continue; | |||||
bool Diagnosed = false; | bool ODRDiagsEmitter::diagnoseMismatch( | ||||
CXXRecordDecl *FirstRecord = Merge.first; | const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord, | ||||
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); | const struct CXXRecordDecl::DefinitionData *SecondDD) const { | ||||
for (auto &RecordPair : Merge.second) { | |||||
CXXRecordDecl *SecondRecord = RecordPair.first; | |||||
// Multiple different declarations got merged together; tell the user | // Multiple different declarations got merged together; tell the user | ||||
// where they came from. | // where they came from. | ||||
if (FirstRecord == SecondRecord) | if (FirstRecord == SecondRecord) | ||||
continue; | return false; | ||||
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); | |||||
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); | std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); | ||||
auto ODRDiagDeclError = [FirstRecord, &FirstModule, | |||||
this](SourceLocation Loc, SourceRange Range, | |||||
ODRCXXRecordDifference DiffType) { | |||||
return Diag(Loc, diag::err_module_odr_violation_record) | |||||
<< FirstRecord << FirstModule.empty() << FirstModule << Range | |||||
<< DiffType; | |||||
}; | |||||
auto ODRDiagDeclNote = [&SecondModule, | |||||
this](SourceLocation Loc, SourceRange Range, | |||||
ODRCXXRecordDifference DiffType) { | |||||
return Diag(Loc, diag::note_module_odr_violation_record) | |||||
<< SecondModule << Range << DiffType; | |||||
}; | |||||
auto *FirstDD = FirstRecord->DefinitionData; | |||||
auto *SecondDD = RecordPair.second; | |||||
const struct CXXRecordDecl::DefinitionData *FirstDD = | |||||
FirstRecord->DefinitionData; | |||||
assert(FirstDD && SecondDD && "Definitions without DefinitionData"); | assert(FirstDD && SecondDD && "Definitions without DefinitionData"); | ||||
// Diagnostics from DefinitionData are emitted here. | // Diagnostics from DefinitionData are emitted here. | ||||
if (FirstDD != SecondDD) { | if (FirstDD != SecondDD) { | ||||
// Keep in sync with err_module_odr_violation_definition_data. | |||||
enum ODRDefinitionDataDifference { | enum ODRDefinitionDataDifference { | ||||
NumBases, | NumBases, | ||||
NumVBases, | NumVBases, | ||||
BaseType, | BaseType, | ||||
BaseVirtual, | BaseVirtual, | ||||
BaseAccess, | BaseAccess, | ||||
}; | }; | ||||
auto ODRDiagBaseError = [FirstRecord, &FirstModule, | auto DiagBaseError = [FirstRecord, &FirstModule, | ||||
this](SourceLocation Loc, SourceRange Range, | this](SourceLocation Loc, SourceRange Range, | ||||
ODRDefinitionDataDifference DiffType) { | ODRDefinitionDataDifference DiffType) { | ||||
return Diag(Loc, diag::err_module_odr_violation_definition_data) | return Diag(Loc, diag::err_module_odr_violation_definition_data) | ||||
<< FirstRecord << FirstModule.empty() << FirstModule << Range | << FirstRecord << FirstModule.empty() << FirstModule << Range | ||||
<< DiffType; | << DiffType; | ||||
}; | }; | ||||
auto ODRDiagBaseNote = [&SecondModule, | auto DiagBaseNote = [&SecondModule, | ||||
this](SourceLocation Loc, SourceRange Range, | this](SourceLocation Loc, SourceRange Range, | ||||
ODRDefinitionDataDifference DiffType) { | ODRDefinitionDataDifference DiffType) { | ||||
return Diag(Loc, diag::note_module_odr_violation_definition_data) | return Diag(Loc, diag::note_module_odr_violation_definition_data) | ||||
<< SecondModule << Range << DiffType; | << SecondModule << Range << DiffType; | ||||
}; | }; | ||||
auto GetSourceRange = [](const struct CXXRecordDecl::DefinitionData *DD) { | |||||
unsigned FirstNumBases = FirstDD->NumBases; | |||||
unsigned FirstNumVBases = FirstDD->NumVBases; | |||||
unsigned SecondNumBases = SecondDD->NumBases; | |||||
unsigned SecondNumVBases = SecondDD->NumVBases; | |||||
auto GetSourceRange = [](struct CXXRecordDecl::DefinitionData *DD) { | |||||
unsigned NumBases = DD->NumBases; | unsigned NumBases = DD->NumBases; | ||||
if (NumBases == 0) return SourceRange(); | if (NumBases == 0) | ||||
auto bases = DD->bases(); | return SourceRange(); | ||||
ArrayRef<CXXBaseSpecifier> bases = DD->bases(); | |||||
return SourceRange(bases[0].getBeginLoc(), | return SourceRange(bases[0].getBeginLoc(), | ||||
bases[NumBases - 1].getEndLoc()); | bases[NumBases - 1].getEndLoc()); | ||||
}; | }; | ||||
unsigned FirstNumBases = FirstDD->NumBases; | |||||
unsigned FirstNumVBases = FirstDD->NumVBases; | |||||
unsigned SecondNumBases = SecondDD->NumBases; | |||||
unsigned SecondNumVBases = SecondDD->NumVBases; | |||||
if (FirstNumBases != SecondNumBases) { | if (FirstNumBases != SecondNumBases) { | ||||
ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), | DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), | ||||
NumBases) | NumBases) | ||||
<< FirstNumBases; | << FirstNumBases; | ||||
ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), | DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), | ||||
NumBases) | NumBases) | ||||
<< SecondNumBases; | << SecondNumBases; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstNumVBases != SecondNumVBases) { | if (FirstNumVBases != SecondNumVBases) { | ||||
ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), | DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), | ||||
NumVBases) | NumVBases) | ||||
<< FirstNumVBases; | << FirstNumVBases; | ||||
ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), | DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), | ||||
NumVBases) | NumVBases) | ||||
<< SecondNumVBases; | << SecondNumVBases; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
auto FirstBases = FirstDD->bases(); | ArrayRef<CXXBaseSpecifier> FirstBases = FirstDD->bases(); | ||||
auto SecondBases = SecondDD->bases(); | ArrayRef<CXXBaseSpecifier> SecondBases = SecondDD->bases(); | ||||
unsigned i = 0; | for (unsigned I = 0; I < FirstNumBases; ++I) { | ||||
for (i = 0; i < FirstNumBases; ++i) { | const CXXBaseSpecifier FirstBase = FirstBases[I]; | ||||
auto FirstBase = FirstBases[i]; | const CXXBaseSpecifier SecondBase = SecondBases[I]; | ||||
auto SecondBase = SecondBases[i]; | |||||
if (computeODRHash(FirstBase.getType()) != | if (computeODRHash(FirstBase.getType()) != | ||||
computeODRHash(SecondBase.getType())) { | computeODRHash(SecondBase.getType())) { | ||||
ODRDiagBaseError(FirstRecord->getLocation(), | DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), | ||||
FirstBase.getSourceRange(), BaseType) | BaseType) | ||||
<< (i + 1) << FirstBase.getType(); | << (I + 1) << FirstBase.getType(); | ||||
ODRDiagBaseNote(SecondRecord->getLocation(), | DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), | ||||
SecondBase.getSourceRange(), BaseType) | BaseType) | ||||
<< (i + 1) << SecondBase.getType(); | << (I + 1) << SecondBase.getType(); | ||||
break; | return true; | ||||
} | } | ||||
if (FirstBase.isVirtual() != SecondBase.isVirtual()) { | if (FirstBase.isVirtual() != SecondBase.isVirtual()) { | ||||
ODRDiagBaseError(FirstRecord->getLocation(), | DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), | ||||
FirstBase.getSourceRange(), BaseVirtual) | BaseVirtual) | ||||
<< (i + 1) << FirstBase.isVirtual() << FirstBase.getType(); | << (I + 1) << FirstBase.isVirtual() << FirstBase.getType(); | ||||
ODRDiagBaseNote(SecondRecord->getLocation(), | DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), | ||||
SecondBase.getSourceRange(), BaseVirtual) | BaseVirtual) | ||||
<< (i + 1) << SecondBase.isVirtual() << SecondBase.getType(); | << (I + 1) << SecondBase.isVirtual() << SecondBase.getType(); | ||||
break; | return true; | ||||
} | } | ||||
if (FirstBase.getAccessSpecifierAsWritten() != | if (FirstBase.getAccessSpecifierAsWritten() != | ||||
SecondBase.getAccessSpecifierAsWritten()) { | SecondBase.getAccessSpecifierAsWritten()) { | ||||
ODRDiagBaseError(FirstRecord->getLocation(), | DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), | ||||
FirstBase.getSourceRange(), BaseAccess) | BaseAccess) | ||||
<< (i + 1) << FirstBase.getType() | << (I + 1) << FirstBase.getType() | ||||
<< (int)FirstBase.getAccessSpecifierAsWritten(); | << (int)FirstBase.getAccessSpecifierAsWritten(); | ||||
ODRDiagBaseNote(SecondRecord->getLocation(), | DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), | ||||
SecondBase.getSourceRange(), BaseAccess) | BaseAccess) | ||||
<< (i + 1) << SecondBase.getType() | << (I + 1) << SecondBase.getType() | ||||
<< (int)SecondBase.getAccessSpecifierAsWritten(); | << (int)SecondBase.getAccessSpecifierAsWritten(); | ||||
break; | return true; | ||||
} | |||||
} | } | ||||
if (i != FirstNumBases) { | |||||
Diagnosed = true; | |||||
break; | |||||
} | } | ||||
} | } | ||||
const ClassTemplateDecl *FirstTemplate = | const ClassTemplateDecl *FirstTemplate = | ||||
FirstRecord->getDescribedClassTemplate(); | FirstRecord->getDescribedClassTemplate(); | ||||
const ClassTemplateDecl *SecondTemplate = | const ClassTemplateDecl *SecondTemplate = | ||||
SecondRecord->getDescribedClassTemplate(); | SecondRecord->getDescribedClassTemplate(); | ||||
assert(!FirstTemplate == !SecondTemplate && | assert(!FirstTemplate == !SecondTemplate && | ||||
"Both pointers should be null or non-null"); | "Both pointers should be null or non-null"); | ||||
if (FirstTemplate && SecondTemplate) { | if (FirstTemplate && SecondTemplate) { | ||||
DeclHashes FirstTemplateHashes; | ArrayRef<const NamedDecl *> FirstTemplateParams = | ||||
DeclHashes SecondTemplateHashes; | FirstTemplate->getTemplateParameters()->asArray(); | ||||
ArrayRef<const NamedDecl *> SecondTemplateParams = | |||||
auto PopulateTemplateParameterHashs = [](DeclHashes &Hashes, | SecondTemplate->getTemplateParameters()->asArray(); | ||||
const ClassTemplateDecl *TD) { | assert(FirstTemplateParams.size() == SecondTemplateParams.size() && | ||||
for (auto *D : TD->getTemplateParameters()->asArray()) { | |||||
Hashes.emplace_back(D, computeODRHash(D)); | |||||
} | |||||
}; | |||||
PopulateTemplateParameterHashs(FirstTemplateHashes, FirstTemplate); | |||||
PopulateTemplateParameterHashs(SecondTemplateHashes, SecondTemplate); | |||||
assert(FirstTemplateHashes.size() == SecondTemplateHashes.size() && | |||||
"Number of template parameters should be equal."); | "Number of template parameters should be equal."); | ||||
for (auto Pair : llvm::zip(FirstTemplateParams, SecondTemplateParams)) { | |||||
auto FirstIt = FirstTemplateHashes.begin(); | const NamedDecl *FirstDecl = std::get<0>(Pair); | ||||
auto FirstEnd = FirstTemplateHashes.end(); | const NamedDecl *SecondDecl = std::get<1>(Pair); | ||||
auto SecondIt = SecondTemplateHashes.begin(); | if (computeODRHash(FirstDecl) == computeODRHash(SecondDecl)) | ||||
for (; FirstIt != FirstEnd; ++FirstIt, ++SecondIt) { | |||||
if (FirstIt->second == SecondIt->second) | |||||
continue; | continue; | ||||
const NamedDecl* FirstDecl = cast<NamedDecl>(FirstIt->first); | |||||
const NamedDecl* SecondDecl = cast<NamedDecl>(SecondIt->first); | |||||
assert(FirstDecl->getKind() == SecondDecl->getKind() && | assert(FirstDecl->getKind() == SecondDecl->getKind() && | ||||
"Parameter Decl's should be the same kind."); | "Parameter Decl's should be the same kind."); | ||||
enum ODRTemplateDifference { | enum ODRTemplateDifference { | ||||
ParamEmptyName, | ParamEmptyName, | ||||
ParamName, | ParamName, | ||||
ParamSingleDefaultArgument, | ParamSingleDefaultArgument, | ||||
ParamDifferentDefaultArgument, | ParamDifferentDefaultArgument, | ||||
}; | }; | ||||
auto hasDefaultArg = [](const NamedDecl *D) { | auto hasDefaultArg = [](const NamedDecl *D) { | ||||
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) | if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) | ||||
return TTP->hasDefaultArgument() && | return TTP->hasDefaultArgument() && | ||||
!TTP->defaultArgumentWasInherited(); | !TTP->defaultArgumentWasInherited(); | ||||
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) | if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) | ||||
return NTTP->hasDefaultArgument() && | return NTTP->hasDefaultArgument() && | ||||
!NTTP->defaultArgumentWasInherited(); | !NTTP->defaultArgumentWasInherited(); | ||||
auto *TTP = cast<TemplateTemplateParmDecl>(D); | auto *TTP = cast<TemplateTemplateParmDecl>(D); | ||||
return TTP->hasDefaultArgument() && | return TTP->hasDefaultArgument() && !TTP->defaultArgumentWasInherited(); | ||||
!TTP->defaultArgumentWasInherited(); | |||||
}; | }; | ||||
bool hasFirstArg = hasDefaultArg(FirstDecl); | bool hasFirstArg = hasDefaultArg(FirstDecl); | ||||
bool hasSecondArg = hasDefaultArg(SecondDecl); | bool hasSecondArg = hasDefaultArg(SecondDecl); | ||||
ODRTemplateDifference ErrDiffType; | ODRTemplateDifference ErrDiffType; | ||||
ODRTemplateDifference NoteDiffType; | ODRTemplateDifference NoteDiffType; | ||||
DeclarationName FirstName = FirstDecl->getDeclName(); | DeclarationName FirstName = FirstDecl->getDeclName(); | ||||
DeclarationName SecondName = SecondDecl->getDeclName(); | DeclarationName SecondName = SecondDecl->getDeclName(); | ||||
if (FirstName != SecondName) { | if (FirstName != SecondName) { | ||||
bool FirstNameEmpty = | bool FirstNameEmpty = | ||||
FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo(); | FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo(); | ||||
bool SecondNameEmpty = SecondName.isIdentifier() && | bool SecondNameEmpty = | ||||
!SecondName.getAsIdentifierInfo(); | SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo(); | ||||
ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName; | ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName; | ||||
NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName; | NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName; | ||||
} else if (hasFirstArg == hasSecondArg) | } else if (hasFirstArg == hasSecondArg) | ||||
ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument; | ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument; | ||||
else | else | ||||
ErrDiffType = NoteDiffType = ParamSingleDefaultArgument; | ErrDiffType = NoteDiffType = ParamSingleDefaultArgument; | ||||
Diag(FirstDecl->getLocation(), | Diag(FirstDecl->getLocation(), | ||||
diag::err_module_odr_violation_template_parameter) | diag::err_module_odr_violation_template_parameter) | ||||
<< FirstRecord << FirstModule.empty() << FirstModule | << FirstRecord << FirstModule.empty() << FirstModule | ||||
<< FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg | << FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg | ||||
<< FirstName; | << FirstName; | ||||
Diag(SecondDecl->getLocation(), | Diag(SecondDecl->getLocation(), | ||||
diag::note_module_odr_violation_template_parameter) | diag::note_module_odr_violation_template_parameter) | ||||
<< SecondModule << SecondDecl->getSourceRange() << NoteDiffType | << SecondModule << SecondDecl->getSourceRange() << NoteDiffType | ||||
<< hasSecondArg << SecondName; | << hasSecondArg << SecondName; | ||||
break; | return true; | ||||
} | } | ||||
if (FirstIt != FirstEnd) { | |||||
Diagnosed = true; | |||||
break; | |||||
} | } | ||||
auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record, | |||||
const DeclContext *DC) { | |||||
for (const Decl *D : Record->decls()) { | |||||
if (!ODRHash::isDeclToBeProcessed(D, DC)) | |||||
continue; | |||||
Hashes.emplace_back(D, computeODRHash(D)); | |||||
} | } | ||||
}; | |||||
DeclHashes FirstHashes; | DeclHashes FirstHashes; | ||||
DeclHashes SecondHashes; | DeclHashes SecondHashes; | ||||
const DeclContext *DC = FirstRecord; | const DeclContext *DC = FirstRecord; | ||||
PopulateHashes(FirstHashes, FirstRecord, DC); | PopulateHashes(FirstHashes, FirstRecord, DC); | ||||
PopulateHashes(SecondHashes, SecondRecord, DC); | PopulateHashes(SecondHashes, SecondRecord, DC); | ||||
auto DR = FindTypeDiffs(FirstHashes, SecondHashes); | DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); | ||||
ODRMismatchDecl FirstDiffType = DR.FirstDiffType; | ODRMismatchDecl FirstDiffType = DR.FirstDiffType; | ||||
ODRMismatchDecl SecondDiffType = DR.SecondDiffType; | ODRMismatchDecl SecondDiffType = DR.SecondDiffType; | ||||
Decl *FirstDecl = DR.FirstDecl; | const Decl *FirstDecl = DR.FirstDecl; | ||||
Decl *SecondDecl = DR.SecondDecl; | const Decl *SecondDecl = DR.SecondDecl; | ||||
if (FirstDiffType == Other || SecondDiffType == Other) { | if (FirstDiffType == Other || SecondDiffType == Other) { | ||||
DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, | diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord, | ||||
SecondModule); | SecondModule); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstDiffType != SecondDiffType) { | if (FirstDiffType != SecondDiffType) { | ||||
DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, | diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, | ||||
SecondModule); | SecondRecord, SecondModule); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
assert(FirstDiffType == SecondDiffType); | // Used with err_module_odr_violation_record and | ||||
// note_module_odr_violation_record | |||||
enum ODRCXXRecordDifference { | |||||
StaticAssertCondition, | |||||
StaticAssertMessage, | |||||
StaticAssertOnlyMessage, | |||||
MethodName, | |||||
MethodDeleted, | |||||
MethodDefaulted, | |||||
MethodVirtual, | |||||
MethodStatic, | |||||
MethodVolatile, | |||||
MethodConst, | |||||
MethodInline, | |||||
MethodNumberParameters, | |||||
MethodParameterType, | |||||
MethodParameterName, | |||||
MethodParameterSingleDefaultArgument, | |||||
MethodParameterDifferentDefaultArgument, | |||||
MethodNoTemplateArguments, | |||||
MethodDifferentNumberTemplateArguments, | |||||
MethodDifferentTemplateArgument, | |||||
MethodSingleBody, | |||||
MethodDifferentBody, | |||||
FriendTypeFunction, | |||||
FriendType, | |||||
FriendFunction, | |||||
FunctionTemplateDifferentNumberParameters, | |||||
FunctionTemplateParameterDifferentKind, | |||||
FunctionTemplateParameterName, | |||||
FunctionTemplateParameterSingleDefaultArgument, | |||||
FunctionTemplateParameterDifferentDefaultArgument, | |||||
FunctionTemplateParameterDifferentType, | |||||
FunctionTemplatePackParameter, | |||||
}; | |||||
auto DiagError = [FirstRecord, &FirstModule, | |||||
this](SourceLocation Loc, SourceRange Range, | |||||
ODRCXXRecordDifference DiffType) { | |||||
return Diag(Loc, diag::err_module_odr_violation_record) | |||||
<< FirstRecord << FirstModule.empty() << FirstModule << Range | |||||
<< DiffType; | |||||
}; | |||||
auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range, | |||||
ODRCXXRecordDifference DiffType) { | |||||
return Diag(Loc, diag::note_module_odr_violation_record) | |||||
<< SecondModule << Range << DiffType; | |||||
}; | |||||
assert(FirstDiffType == SecondDiffType); | |||||
switch (FirstDiffType) { | switch (FirstDiffType) { | ||||
case Other: | case Other: | ||||
case EndOfClass: | case EndOfClass: | ||||
case PublicSpecifer: | case PublicSpecifer: | ||||
case PrivateSpecifer: | case PrivateSpecifer: | ||||
case ProtectedSpecifer: | case ProtectedSpecifer: | ||||
llvm_unreachable("Invalid diff type"); | llvm_unreachable("Invalid diff type"); | ||||
case StaticAssert: { | case StaticAssert: { | ||||
StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl); | const StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl); | ||||
StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl); | const StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl); | ||||
Expr *FirstExpr = FirstSA->getAssertExpr(); | const Expr *FirstExpr = FirstSA->getAssertExpr(); | ||||
Expr *SecondExpr = SecondSA->getAssertExpr(); | const Expr *SecondExpr = SecondSA->getAssertExpr(); | ||||
unsigned FirstODRHash = computeODRHash(FirstExpr); | unsigned FirstODRHash = computeODRHash(FirstExpr); | ||||
unsigned SecondODRHash = computeODRHash(SecondExpr); | unsigned SecondODRHash = computeODRHash(SecondExpr); | ||||
if (FirstODRHash != SecondODRHash) { | if (FirstODRHash != SecondODRHash) { | ||||
ODRDiagDeclError(FirstExpr->getBeginLoc(), | DiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(), | ||||
FirstExpr->getSourceRange(), StaticAssertCondition); | StaticAssertCondition); | ||||
ODRDiagDeclNote(SecondExpr->getBeginLoc(), | DiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(), | ||||
SecondExpr->getSourceRange(), StaticAssertCondition); | StaticAssertCondition); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
StringLiteral *FirstStr = FirstSA->getMessage(); | const StringLiteral *FirstStr = FirstSA->getMessage(); | ||||
StringLiteral *SecondStr = SecondSA->getMessage(); | const StringLiteral *SecondStr = SecondSA->getMessage(); | ||||
assert((FirstStr || SecondStr) && "Both messages cannot be empty"); | assert((FirstStr || SecondStr) && "Both messages cannot be empty"); | ||||
if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { | if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { | ||||
SourceLocation FirstLoc, SecondLoc; | SourceLocation FirstLoc, SecondLoc; | ||||
SourceRange FirstRange, SecondRange; | SourceRange FirstRange, SecondRange; | ||||
if (FirstStr) { | if (FirstStr) { | ||||
FirstLoc = FirstStr->getBeginLoc(); | FirstLoc = FirstStr->getBeginLoc(); | ||||
FirstRange = FirstStr->getSourceRange(); | FirstRange = FirstStr->getSourceRange(); | ||||
} else { | } else { | ||||
FirstLoc = FirstSA->getBeginLoc(); | FirstLoc = FirstSA->getBeginLoc(); | ||||
FirstRange = FirstSA->getSourceRange(); | FirstRange = FirstSA->getSourceRange(); | ||||
} | } | ||||
if (SecondStr) { | if (SecondStr) { | ||||
SecondLoc = SecondStr->getBeginLoc(); | SecondLoc = SecondStr->getBeginLoc(); | ||||
SecondRange = SecondStr->getSourceRange(); | SecondRange = SecondStr->getSourceRange(); | ||||
} else { | } else { | ||||
SecondLoc = SecondSA->getBeginLoc(); | SecondLoc = SecondSA->getBeginLoc(); | ||||
SecondRange = SecondSA->getSourceRange(); | SecondRange = SecondSA->getSourceRange(); | ||||
} | } | ||||
ODRDiagDeclError(FirstLoc, FirstRange, StaticAssertOnlyMessage) | DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) | ||||
<< (FirstStr == nullptr); | << (FirstStr == nullptr); | ||||
ODRDiagDeclNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) | DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) | ||||
<< (SecondStr == nullptr); | << (SecondStr == nullptr); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstStr && SecondStr && | if (FirstStr && SecondStr && | ||||
FirstStr->getString() != SecondStr->getString()) { | FirstStr->getString() != SecondStr->getString()) { | ||||
ODRDiagDeclError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), | DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), | ||||
StaticAssertMessage); | StaticAssertMessage); | ||||
ODRDiagDeclNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), | DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), | ||||
StaticAssertMessage); | StaticAssertMessage); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case Field: { | case Field: { | ||||
Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, | if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, | ||||
cast<FieldDecl>(FirstDecl), | cast<FieldDecl>(FirstDecl), | ||||
cast<FieldDecl>(SecondDecl)); | cast<FieldDecl>(SecondDecl))) | ||||
return true; | |||||
break; | break; | ||||
} | } | ||||
case CXXMethod: { | case CXXMethod: { | ||||
enum { | enum { | ||||
DiagMethod, | DiagMethod, | ||||
DiagConstructor, | DiagConstructor, | ||||
DiagDestructor, | DiagDestructor, | ||||
} FirstMethodType, | } FirstMethodType, | ||||
SecondMethodType; | SecondMethodType; | ||||
auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl* D) { | auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) { | ||||
if (isa<CXXConstructorDecl>(D)) return DiagConstructor; | if (isa<CXXConstructorDecl>(D)) | ||||
if (isa<CXXDestructorDecl>(D)) return DiagDestructor; | return DiagConstructor; | ||||
if (isa<CXXDestructorDecl>(D)) | |||||
return DiagDestructor; | |||||
return DiagMethod; | return DiagMethod; | ||||
}; | }; | ||||
const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl); | const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl); | ||||
const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl); | const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl); | ||||
FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); | FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); | ||||
SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); | SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); | ||||
DeclarationName FirstName = FirstMethod->getDeclName(); | DeclarationName FirstName = FirstMethod->getDeclName(); | ||||
DeclarationName SecondName = SecondMethod->getDeclName(); | DeclarationName SecondName = SecondMethod->getDeclName(); | ||||
auto DiagMethodError = [&ODRDiagDeclError, FirstMethod, FirstMethodType, | auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType, | ||||
FirstName](ODRCXXRecordDifference DiffType) { | FirstName](ODRCXXRecordDifference DiffType) { | ||||
return ODRDiagDeclError(FirstMethod->getLocation(), | return DiagError(FirstMethod->getLocation(), | ||||
FirstMethod->getSourceRange(), DiffType) | FirstMethod->getSourceRange(), DiffType) | ||||
<< FirstMethodType << FirstName; | << FirstMethodType << FirstName; | ||||
}; | }; | ||||
auto DiagMethodNote = [&ODRDiagDeclNote, SecondMethod, SecondMethodType, | auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType, | ||||
SecondName](ODRCXXRecordDifference DiffType) { | SecondName](ODRCXXRecordDifference DiffType) { | ||||
return ODRDiagDeclNote(SecondMethod->getLocation(), | return DiagNote(SecondMethod->getLocation(), | ||||
SecondMethod->getSourceRange(), DiffType) | SecondMethod->getSourceRange(), DiffType) | ||||
<< SecondMethodType << SecondName; | << SecondMethodType << SecondName; | ||||
}; | }; | ||||
if (FirstMethodType != SecondMethodType || FirstName != SecondName) { | if (FirstMethodType != SecondMethodType || FirstName != SecondName) { | ||||
DiagMethodError(MethodName); | DiagMethodError(MethodName); | ||||
DiagMethodNote(MethodName); | DiagMethodNote(MethodName); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); | const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); | ||||
const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); | const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); | ||||
if (FirstDeleted != SecondDeleted) { | if (FirstDeleted != SecondDeleted) { | ||||
DiagMethodError(MethodDeleted) << FirstDeleted; | DiagMethodError(MethodDeleted) << FirstDeleted; | ||||
DiagMethodNote(MethodDeleted) << SecondDeleted; | DiagMethodNote(MethodDeleted) << SecondDeleted; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); | const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); | ||||
const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); | const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); | ||||
if (FirstDefaulted != SecondDefaulted) { | if (FirstDefaulted != SecondDefaulted) { | ||||
DiagMethodError(MethodDefaulted) << FirstDefaulted; | DiagMethodError(MethodDefaulted) << FirstDefaulted; | ||||
DiagMethodNote(MethodDefaulted) << SecondDefaulted; | DiagMethodNote(MethodDefaulted) << SecondDefaulted; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); | const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); | ||||
const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); | const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); | ||||
const bool FirstPure = FirstMethod->isPure(); | const bool FirstPure = FirstMethod->isPure(); | ||||
const bool SecondPure = SecondMethod->isPure(); | const bool SecondPure = SecondMethod->isPure(); | ||||
if ((FirstVirtual || SecondVirtual) && | if ((FirstVirtual || SecondVirtual) && | ||||
(FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { | (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { | ||||
DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; | DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; | ||||
DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; | DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
// CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, | // CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, | ||||
// FirstDecl is the canonical Decl of SecondDecl, so the storage | // FirstDecl is the canonical Decl of SecondDecl, so the storage | ||||
// class needs to be checked instead. | // class needs to be checked instead. | ||||
const auto FirstStorage = FirstMethod->getStorageClass(); | StorageClass FirstStorage = FirstMethod->getStorageClass(); | ||||
const auto SecondStorage = SecondMethod->getStorageClass(); | StorageClass SecondStorage = SecondMethod->getStorageClass(); | ||||
const bool FirstStatic = FirstStorage == SC_Static; | const bool FirstStatic = FirstStorage == SC_Static; | ||||
const bool SecondStatic = SecondStorage == SC_Static; | const bool SecondStatic = SecondStorage == SC_Static; | ||||
if (FirstStatic != SecondStatic) { | if (FirstStatic != SecondStatic) { | ||||
DiagMethodError(MethodStatic) << FirstStatic; | DiagMethodError(MethodStatic) << FirstStatic; | ||||
DiagMethodNote(MethodStatic) << SecondStatic; | DiagMethodNote(MethodStatic) << SecondStatic; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstVolatile = FirstMethod->isVolatile(); | const bool FirstVolatile = FirstMethod->isVolatile(); | ||||
const bool SecondVolatile = SecondMethod->isVolatile(); | const bool SecondVolatile = SecondMethod->isVolatile(); | ||||
if (FirstVolatile != SecondVolatile) { | if (FirstVolatile != SecondVolatile) { | ||||
DiagMethodError(MethodVolatile) << FirstVolatile; | DiagMethodError(MethodVolatile) << FirstVolatile; | ||||
DiagMethodNote(MethodVolatile) << SecondVolatile; | DiagMethodNote(MethodVolatile) << SecondVolatile; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstConst = FirstMethod->isConst(); | const bool FirstConst = FirstMethod->isConst(); | ||||
const bool SecondConst = SecondMethod->isConst(); | const bool SecondConst = SecondMethod->isConst(); | ||||
if (FirstConst != SecondConst) { | if (FirstConst != SecondConst) { | ||||
DiagMethodError(MethodConst) << FirstConst; | DiagMethodError(MethodConst) << FirstConst; | ||||
DiagMethodNote(MethodConst) << SecondConst; | DiagMethodNote(MethodConst) << SecondConst; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const bool FirstInline = FirstMethod->isInlineSpecified(); | const bool FirstInline = FirstMethod->isInlineSpecified(); | ||||
const bool SecondInline = SecondMethod->isInlineSpecified(); | const bool SecondInline = SecondMethod->isInlineSpecified(); | ||||
if (FirstInline != SecondInline) { | if (FirstInline != SecondInline) { | ||||
DiagMethodError(MethodInline) << FirstInline; | DiagMethodError(MethodInline) << FirstInline; | ||||
DiagMethodNote(MethodInline) << SecondInline; | DiagMethodNote(MethodInline) << SecondInline; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
const unsigned FirstNumParameters = FirstMethod->param_size(); | const unsigned FirstNumParameters = FirstMethod->param_size(); | ||||
const unsigned SecondNumParameters = SecondMethod->param_size(); | const unsigned SecondNumParameters = SecondMethod->param_size(); | ||||
if (FirstNumParameters != SecondNumParameters) { | if (FirstNumParameters != SecondNumParameters) { | ||||
DiagMethodError(MethodNumberParameters) << FirstNumParameters; | DiagMethodError(MethodNumberParameters) << FirstNumParameters; | ||||
DiagMethodNote(MethodNumberParameters) << SecondNumParameters; | DiagMethodNote(MethodNumberParameters) << SecondNumParameters; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
// Need this status boolean to know when break out of the switch. | |||||
bool ParameterMismatch = false; | |||||
for (unsigned I = 0; I < FirstNumParameters; ++I) { | for (unsigned I = 0; I < FirstNumParameters; ++I) { | ||||
const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); | const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); | ||||
const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); | const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); | ||||
QualType FirstParamType = FirstParam->getType(); | QualType FirstParamType = FirstParam->getType(); | ||||
QualType SecondParamType = SecondParam->getType(); | QualType SecondParamType = SecondParam->getType(); | ||||
if (FirstParamType != SecondParamType && | if (FirstParamType != SecondParamType && | ||||
computeODRHash(FirstParamType) != | computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { | ||||
computeODRHash(SecondParamType)) { | |||||
if (const DecayedType *ParamDecayedType = | if (const DecayedType *ParamDecayedType = | ||||
FirstParamType->getAs<DecayedType>()) { | FirstParamType->getAs<DecayedType>()) { | ||||
DiagMethodError(MethodParameterType) | DiagMethodError(MethodParameterType) | ||||
<< (I + 1) << FirstParamType << true | << (I + 1) << FirstParamType << true | ||||
<< ParamDecayedType->getOriginalType(); | << ParamDecayedType->getOriginalType(); | ||||
} else { | } else { | ||||
DiagMethodError(MethodParameterType) | DiagMethodError(MethodParameterType) | ||||
<< (I + 1) << FirstParamType << false; | << (I + 1) << FirstParamType << false; | ||||
} | } | ||||
if (const DecayedType *ParamDecayedType = | if (const DecayedType *ParamDecayedType = | ||||
SecondParamType->getAs<DecayedType>()) { | SecondParamType->getAs<DecayedType>()) { | ||||
DiagMethodNote(MethodParameterType) | DiagMethodNote(MethodParameterType) | ||||
<< (I + 1) << SecondParamType << true | << (I + 1) << SecondParamType << true | ||||
<< ParamDecayedType->getOriginalType(); | << ParamDecayedType->getOriginalType(); | ||||
} else { | } else { | ||||
DiagMethodNote(MethodParameterType) | DiagMethodNote(MethodParameterType) | ||||
<< (I + 1) << SecondParamType << false; | << (I + 1) << SecondParamType << false; | ||||
} | } | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
DeclarationName FirstParamName = FirstParam->getDeclName(); | DeclarationName FirstParamName = FirstParam->getDeclName(); | ||||
DeclarationName SecondParamName = SecondParam->getDeclName(); | DeclarationName SecondParamName = SecondParam->getDeclName(); | ||||
if (FirstParamName != SecondParamName) { | if (FirstParamName != SecondParamName) { | ||||
DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName; | DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName; | ||||
DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName; | DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
const Expr *FirstInit = FirstParam->getInit(); | const Expr *FirstInit = FirstParam->getInit(); | ||||
const Expr *SecondInit = SecondParam->getInit(); | const Expr *SecondInit = SecondParam->getInit(); | ||||
if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | ||||
DiagMethodError(MethodParameterSingleDefaultArgument) | DiagMethodError(MethodParameterSingleDefaultArgument) | ||||
<< (I + 1) << (FirstInit == nullptr) | << (I + 1) << (FirstInit == nullptr) | ||||
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | ||||
DiagMethodNote(MethodParameterSingleDefaultArgument) | DiagMethodNote(MethodParameterSingleDefaultArgument) | ||||
<< (I + 1) << (SecondInit == nullptr) | << (I + 1) << (SecondInit == nullptr) | ||||
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstInit && SecondInit && | if (FirstInit && SecondInit && | ||||
computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | ||||
DiagMethodError(MethodParameterDifferentDefaultArgument) | DiagMethodError(MethodParameterDifferentDefaultArgument) | ||||
<< (I + 1) << FirstInit->getSourceRange(); | << (I + 1) << FirstInit->getSourceRange(); | ||||
DiagMethodNote(MethodParameterDifferentDefaultArgument) | DiagMethodNote(MethodParameterDifferentDefaultArgument) | ||||
<< (I + 1) << SecondInit->getSourceRange(); | << (I + 1) << SecondInit->getSourceRange(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (ParameterMismatch) { | const TemplateArgumentList *FirstTemplateArgs = | ||||
Diagnosed = true; | |||||
break; | |||||
} | |||||
const auto *FirstTemplateArgs = | |||||
FirstMethod->getTemplateSpecializationArgs(); | FirstMethod->getTemplateSpecializationArgs(); | ||||
const auto *SecondTemplateArgs = | const TemplateArgumentList *SecondTemplateArgs = | ||||
SecondMethod->getTemplateSpecializationArgs(); | SecondMethod->getTemplateSpecializationArgs(); | ||||
if ((FirstTemplateArgs && !SecondTemplateArgs) || | if ((FirstTemplateArgs && !SecondTemplateArgs) || | ||||
(!FirstTemplateArgs && SecondTemplateArgs)) { | (!FirstTemplateArgs && SecondTemplateArgs)) { | ||||
DiagMethodError(MethodNoTemplateArguments) | DiagMethodError(MethodNoTemplateArguments) | ||||
<< (FirstTemplateArgs != nullptr); | << (FirstTemplateArgs != nullptr); | ||||
DiagMethodNote(MethodNoTemplateArguments) | DiagMethodNote(MethodNoTemplateArguments) | ||||
<< (SecondTemplateArgs != nullptr); | << (SecondTemplateArgs != nullptr); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstTemplateArgs && SecondTemplateArgs) { | if (FirstTemplateArgs && SecondTemplateArgs) { | ||||
// Remove pack expansions from argument list. | // Remove pack expansions from argument list. | ||||
auto ExpandTemplateArgumentList = | auto ExpandTemplateArgumentList = [](const TemplateArgumentList *TAL) { | ||||
[](const TemplateArgumentList *TAL) { | |||||
llvm::SmallVector<const TemplateArgument *, 8> ExpandedList; | llvm::SmallVector<const TemplateArgument *, 8> ExpandedList; | ||||
for (const TemplateArgument &TA : TAL->asArray()) { | for (const TemplateArgument &TA : TAL->asArray()) { | ||||
if (TA.getKind() != TemplateArgument::Pack) { | if (TA.getKind() != TemplateArgument::Pack) { | ||||
ExpandedList.push_back(&TA); | ExpandedList.push_back(&TA); | ||||
continue; | continue; | ||||
} | } | ||||
llvm::append_range(ExpandedList, llvm::make_pointer_range( | llvm::append_range(ExpandedList, | ||||
TA.getPackAsArray())); | llvm::make_pointer_range(TA.getPackAsArray())); | ||||
} | } | ||||
return ExpandedList; | return ExpandedList; | ||||
}; | }; | ||||
llvm::SmallVector<const TemplateArgument *, 8> FirstExpandedList = | llvm::SmallVector<const TemplateArgument *, 8> FirstExpandedList = | ||||
ExpandTemplateArgumentList(FirstTemplateArgs); | ExpandTemplateArgumentList(FirstTemplateArgs); | ||||
llvm::SmallVector<const TemplateArgument *, 8> SecondExpandedList = | llvm::SmallVector<const TemplateArgument *, 8> SecondExpandedList = | ||||
ExpandTemplateArgumentList(SecondTemplateArgs); | ExpandTemplateArgumentList(SecondTemplateArgs); | ||||
if (FirstExpandedList.size() != SecondExpandedList.size()) { | if (FirstExpandedList.size() != SecondExpandedList.size()) { | ||||
DiagMethodError(MethodDifferentNumberTemplateArguments) | DiagMethodError(MethodDifferentNumberTemplateArguments) | ||||
<< (unsigned)FirstExpandedList.size(); | << (unsigned)FirstExpandedList.size(); | ||||
DiagMethodNote(MethodDifferentNumberTemplateArguments) | DiagMethodNote(MethodDifferentNumberTemplateArguments) | ||||
<< (unsigned)SecondExpandedList.size(); | << (unsigned)SecondExpandedList.size(); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
bool TemplateArgumentMismatch = false; | |||||
for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { | for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { | ||||
const TemplateArgument &FirstTA = *FirstExpandedList[i], | const TemplateArgument &FirstTA = *FirstExpandedList[i], | ||||
&SecondTA = *SecondExpandedList[i]; | &SecondTA = *SecondExpandedList[i]; | ||||
if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) { | if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) | ||||
continue; | continue; | ||||
} | |||||
DiagMethodError(MethodDifferentTemplateArgument) | |||||
<< FirstTA << i + 1; | |||||
DiagMethodNote(MethodDifferentTemplateArgument) | |||||
<< SecondTA << i + 1; | |||||
TemplateArgumentMismatch = true; | |||||
break; | |||||
} | |||||
if (TemplateArgumentMismatch) { | DiagMethodError(MethodDifferentTemplateArgument) << FirstTA << i + 1; | ||||
Diagnosed = true; | DiagMethodNote(MethodDifferentTemplateArgument) << SecondTA << i + 1; | ||||
break; | return true; | ||||
} | } | ||||
} | } | ||||
// Compute the hash of the method as if it has no body. | // Compute the hash of the method as if it has no body. | ||||
auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) { | auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) { | ||||
ODRHash Hasher; | ODRHash Hasher; | ||||
Hasher.AddFunctionDecl(D, true /*SkipBody*/); | Hasher.AddFunctionDecl(D, true /*SkipBody*/); | ||||
return Hasher.CalculateHash(); | return Hasher.CalculateHash(); | ||||
}; | }; | ||||
// Compare the hash generated to the hash stored. A difference means | // Compare the hash generated to the hash stored. A difference means | ||||
// that a body was present in the original source. Due to merging, | // that a body was present in the original source. Due to merging, | ||||
// the stardard way of detecting a body will not work. | // the standard way of detecting a body will not work. | ||||
const bool HasFirstBody = | const bool HasFirstBody = | ||||
ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash(); | ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash(); | ||||
const bool HasSecondBody = | const bool HasSecondBody = | ||||
ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); | ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); | ||||
if (HasFirstBody != HasSecondBody) { | if (HasFirstBody != HasSecondBody) { | ||||
DiagMethodError(MethodSingleBody) << HasFirstBody; | DiagMethodError(MethodSingleBody) << HasFirstBody; | ||||
DiagMethodNote(MethodSingleBody) << HasSecondBody; | DiagMethodNote(MethodSingleBody) << HasSecondBody; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (HasFirstBody && HasSecondBody) { | if (HasFirstBody && HasSecondBody) { | ||||
DiagMethodError(MethodDifferentBody); | DiagMethodError(MethodDifferentBody); | ||||
DiagMethodNote(MethodDifferentBody); | DiagMethodNote(MethodDifferentBody); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case TypeAlias: | case TypeAlias: | ||||
case TypeDef: { | case TypeDef: { | ||||
Diagnosed = ODRDiagTypeDefOrAlias( | if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, | ||||
FirstRecord, FirstModule, SecondModule, | cast<TypedefNameDecl>(FirstDecl), | ||||
cast<TypedefNameDecl>(FirstDecl), cast<TypedefNameDecl>(SecondDecl), | cast<TypedefNameDecl>(SecondDecl), | ||||
FirstDiffType == TypeAlias); | FirstDiffType == TypeAlias)) | ||||
return true; | |||||
break; | break; | ||||
} | } | ||||
case Var: { | case Var: { | ||||
Diagnosed = | if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, | ||||
ODRDiagVar(FirstRecord, FirstModule, SecondModule, | cast<VarDecl>(FirstDecl), | ||||
cast<VarDecl>(FirstDecl), cast<VarDecl>(SecondDecl)); | cast<VarDecl>(SecondDecl))) | ||||
return true; | |||||
break; | break; | ||||
} | } | ||||
case Friend: { | case Friend: { | ||||
FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl); | const FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl); | ||||
FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl); | const FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl); | ||||
NamedDecl *FirstND = FirstFriend->getFriendDecl(); | const NamedDecl *FirstND = FirstFriend->getFriendDecl(); | ||||
NamedDecl *SecondND = SecondFriend->getFriendDecl(); | const NamedDecl *SecondND = SecondFriend->getFriendDecl(); | ||||
TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); | TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); | ||||
TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); | TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); | ||||
if (FirstND && SecondND) { | if (FirstND && SecondND) { | ||||
ODRDiagDeclError(FirstFriend->getFriendLoc(), | DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), | ||||
FirstFriend->getSourceRange(), FriendFunction) | FriendFunction) | ||||
<< FirstND; | << FirstND; | ||||
ODRDiagDeclNote(SecondFriend->getFriendLoc(), | DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), | ||||
SecondFriend->getSourceRange(), FriendFunction) | FriendFunction) | ||||
<< SecondND; | << SecondND; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstTSI && SecondTSI) { | if (FirstTSI && SecondTSI) { | ||||
QualType FirstFriendType = FirstTSI->getType(); | QualType FirstFriendType = FirstTSI->getType(); | ||||
QualType SecondFriendType = SecondTSI->getType(); | QualType SecondFriendType = SecondTSI->getType(); | ||||
assert(computeODRHash(FirstFriendType) != | assert(computeODRHash(FirstFriendType) != | ||||
computeODRHash(SecondFriendType)); | computeODRHash(SecondFriendType)); | ||||
ODRDiagDeclError(FirstFriend->getFriendLoc(), | DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), | ||||
FirstFriend->getSourceRange(), FriendType) | FriendType) | ||||
<< FirstFriendType; | << FirstFriendType; | ||||
ODRDiagDeclNote(SecondFriend->getFriendLoc(), | DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), | ||||
SecondFriend->getSourceRange(), FriendType) | FriendType) | ||||
<< SecondFriendType; | << SecondFriendType; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
ODRDiagDeclError(FirstFriend->getFriendLoc(), | DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), | ||||
FirstFriend->getSourceRange(), FriendTypeFunction) | FriendTypeFunction) | ||||
<< (FirstTSI == nullptr); | << (FirstTSI == nullptr); | ||||
ODRDiagDeclNote(SecondFriend->getFriendLoc(), | DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), | ||||
SecondFriend->getSourceRange(), FriendTypeFunction) | FriendTypeFunction) | ||||
<< (SecondTSI == nullptr); | << (SecondTSI == nullptr); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
case FunctionTemplate: { | case FunctionTemplate: { | ||||
FunctionTemplateDecl *FirstTemplate = | const FunctionTemplateDecl *FirstTemplate = | ||||
cast<FunctionTemplateDecl>(FirstDecl); | cast<FunctionTemplateDecl>(FirstDecl); | ||||
FunctionTemplateDecl *SecondTemplate = | const FunctionTemplateDecl *SecondTemplate = | ||||
cast<FunctionTemplateDecl>(SecondDecl); | cast<FunctionTemplateDecl>(SecondDecl); | ||||
TemplateParameterList *FirstTPL = | TemplateParameterList *FirstTPL = FirstTemplate->getTemplateParameters(); | ||||
FirstTemplate->getTemplateParameters(); | TemplateParameterList *SecondTPL = SecondTemplate->getTemplateParameters(); | ||||
TemplateParameterList *SecondTPL = | |||||
SecondTemplate->getTemplateParameters(); | |||||
auto DiagTemplateError = [&ODRDiagDeclError, FirstTemplate]( | auto DiagTemplateError = [&DiagError, | ||||
ODRCXXRecordDifference DiffType) { | FirstTemplate](ODRCXXRecordDifference DiffType) { | ||||
return ODRDiagDeclError(FirstTemplate->getLocation(), | return DiagError(FirstTemplate->getLocation(), | ||||
FirstTemplate->getSourceRange(), DiffType) | FirstTemplate->getSourceRange(), DiffType) | ||||
<< FirstTemplate; | << FirstTemplate; | ||||
}; | }; | ||||
auto DiagTemplateNote = [&ODRDiagDeclNote, SecondTemplate]( | auto DiagTemplateNote = [&DiagNote, | ||||
ODRCXXRecordDifference DiffType) { | SecondTemplate](ODRCXXRecordDifference DiffType) { | ||||
return ODRDiagDeclNote(SecondTemplate->getLocation(), | return DiagNote(SecondTemplate->getLocation(), | ||||
SecondTemplate->getSourceRange(), DiffType) | SecondTemplate->getSourceRange(), DiffType) | ||||
<< SecondTemplate; | << SecondTemplate; | ||||
}; | }; | ||||
if (FirstTPL->size() != SecondTPL->size()) { | if (FirstTPL->size() != SecondTPL->size()) { | ||||
DiagTemplateError(FunctionTemplateDifferentNumberParameters) | DiagTemplateError(FunctionTemplateDifferentNumberParameters) | ||||
<< FirstTPL->size(); | << FirstTPL->size(); | ||||
DiagTemplateNote(FunctionTemplateDifferentNumberParameters) | DiagTemplateNote(FunctionTemplateDifferentNumberParameters) | ||||
<< SecondTPL->size(); | << SecondTPL->size(); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
bool ParameterMismatch = false; | |||||
for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { | for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { | ||||
NamedDecl *FirstParam = FirstTPL->getParam(i); | NamedDecl *FirstParam = FirstTPL->getParam(i); | ||||
NamedDecl *SecondParam = SecondTPL->getParam(i); | NamedDecl *SecondParam = SecondTPL->getParam(i); | ||||
if (FirstParam->getKind() != SecondParam->getKind()) { | if (FirstParam->getKind() != SecondParam->getKind()) { | ||||
enum { | enum { | ||||
TemplateTypeParameter, | TemplateTypeParameter, | ||||
NonTypeTemplateParameter, | NonTypeTemplateParameter, | ||||
TemplateTemplateParameter, | TemplateTemplateParameter, | ||||
}; | }; | ||||
auto GetParamType = [](NamedDecl *D) { | auto GetParamType = [](NamedDecl *D) { | ||||
switch (D->getKind()) { | switch (D->getKind()) { | ||||
default: | default: | ||||
llvm_unreachable("Unexpected template parameter type"); | llvm_unreachable("Unexpected template parameter type"); | ||||
case Decl::TemplateTypeParm: | case Decl::TemplateTypeParm: | ||||
return TemplateTypeParameter; | return TemplateTypeParameter; | ||||
case Decl::NonTypeTemplateParm: | case Decl::NonTypeTemplateParm: | ||||
return NonTypeTemplateParameter; | return NonTypeTemplateParameter; | ||||
case Decl::TemplateTemplateParm: | case Decl::TemplateTemplateParm: | ||||
return TemplateTemplateParameter; | return TemplateTemplateParameter; | ||||
} | } | ||||
}; | }; | ||||
DiagTemplateError(FunctionTemplateParameterDifferentKind) | DiagTemplateError(FunctionTemplateParameterDifferentKind) | ||||
<< (i + 1) << GetParamType(FirstParam); | << (i + 1) << GetParamType(FirstParam); | ||||
DiagTemplateNote(FunctionTemplateParameterDifferentKind) | DiagTemplateNote(FunctionTemplateParameterDifferentKind) | ||||
<< (i + 1) << GetParamType(SecondParam); | << (i + 1) << GetParamType(SecondParam); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstParam->getName() != SecondParam->getName()) { | if (FirstParam->getName() != SecondParam->getName()) { | ||||
DiagTemplateError(FunctionTemplateParameterName) | DiagTemplateError(FunctionTemplateParameterName) | ||||
<< (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; | << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; | ||||
DiagTemplateNote(FunctionTemplateParameterName) | DiagTemplateNote(FunctionTemplateParameterName) | ||||
<< (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; | << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (isa<TemplateTypeParmDecl>(FirstParam) && | if (isa<TemplateTypeParmDecl>(FirstParam) && | ||||
isa<TemplateTypeParmDecl>(SecondParam)) { | isa<TemplateTypeParmDecl>(SecondParam)) { | ||||
TemplateTypeParmDecl *FirstTTPD = | TemplateTypeParmDecl *FirstTTPD = | ||||
cast<TemplateTypeParmDecl>(FirstParam); | cast<TemplateTypeParmDecl>(FirstParam); | ||||
TemplateTypeParmDecl *SecondTTPD = | TemplateTypeParmDecl *SecondTTPD = | ||||
cast<TemplateTypeParmDecl>(SecondParam); | cast<TemplateTypeParmDecl>(SecondParam); | ||||
bool HasFirstDefaultArgument = | bool HasFirstDefaultArgument = | ||||
FirstTTPD->hasDefaultArgument() && | FirstTTPD->hasDefaultArgument() && | ||||
!FirstTTPD->defaultArgumentWasInherited(); | !FirstTTPD->defaultArgumentWasInherited(); | ||||
bool HasSecondDefaultArgument = | bool HasSecondDefaultArgument = | ||||
SecondTTPD->hasDefaultArgument() && | SecondTTPD->hasDefaultArgument() && | ||||
!SecondTTPD->defaultArgumentWasInherited(); | !SecondTTPD->defaultArgumentWasInherited(); | ||||
if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | ||||
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasFirstDefaultArgument; | << (i + 1) << HasFirstDefaultArgument; | ||||
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasSecondDefaultArgument; | << (i + 1) << HasSecondDefaultArgument; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | ||||
QualType FirstType = FirstTTPD->getDefaultArgument(); | QualType FirstType = FirstTTPD->getDefaultArgument(); | ||||
QualType SecondType = SecondTTPD->getDefaultArgument(); | QualType SecondType = SecondTTPD->getDefaultArgument(); | ||||
if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | ||||
DiagTemplateError( | DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << FirstType; | << (i + 1) << FirstType; | ||||
DiagTemplateNote( | DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << SecondType; | << (i + 1) << SecondType; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (FirstTTPD->isParameterPack() != | if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { | ||||
SecondTTPD->isParameterPack()) { | |||||
DiagTemplateError(FunctionTemplatePackParameter) | DiagTemplateError(FunctionTemplatePackParameter) | ||||
<< (i + 1) << FirstTTPD->isParameterPack(); | << (i + 1) << FirstTTPD->isParameterPack(); | ||||
DiagTemplateNote(FunctionTemplatePackParameter) | DiagTemplateNote(FunctionTemplatePackParameter) | ||||
<< (i + 1) << SecondTTPD->isParameterPack(); | << (i + 1) << SecondTTPD->isParameterPack(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (isa<TemplateTemplateParmDecl>(FirstParam) && | if (isa<TemplateTemplateParmDecl>(FirstParam) && | ||||
isa<TemplateTemplateParmDecl>(SecondParam)) { | isa<TemplateTemplateParmDecl>(SecondParam)) { | ||||
TemplateTemplateParmDecl *FirstTTPD = | TemplateTemplateParmDecl *FirstTTPD = | ||||
cast<TemplateTemplateParmDecl>(FirstParam); | cast<TemplateTemplateParmDecl>(FirstParam); | ||||
TemplateTemplateParmDecl *SecondTTPD = | TemplateTemplateParmDecl *SecondTTPD = | ||||
cast<TemplateTemplateParmDecl>(SecondParam); | cast<TemplateTemplateParmDecl>(SecondParam); | ||||
TemplateParameterList *FirstTPL = | TemplateParameterList *FirstTPL = FirstTTPD->getTemplateParameters(); | ||||
FirstTTPD->getTemplateParameters(); | TemplateParameterList *SecondTPL = SecondTTPD->getTemplateParameters(); | ||||
TemplateParameterList *SecondTPL = | |||||
SecondTTPD->getTemplateParameters(); | |||||
if (computeODRHash(FirstTPL) != computeODRHash(SecondTPL)) { | if (computeODRHash(FirstTPL) != computeODRHash(SecondTPL)) { | ||||
DiagTemplateError(FunctionTemplateParameterDifferentType) | DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1); | ||||
<< (i + 1); | DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1); | ||||
DiagTemplateNote(FunctionTemplateParameterDifferentType) | return true; | ||||
<< (i + 1); | |||||
ParameterMismatch = true; | |||||
break; | |||||
} | } | ||||
bool HasFirstDefaultArgument = | bool HasFirstDefaultArgument = | ||||
FirstTTPD->hasDefaultArgument() && | FirstTTPD->hasDefaultArgument() && | ||||
!FirstTTPD->defaultArgumentWasInherited(); | !FirstTTPD->defaultArgumentWasInherited(); | ||||
bool HasSecondDefaultArgument = | bool HasSecondDefaultArgument = | ||||
SecondTTPD->hasDefaultArgument() && | SecondTTPD->hasDefaultArgument() && | ||||
!SecondTTPD->defaultArgumentWasInherited(); | !SecondTTPD->defaultArgumentWasInherited(); | ||||
if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | ||||
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasFirstDefaultArgument; | << (i + 1) << HasFirstDefaultArgument; | ||||
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasSecondDefaultArgument; | << (i + 1) << HasSecondDefaultArgument; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | ||||
TemplateArgument FirstTA = | TemplateArgument FirstTA = | ||||
FirstTTPD->getDefaultArgument().getArgument(); | FirstTTPD->getDefaultArgument().getArgument(); | ||||
TemplateArgument SecondTA = | TemplateArgument SecondTA = | ||||
SecondTTPD->getDefaultArgument().getArgument(); | SecondTTPD->getDefaultArgument().getArgument(); | ||||
if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) { | if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) { | ||||
DiagTemplateError( | DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << FirstTA; | << (i + 1) << FirstTA; | ||||
DiagTemplateNote( | DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << SecondTA; | << (i + 1) << SecondTA; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (FirstTTPD->isParameterPack() != | if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { | ||||
SecondTTPD->isParameterPack()) { | |||||
DiagTemplateError(FunctionTemplatePackParameter) | DiagTemplateError(FunctionTemplatePackParameter) | ||||
<< (i + 1) << FirstTTPD->isParameterPack(); | << (i + 1) << FirstTTPD->isParameterPack(); | ||||
DiagTemplateNote(FunctionTemplatePackParameter) | DiagTemplateNote(FunctionTemplatePackParameter) | ||||
<< (i + 1) << SecondTTPD->isParameterPack(); | << (i + 1) << SecondTTPD->isParameterPack(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (isa<NonTypeTemplateParmDecl>(FirstParam) && | if (isa<NonTypeTemplateParmDecl>(FirstParam) && | ||||
isa<NonTypeTemplateParmDecl>(SecondParam)) { | isa<NonTypeTemplateParmDecl>(SecondParam)) { | ||||
NonTypeTemplateParmDecl *FirstNTTPD = | NonTypeTemplateParmDecl *FirstNTTPD = | ||||
cast<NonTypeTemplateParmDecl>(FirstParam); | cast<NonTypeTemplateParmDecl>(FirstParam); | ||||
NonTypeTemplateParmDecl *SecondNTTPD = | NonTypeTemplateParmDecl *SecondNTTPD = | ||||
cast<NonTypeTemplateParmDecl>(SecondParam); | cast<NonTypeTemplateParmDecl>(SecondParam); | ||||
QualType FirstType = FirstNTTPD->getType(); | QualType FirstType = FirstNTTPD->getType(); | ||||
QualType SecondType = SecondNTTPD->getType(); | QualType SecondType = SecondNTTPD->getType(); | ||||
if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | if (computeODRHash(FirstType) != computeODRHash(SecondType)) { | ||||
DiagTemplateError(FunctionTemplateParameterDifferentType) | DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1); | ||||
<< (i + 1); | DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1); | ||||
DiagTemplateNote(FunctionTemplateParameterDifferentType) | return true; | ||||
<< (i + 1); | |||||
ParameterMismatch = true; | |||||
break; | |||||
} | } | ||||
bool HasFirstDefaultArgument = | bool HasFirstDefaultArgument = | ||||
FirstNTTPD->hasDefaultArgument() && | FirstNTTPD->hasDefaultArgument() && | ||||
!FirstNTTPD->defaultArgumentWasInherited(); | !FirstNTTPD->defaultArgumentWasInherited(); | ||||
bool HasSecondDefaultArgument = | bool HasSecondDefaultArgument = | ||||
SecondNTTPD->hasDefaultArgument() && | SecondNTTPD->hasDefaultArgument() && | ||||
!SecondNTTPD->defaultArgumentWasInherited(); | !SecondNTTPD->defaultArgumentWasInherited(); | ||||
if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | if (HasFirstDefaultArgument != HasSecondDefaultArgument) { | ||||
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasFirstDefaultArgument; | << (i + 1) << HasFirstDefaultArgument; | ||||
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) | ||||
<< (i + 1) << HasSecondDefaultArgument; | << (i + 1) << HasSecondDefaultArgument; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | if (HasFirstDefaultArgument && HasSecondDefaultArgument) { | ||||
Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument(); | Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument(); | ||||
Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); | Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); | ||||
if (computeODRHash(FirstDefaultArgument) != | if (computeODRHash(FirstDefaultArgument) != | ||||
computeODRHash(SecondDefaultArgument)) { | computeODRHash(SecondDefaultArgument)) { | ||||
DiagTemplateError( | DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << FirstDefaultArgument; | << (i + 1) << FirstDefaultArgument; | ||||
DiagTemplateNote( | DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) | ||||
FunctionTemplateParameterDifferentDefaultArgument) | |||||
<< (i + 1) << SecondDefaultArgument; | << (i + 1) << SecondDefaultArgument; | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (FirstNTTPD->isParameterPack() != | if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) { | ||||
SecondNTTPD->isParameterPack()) { | |||||
DiagTemplateError(FunctionTemplatePackParameter) | DiagTemplateError(FunctionTemplatePackParameter) | ||||
<< (i + 1) << FirstNTTPD->isParameterPack(); | << (i + 1) << FirstNTTPD->isParameterPack(); | ||||
DiagTemplateNote(FunctionTemplatePackParameter) | DiagTemplateNote(FunctionTemplatePackParameter) | ||||
<< (i + 1) << SecondNTTPD->isParameterPack(); | << (i + 1) << SecondNTTPD->isParameterPack(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | |||||
} | } | ||||
} | } | ||||
if (ParameterMismatch) { | |||||
Diagnosed = true; | |||||
break; | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (Diagnosed) | |||||
continue; | |||||
Diag(FirstDecl->getLocation(), | Diag(FirstDecl->getLocation(), | ||||
diag::err_module_odr_violation_mismatch_decl_unknown) | diag::err_module_odr_violation_mismatch_decl_unknown) | ||||
<< FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType | << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType | ||||
<< FirstDecl->getSourceRange(); | << FirstDecl->getSourceRange(); | ||||
Diag(SecondDecl->getLocation(), | Diag(SecondDecl->getLocation(), | ||||
diag::note_module_odr_violation_mismatch_decl_unknown) | diag::note_module_odr_violation_mismatch_decl_unknown) | ||||
<< SecondModule << FirstDiffType << SecondDecl->getSourceRange(); | << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); | ||||
Diagnosed = true; | return true; | ||||
} | } | ||||
if (!Diagnosed) { | bool ODRDiagsEmitter::diagnoseMismatch( | ||||
// All definitions are updates to the same declaration. This happens if a | const FunctionDecl *FirstFunction, | ||||
// module instantiates the declaration of a class template specialization | const FunctionDecl *SecondFunction) const { | ||||
// and two or more other modules instantiate its definition. | if (FirstFunction == SecondFunction) | ||||
// | return false; | ||||
// 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; | |||||
} | |||||
} | |||||
// Issue ODR failures diagnostics for functions. | // Keep in sync with select options in err_module_odr_violation_function. | ||||
for (auto &Merge : FunctionOdrMergeFailures) { | |||||
enum ODRFunctionDifference { | enum ODRFunctionDifference { | ||||
ReturnType, | ReturnType, | ||||
ParameterName, | ParameterName, | ||||
ParameterType, | ParameterType, | ||||
ParameterSingleDefaultArgument, | ParameterSingleDefaultArgument, | ||||
ParameterDifferentDefaultArgument, | ParameterDifferentDefaultArgument, | ||||
FunctionBody, | FunctionBody, | ||||
}; | }; | ||||
FunctionDecl *FirstFunction = Merge.first; | |||||
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); | std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); | ||||
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction); | |||||
bool Diagnosed = false; | auto DiagError = [FirstFunction, &FirstModule, | ||||
for (auto &SecondFunction : Merge.second) { | |||||
if (FirstFunction == SecondFunction) | |||||
continue; | |||||
std::string SecondModule = | |||||
getOwningModuleNameForDiagnostic(SecondFunction); | |||||
auto ODRDiagError = [FirstFunction, &FirstModule, | |||||
this](SourceLocation Loc, SourceRange Range, | this](SourceLocation Loc, SourceRange Range, | ||||
ODRFunctionDifference DiffType) { | ODRFunctionDifference DiffType) { | ||||
return Diag(Loc, diag::err_module_odr_violation_function) | return Diag(Loc, diag::err_module_odr_violation_function) | ||||
<< FirstFunction << FirstModule.empty() << FirstModule << Range | << FirstFunction << FirstModule.empty() << FirstModule << Range | ||||
<< DiffType; | << DiffType; | ||||
}; | }; | ||||
auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc, | auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range, | ||||
SourceRange Range, | |||||
ODRFunctionDifference DiffType) { | ODRFunctionDifference DiffType) { | ||||
return Diag(Loc, diag::note_module_odr_violation_function) | return Diag(Loc, diag::note_module_odr_violation_function) | ||||
<< SecondModule << Range << DiffType; | << SecondModule << Range << DiffType; | ||||
}; | }; | ||||
if (computeODRHash(FirstFunction->getReturnType()) != | if (computeODRHash(FirstFunction->getReturnType()) != | ||||
computeODRHash(SecondFunction->getReturnType())) { | computeODRHash(SecondFunction->getReturnType())) { | ||||
ODRDiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), | DiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), | ||||
FirstFunction->getReturnTypeSourceRange(), ReturnType) | FirstFunction->getReturnTypeSourceRange(), ReturnType) | ||||
<< FirstFunction->getReturnType(); | << FirstFunction->getReturnType(); | ||||
ODRDiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), | DiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), | ||||
SecondFunction->getReturnTypeSourceRange(), ReturnType) | SecondFunction->getReturnTypeSourceRange(), ReturnType) | ||||
<< SecondFunction->getReturnType(); | << SecondFunction->getReturnType(); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
assert(FirstFunction->param_size() == SecondFunction->param_size() && | assert(FirstFunction->param_size() == SecondFunction->param_size() && | ||||
"Merged functions with different number of parameters"); | "Merged functions with different number of parameters"); | ||||
auto ParamSize = FirstFunction->param_size(); | size_t ParamSize = FirstFunction->param_size(); | ||||
bool ParameterMismatch = false; | |||||
for (unsigned I = 0; I < ParamSize; ++I) { | for (unsigned I = 0; I < ParamSize; ++I) { | ||||
auto *FirstParam = FirstFunction->getParamDecl(I); | const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I); | ||||
auto *SecondParam = SecondFunction->getParamDecl(I); | const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I); | ||||
assert(getContext().hasSameType(FirstParam->getType(), | assert(Context.hasSameType(FirstParam->getType(), SecondParam->getType()) && | ||||
SecondParam->getType()) && | |||||
"Merged function has different parameter types."); | "Merged function has different parameter types."); | ||||
if (FirstParam->getDeclName() != SecondParam->getDeclName()) { | if (FirstParam->getDeclName() != SecondParam->getDeclName()) { | ||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | ||||
ParameterName) | ParameterName) | ||||
<< I + 1 << FirstParam->getDeclName(); | << I + 1 << FirstParam->getDeclName(); | ||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | ||||
ParameterName) | ParameterName) | ||||
<< I + 1 << SecondParam->getDeclName(); | << I + 1 << SecondParam->getDeclName(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
}; | }; | ||||
QualType FirstParamType = FirstParam->getType(); | QualType FirstParamType = FirstParam->getType(); | ||||
QualType SecondParamType = SecondParam->getType(); | QualType SecondParamType = SecondParam->getType(); | ||||
if (FirstParamType != SecondParamType && | if (FirstParamType != SecondParamType && | ||||
computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { | computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { | ||||
if (const DecayedType *ParamDecayedType = | if (const DecayedType *ParamDecayedType = | ||||
FirstParamType->getAs<DecayedType>()) { | FirstParamType->getAs<DecayedType>()) { | ||||
ODRDiagError(FirstParam->getLocation(), | DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | ||||
FirstParam->getSourceRange(), ParameterType) | ParameterType) | ||||
<< (I + 1) << FirstParamType << true | << (I + 1) << FirstParamType << true | ||||
<< ParamDecayedType->getOriginalType(); | << ParamDecayedType->getOriginalType(); | ||||
} else { | } else { | ||||
ODRDiagError(FirstParam->getLocation(), | DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | ||||
FirstParam->getSourceRange(), ParameterType) | ParameterType) | ||||
<< (I + 1) << FirstParamType << false; | << (I + 1) << FirstParamType << false; | ||||
} | } | ||||
if (const DecayedType *ParamDecayedType = | if (const DecayedType *ParamDecayedType = | ||||
SecondParamType->getAs<DecayedType>()) { | SecondParamType->getAs<DecayedType>()) { | ||||
ODRDiagNote(SecondParam->getLocation(), | DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | ||||
SecondParam->getSourceRange(), ParameterType) | ParameterType) | ||||
<< (I + 1) << SecondParamType << true | << (I + 1) << SecondParamType << true | ||||
<< ParamDecayedType->getOriginalType(); | << ParamDecayedType->getOriginalType(); | ||||
} else { | } else { | ||||
ODRDiagNote(SecondParam->getLocation(), | DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | ||||
SecondParam->getSourceRange(), ParameterType) | ParameterType) | ||||
<< (I + 1) << SecondParamType << false; | << (I + 1) << SecondParamType << false; | ||||
} | } | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
const Expr *FirstInit = FirstParam->getInit(); | const Expr *FirstInit = FirstParam->getInit(); | ||||
const Expr *SecondInit = SecondParam->getInit(); | const Expr *SecondInit = SecondParam->getInit(); | ||||
if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | if ((FirstInit == nullptr) != (SecondInit == nullptr)) { | ||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | ||||
ParameterSingleDefaultArgument) | ParameterSingleDefaultArgument) | ||||
<< (I + 1) << (FirstInit == nullptr) | << (I + 1) << (FirstInit == nullptr) | ||||
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); | ||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | ||||
ParameterSingleDefaultArgument) | ParameterSingleDefaultArgument) | ||||
<< (I + 1) << (SecondInit == nullptr) | << (I + 1) << (SecondInit == nullptr) | ||||
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
if (FirstInit && SecondInit && | if (FirstInit && SecondInit && | ||||
computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | ||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), | ||||
ParameterDifferentDefaultArgument) | ParameterDifferentDefaultArgument) | ||||
<< (I + 1) << FirstInit->getSourceRange(); | << (I + 1) << FirstInit->getSourceRange(); | ||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), | ||||
ParameterDifferentDefaultArgument) | ParameterDifferentDefaultArgument) | ||||
<< (I + 1) << SecondInit->getSourceRange(); | << (I + 1) << SecondInit->getSourceRange(); | ||||
ParameterMismatch = true; | return true; | ||||
break; | |||||
} | } | ||||
assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && | assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && | ||||
"Undiagnosed parameter difference."); | "Undiagnosed parameter difference."); | ||||
} | } | ||||
if (ParameterMismatch) { | |||||
Diagnosed = true; | |||||
break; | |||||
} | |||||
// If no error has been generated before now, assume the problem is in | // If no error has been generated before now, assume the problem is in | ||||
// the body and generate a message. | // the body and generate a message. | ||||
ODRDiagError(FirstFunction->getLocation(), | DiagError(FirstFunction->getLocation(), FirstFunction->getSourceRange(), | ||||
FirstFunction->getSourceRange(), FunctionBody); | FunctionBody); | ||||
ODRDiagNote(SecondFunction->getLocation(), | DiagNote(SecondFunction->getLocation(), SecondFunction->getSourceRange(), | ||||
SecondFunction->getSourceRange(), FunctionBody); | FunctionBody); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | |||||
(void)Diagnosed; | |||||
assert(Diagnosed && "Unable to emit ODR diagnostic."); | |||||
} | } | ||||
// Issue ODR failures diagnostics for enums. | bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum, | ||||
for (auto &Merge : EnumOdrMergeFailures) { | const EnumDecl *SecondEnum) const { | ||||
if (FirstEnum == SecondEnum) | |||||
return false; | |||||
// Keep in sync with select options in err_module_odr_violation_enum. | |||||
enum ODREnumDifference { | enum ODREnumDifference { | ||||
SingleScopedEnum, | SingleScopedEnum, | ||||
EnumTagKeywordMismatch, | EnumTagKeywordMismatch, | ||||
SingleSpecifiedType, | SingleSpecifiedType, | ||||
DifferentSpecifiedTypes, | DifferentSpecifiedTypes, | ||||
DifferentNumberEnumConstants, | DifferentNumberEnumConstants, | ||||
EnumConstantName, | EnumConstantName, | ||||
EnumConstantSingleInitilizer, | EnumConstantSingleInitilizer, | ||||
EnumConstantDifferentInitilizer, | EnumConstantDifferentInitilizer, | ||||
}; | }; | ||||
// If we've already pointed out a specific problem with this enum, don't | |||||
// bother issuing a general "something's different" diagnostic. | |||||
if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) | |||||
continue; | |||||
EnumDecl *FirstEnum = Merge.first; | |||||
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); | std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); | ||||
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum); | |||||
using DeclHashes = | auto DiagError = [FirstEnum, &FirstModule, this](const auto *DiagAnchor, | ||||
llvm::SmallVector<std::pair<EnumConstantDecl *, unsigned>, 4>; | |||||
auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, EnumDecl *Enum) { | |||||
for (auto *D : Enum->decls()) { | |||||
// Due to decl merging, the first EnumDecl is the parent of | |||||
// Decls in both records. | |||||
if (!ODRHash::isDeclToBeProcessed(D, FirstEnum)) | |||||
continue; | |||||
assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind"); | |||||
Hashes.emplace_back(cast<EnumConstantDecl>(D), computeODRHash(D)); | |||||
} | |||||
}; | |||||
DeclHashes FirstHashes; | |||||
PopulateHashes(FirstHashes, FirstEnum); | |||||
bool Diagnosed = false; | |||||
for (auto &SecondEnum : Merge.second) { | |||||
if (FirstEnum == SecondEnum) | |||||
continue; | |||||
std::string SecondModule = | |||||
getOwningModuleNameForDiagnostic(SecondEnum); | |||||
auto ODRDiagError = [FirstEnum, &FirstModule, | |||||
this](const auto *DiagAnchor, | |||||
ODREnumDifference DiffType) { | ODREnumDifference DiffType) { | ||||
return Diag(DiagAnchor->getLocation(), | return Diags.Report(DiagAnchor->getLocation(), | ||||
diag::err_module_odr_violation_enum) | diag::err_module_odr_violation_enum) | ||||
<< FirstEnum << FirstModule.empty() << FirstModule | << FirstEnum << FirstModule.empty() << FirstModule | ||||
<< DiagAnchor->getSourceRange() << DiffType; | << DiagAnchor->getSourceRange() << DiffType; | ||||
}; | }; | ||||
auto ODRDiagNote = [&SecondModule, this](const auto *DiagAnchor, | auto DiagNote = [&SecondModule, this](const auto *DiagAnchor, | ||||
ODREnumDifference DiffType) { | ODREnumDifference DiffType) { | ||||
return Diag(DiagAnchor->getLocation(), | return Diags.Report(DiagAnchor->getLocation(), | ||||
diag::note_module_odr_violation_enum) | diag::note_module_odr_violation_enum) | ||||
<< SecondModule << DiagAnchor->getSourceRange() << DiffType; | << SecondModule << DiagAnchor->getSourceRange() << DiffType; | ||||
}; | }; | ||||
if (FirstEnum->isScoped() != SecondEnum->isScoped()) { | if (FirstEnum->isScoped() != SecondEnum->isScoped()) { | ||||
ODRDiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); | DiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); | ||||
ODRDiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); | DiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); | ||||
Diagnosed = true; | return true; | ||||
continue; | |||||
} | } | ||||
if (FirstEnum->isScoped() && SecondEnum->isScoped()) { | if (FirstEnum->isScoped() && SecondEnum->isScoped()) { | ||||
if (FirstEnum->isScopedUsingClassTag() != | if (FirstEnum->isScopedUsingClassTag() != | ||||
SecondEnum->isScopedUsingClassTag()) { | SecondEnum->isScopedUsingClassTag()) { | ||||
ODRDiagError(FirstEnum, EnumTagKeywordMismatch) | DiagError(FirstEnum, EnumTagKeywordMismatch) | ||||
<< FirstEnum->isScopedUsingClassTag(); | << FirstEnum->isScopedUsingClassTag(); | ||||
ODRDiagNote(SecondEnum, EnumTagKeywordMismatch) | DiagNote(SecondEnum, EnumTagKeywordMismatch) | ||||
<< SecondEnum->isScopedUsingClassTag(); | << SecondEnum->isScopedUsingClassTag(); | ||||
Diagnosed = true; | return true; | ||||
continue; | |||||
} | } | ||||
} | } | ||||
QualType FirstUnderlyingType = | QualType FirstUnderlyingType = | ||||
FirstEnum->getIntegerTypeSourceInfo() | FirstEnum->getIntegerTypeSourceInfo() | ||||
? FirstEnum->getIntegerTypeSourceInfo()->getType() | ? FirstEnum->getIntegerTypeSourceInfo()->getType() | ||||
: QualType(); | : QualType(); | ||||
QualType SecondUnderlyingType = | QualType SecondUnderlyingType = | ||||
SecondEnum->getIntegerTypeSourceInfo() | SecondEnum->getIntegerTypeSourceInfo() | ||||
? SecondEnum->getIntegerTypeSourceInfo()->getType() | ? SecondEnum->getIntegerTypeSourceInfo()->getType() | ||||
: QualType(); | : QualType(); | ||||
if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) { | if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) { | ||||
ODRDiagError(FirstEnum, SingleSpecifiedType) | DiagError(FirstEnum, SingleSpecifiedType) << !FirstUnderlyingType.isNull(); | ||||
<< !FirstUnderlyingType.isNull(); | DiagNote(SecondEnum, SingleSpecifiedType) << !SecondUnderlyingType.isNull(); | ||||
ODRDiagNote(SecondEnum, SingleSpecifiedType) | return true; | ||||
<< !SecondUnderlyingType.isNull(); | |||||
Diagnosed = true; | |||||
continue; | |||||
} | } | ||||
if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { | if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { | ||||
if (computeODRHash(FirstUnderlyingType) != | if (computeODRHash(FirstUnderlyingType) != | ||||
computeODRHash(SecondUnderlyingType)) { | computeODRHash(SecondUnderlyingType)) { | ||||
ODRDiagError(FirstEnum, DifferentSpecifiedTypes) | DiagError(FirstEnum, DifferentSpecifiedTypes) << FirstUnderlyingType; | ||||
<< FirstUnderlyingType; | DiagNote(SecondEnum, DifferentSpecifiedTypes) << SecondUnderlyingType; | ||||
ODRDiagNote(SecondEnum, DifferentSpecifiedTypes) | return true; | ||||
<< SecondUnderlyingType; | |||||
Diagnosed = true; | |||||
continue; | |||||
} | } | ||||
} | } | ||||
// Compare enum constants. | |||||
using DeclHashes = | |||||
llvm::SmallVector<std::pair<const EnumConstantDecl *, unsigned>, 4>; | |||||
auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, const EnumDecl *Enum) { | |||||
for (const Decl *D : Enum->decls()) { | |||||
// Due to decl merging, the first EnumDecl is the parent of | |||||
// Decls in both records. | |||||
if (!ODRHash::isDeclToBeProcessed(D, FirstEnum)) | |||||
continue; | |||||
assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind"); | |||||
Hashes.emplace_back(cast<EnumConstantDecl>(D), computeODRHash(D)); | |||||
} | |||||
}; | |||||
DeclHashes FirstHashes; | |||||
PopulateHashes(FirstHashes, FirstEnum); | |||||
DeclHashes SecondHashes; | DeclHashes SecondHashes; | ||||
PopulateHashes(SecondHashes, SecondEnum); | PopulateHashes(SecondHashes, SecondEnum); | ||||
if (FirstHashes.size() != SecondHashes.size()) { | if (FirstHashes.size() != SecondHashes.size()) { | ||||
ODRDiagError(FirstEnum, DifferentNumberEnumConstants) | DiagError(FirstEnum, DifferentNumberEnumConstants) | ||||
<< (int)FirstHashes.size(); | << (int)FirstHashes.size(); | ||||
ODRDiagNote(SecondEnum, DifferentNumberEnumConstants) | DiagNote(SecondEnum, DifferentNumberEnumConstants) | ||||
<< (int)SecondHashes.size(); | << (int)SecondHashes.size(); | ||||
Diagnosed = true; | return true; | ||||
continue; | |||||
} | } | ||||
for (unsigned I = 0; I < FirstHashes.size(); ++I) { | for (unsigned I = 0, N = FirstHashes.size(); I < N; ++I) { | ||||
if (FirstHashes[I].second == SecondHashes[I].second) | if (FirstHashes[I].second == SecondHashes[I].second) | ||||
continue; | continue; | ||||
const EnumConstantDecl *FirstEnumConstant = FirstHashes[I].first; | const EnumConstantDecl *FirstConstant = FirstHashes[I].first; | ||||
const EnumConstantDecl *SecondEnumConstant = SecondHashes[I].first; | const EnumConstantDecl *SecondConstant = SecondHashes[I].first; | ||||
if (FirstEnumConstant->getDeclName() != | |||||
SecondEnumConstant->getDeclName()) { | |||||
ODRDiagError(FirstEnumConstant, EnumConstantName) | if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) { | ||||
<< I + 1 << FirstEnumConstant; | DiagError(FirstConstant, EnumConstantName) << I + 1 << FirstConstant; | ||||
ODRDiagNote(SecondEnumConstant, EnumConstantName) | DiagNote(SecondConstant, EnumConstantName) << I + 1 << SecondConstant; | ||||
<< I + 1 << SecondEnumConstant; | return true; | ||||
Diagnosed = true; | |||||
break; | |||||
} | } | ||||
const Expr *FirstInit = FirstEnumConstant->getInitExpr(); | const Expr *FirstInit = FirstConstant->getInitExpr(); | ||||
const Expr *SecondInit = SecondEnumConstant->getInitExpr(); | const Expr *SecondInit = SecondConstant->getInitExpr(); | ||||
if (!FirstInit && !SecondInit) | if (!FirstInit && !SecondInit) | ||||
continue; | continue; | ||||
if (!FirstInit || !SecondInit) { | if (!FirstInit || !SecondInit) { | ||||
ODRDiagError(FirstEnumConstant, EnumConstantSingleInitilizer) | DiagError(FirstConstant, EnumConstantSingleInitilizer) | ||||
<< I + 1 << FirstEnumConstant << (FirstInit != nullptr); | << I + 1 << FirstConstant << (FirstInit != nullptr); | ||||
ODRDiagNote(SecondEnumConstant, EnumConstantSingleInitilizer) | DiagNote(SecondConstant, EnumConstantSingleInitilizer) | ||||
<< I + 1 << SecondEnumConstant << (SecondInit != nullptr); | << I + 1 << SecondConstant << (SecondInit != nullptr); | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | } | ||||
if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { | ||||
ODRDiagError(FirstEnumConstant, EnumConstantDifferentInitilizer) | DiagError(FirstConstant, EnumConstantDifferentInitilizer) | ||||
<< I + 1 << FirstEnumConstant; | << I + 1 << FirstConstant; | ||||
ODRDiagNote(SecondEnumConstant, EnumConstantDifferentInitilizer) | DiagNote(SecondConstant, EnumConstantDifferentInitilizer) | ||||
<< I + 1 << SecondEnumConstant; | << I + 1 << SecondConstant; | ||||
Diagnosed = true; | return true; | ||||
break; | |||||
} | |||||
} | |||||
} | } | ||||
(void)Diagnosed; | |||||
assert(Diagnosed && "Unable to emit ODR diagnostic."); | |||||
} | } | ||||
return false; | |||||
} | } | ||||
void ASTReader::StartedDeserializing() { | void ASTReader::StartedDeserializing() { | ||||
if (++NumCurrentElementsDeserializing == 1 && ReadTimer.get()) | if (++NumCurrentElementsDeserializing == 1 && ReadTimer.get()) | ||||
ReadTimer->startTimer(); | ReadTimer->startTimer(); | ||||
} | } | ||||
void ASTReader::FinishedDeserializing() { | void ASTReader::FinishedDeserializing() { | ||||
▲ Show 20 Lines • Show All 1,532 Lines • Show Last 20 Lines |
At first I though this part was important for certain tests. But I guess I've messed up somewhere else.