forked from OSchip/llvm-project
[ODRHash] Support ODR violation detection in functions.
Extend the hashing to functions, which allows detection of function definition mismatches across modules. llvm-svn: 320230
This commit is contained in:
parent
a249c4f513
commit
e81caeb314
|
@ -1760,6 +1760,11 @@ protected:
|
|||
unsigned IsCopyDeductionCandidate : 1;
|
||||
|
||||
private:
|
||||
|
||||
/// Store the ODRHash after first calculation.
|
||||
unsigned HasODRHash : 1;
|
||||
unsigned ODRHash;
|
||||
|
||||
/// \brief End part of this FunctionDecl's source range.
|
||||
///
|
||||
/// We could compute the full range in getSourceRange(). However, when we're
|
||||
|
@ -1842,8 +1847,9 @@ protected:
|
|||
IsExplicitlyDefaulted(false), HasImplicitReturnZero(false),
|
||||
IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified),
|
||||
InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false),
|
||||
WillHaveBody(false), IsCopyDeductionCandidate(false),
|
||||
EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {}
|
||||
WillHaveBody(false), IsCopyDeductionCandidate(false), HasODRHash(false),
|
||||
ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()),
|
||||
DNLoc(NameInfo.getInfo()) {}
|
||||
|
||||
using redeclarable_base = Redeclarable<FunctionDecl>;
|
||||
|
||||
|
@ -2443,6 +2449,10 @@ public:
|
|||
/// returns 0.
|
||||
unsigned getMemoryFunctionKind() const;
|
||||
|
||||
/// \brief Returns ODRHash of the function. This value is calculated and
|
||||
/// stored on first call, then the stored value returned on the other calls.
|
||||
unsigned getODRHash();
|
||||
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) {
|
||||
|
|
|
@ -53,6 +53,10 @@ public:
|
|||
// more information than the AddDecl class.
|
||||
void AddCXXRecordDecl(const CXXRecordDecl *Record);
|
||||
|
||||
// Use this for ODR checking functions between modules. This method compares
|
||||
// more information than the AddDecl class.
|
||||
void AddFunctionDecl(const FunctionDecl *Function);
|
||||
|
||||
// Process SubDecls of the main Decl. This method calls the DeclVisitor
|
||||
// while AddDecl does not.
|
||||
void AddSubDecl(const Decl *D);
|
||||
|
|
|
@ -270,6 +270,29 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
|
|||
"friend function %2|"
|
||||
"}1">;
|
||||
|
||||
def err_module_odr_violation_function : Error<
|
||||
"%q0 has different definitions in different modules; "
|
||||
"%select{definition in module '%2'|defined here}1 "
|
||||
"first difference is "
|
||||
"%select{"
|
||||
"return type is %4|"
|
||||
"%ordinal4 parameter with name %5|"
|
||||
"%ordinal4 parameter with type %5%select{| decayed from %7}6|"
|
||||
"%ordinal4 parameter with%select{out|}5 a default argument|"
|
||||
"%ordinal4 parameter with a default argument|"
|
||||
"function body"
|
||||
"}3">;
|
||||
|
||||
def note_module_odr_violation_function : Note<"but in '%0' found "
|
||||
"%select{"
|
||||
"different return type %2|"
|
||||
"%ordinal2 parameter with name %3|"
|
||||
"%ordinal2 parameter with type %3%select{| decayed from %5}4|"
|
||||
"%ordinal2 parameter with%select{out|}3 a default argument|"
|
||||
"%ordinal2 parameter with a different default argument|"
|
||||
"a different body"
|
||||
"}1">;
|
||||
|
||||
def err_module_odr_violation_mismatch_decl_unknown : Error<
|
||||
"%q0 %select{with definition in module '%2'|defined here}1 has different "
|
||||
"definitions in different modules; first difference is this "
|
||||
|
|
|
@ -1092,6 +1092,10 @@ private:
|
|||
llvm::SmallDenseMap<CXXRecordDecl *, llvm::SmallVector<DataPointers, 2>, 2>
|
||||
PendingOdrMergeFailures;
|
||||
|
||||
/// \brief Function definitions in which we found an ODR violation.
|
||||
llvm::SmallDenseMap<FunctionDecl *, llvm::SmallVector<FunctionDecl *, 2>, 2>
|
||||
PendingFunctionOdrMergeFailures;
|
||||
|
||||
/// \brief DeclContexts in which we have diagnosed an ODR violation.
|
||||
llvm::SmallPtrSet<DeclContext*, 2> DiagnosedOdrMergeFailures;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
#include "clang/AST/ODRHash.h"
|
||||
#include "clang/AST/PrettyPrinter.h"
|
||||
#include "clang/AST/Redeclarable.h"
|
||||
#include "clang/AST/Stmt.h"
|
||||
|
@ -3601,6 +3602,25 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned FunctionDecl::getODRHash() {
|
||||
if (HasODRHash)
|
||||
return ODRHash;
|
||||
|
||||
if (FunctionDecl *Definition = getDefinition()) {
|
||||
if (Definition != this) {
|
||||
HasODRHash = true;
|
||||
ODRHash = Definition->getODRHash();
|
||||
return ODRHash;
|
||||
}
|
||||
}
|
||||
|
||||
class ODRHash Hash;
|
||||
Hash.AddFunctionDecl(this);
|
||||
HasODRHash = true;
|
||||
ODRHash = Hash.CalculateHash();
|
||||
return ODRHash;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FieldDecl Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -466,6 +466,36 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
|
|||
}
|
||||
}
|
||||
|
||||
void ODRHash::AddFunctionDecl(const FunctionDecl *Function) {
|
||||
assert(Function && "Expecting non-null pointer.");
|
||||
|
||||
// Skip hashing these kinds of function.
|
||||
if (Function->isImplicit()) return;
|
||||
if (Function->isDefaulted()) return;
|
||||
if (Function->isDeleted()) return;
|
||||
if (!Function->hasBody()) return;
|
||||
if (!Function->getBody()) return;
|
||||
|
||||
// Skip functions that are specializations or in specialization context.
|
||||
const DeclContext *DC = Function;
|
||||
while (DC) {
|
||||
if (isa<ClassTemplateSpecializationDecl>(DC)) return;
|
||||
if (auto *F = dyn_cast<FunctionDecl>(DC))
|
||||
if (F->isFunctionTemplateSpecialization()) return;
|
||||
DC = DC->getParent();
|
||||
}
|
||||
|
||||
AddDecl(Function);
|
||||
|
||||
AddQualType(Function->getReturnType());
|
||||
|
||||
ID.AddInteger(Function->param_size());
|
||||
for (auto Param : Function->parameters())
|
||||
AddSubDecl(Param);
|
||||
|
||||
AddStmt(Function->getBody());
|
||||
}
|
||||
|
||||
void ODRHash::AddDecl(const Decl *D) {
|
||||
assert(D && "Expecting non-null pointer.");
|
||||
auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size()));
|
||||
|
|
|
@ -9235,8 +9235,16 @@ void ASTReader::finishPendingActions() {
|
|||
const FunctionDecl *Defn = nullptr;
|
||||
if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) {
|
||||
FD->setLazyBody(PB->second);
|
||||
} else
|
||||
mergeDefinitionVisibility(const_cast<FunctionDecl*>(Defn), FD);
|
||||
} else {
|
||||
auto *NonConstDefn = const_cast<FunctionDecl*>(Defn);
|
||||
mergeDefinitionVisibility(NonConstDefn, FD);
|
||||
|
||||
if (!FD->isLateTemplateParsed() &&
|
||||
!NonConstDefn->isLateTemplateParsed() &&
|
||||
FD->getODRHash() != NonConstDefn->getODRHash()) {
|
||||
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -9253,7 +9261,8 @@ void ASTReader::finishPendingActions() {
|
|||
}
|
||||
|
||||
void ASTReader::diagnoseOdrViolations() {
|
||||
if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty())
|
||||
if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() &&
|
||||
PendingFunctionOdrMergeFailures.empty())
|
||||
return;
|
||||
|
||||
// Trigger the import of the full definition of each class that had any
|
||||
|
@ -9275,6 +9284,20 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
}
|
||||
}
|
||||
|
||||
// Trigger the import of functions.
|
||||
auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures);
|
||||
PendingFunctionOdrMergeFailures.clear();
|
||||
for (auto &Merge : FunctionOdrMergeFailures) {
|
||||
Merge.first->buildLookup();
|
||||
Merge.first->decls_begin();
|
||||
Merge.first->getBody();
|
||||
for (auto &FD : Merge.second) {
|
||||
FD->buildLookup();
|
||||
FD->decls_begin();
|
||||
FD->getBody();
|
||||
}
|
||||
}
|
||||
|
||||
// For each declaration from a merged context, check that the canonical
|
||||
// definition of that context also contains a declaration of the same
|
||||
// entity.
|
||||
|
@ -9357,13 +9380,35 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
}
|
||||
}
|
||||
|
||||
if (OdrMergeFailures.empty())
|
||||
if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty())
|
||||
return;
|
||||
|
||||
// Ensure we don't accidentally recursively enter deserialization while
|
||||
// we're producing our diagnostics.
|
||||
Deserializing RecursionGuard(this);
|
||||
|
||||
// Common code for hashing helpers.
|
||||
ODRHash Hash;
|
||||
auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
|
||||
Hash.clear();
|
||||
Hash.AddQualType(Ty);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
auto ComputeODRHash = [&Hash](const Stmt *S) {
|
||||
assert(S);
|
||||
Hash.clear();
|
||||
Hash.AddStmt(S);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
auto ComputeSubDeclODRHash = [&Hash](const Decl *D) {
|
||||
assert(D);
|
||||
Hash.clear();
|
||||
Hash.AddSubDecl(D);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
// Issue any pending ODR-failure diagnostics.
|
||||
for (auto &Merge : OdrMergeFailures) {
|
||||
// If we've already pointed out a specific problem with this class, don't
|
||||
|
@ -9411,13 +9456,6 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
<< SecondModule << Range << DiffType;
|
||||
};
|
||||
|
||||
ODRHash Hash;
|
||||
auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
|
||||
Hash.clear();
|
||||
Hash.AddQualType(Ty);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
unsigned FirstNumBases = FirstDD->NumBases;
|
||||
unsigned FirstNumVBases = FirstDD->NumVBases;
|
||||
unsigned SecondNumBases = SecondDD->NumBases;
|
||||
|
@ -9520,14 +9558,12 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
if (FirstTemplate && SecondTemplate) {
|
||||
DeclHashes FirstTemplateHashes;
|
||||
DeclHashes SecondTemplateHashes;
|
||||
ODRHash Hash;
|
||||
|
||||
auto PopulateTemplateParameterHashs =
|
||||
[&Hash](DeclHashes &Hashes, const ClassTemplateDecl *TD) {
|
||||
[&ComputeSubDeclODRHash](DeclHashes &Hashes,
|
||||
const ClassTemplateDecl *TD) {
|
||||
for (auto *D : TD->getTemplateParameters()->asArray()) {
|
||||
Hash.clear();
|
||||
Hash.AddSubDecl(D);
|
||||
Hashes.emplace_back(D, Hash.CalculateHash());
|
||||
Hashes.emplace_back(D, ComputeSubDeclODRHash(D));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9696,18 +9732,15 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
|
||||
DeclHashes FirstHashes;
|
||||
DeclHashes SecondHashes;
|
||||
ODRHash Hash;
|
||||
|
||||
auto PopulateHashes = [&Hash, FirstRecord](DeclHashes &Hashes,
|
||||
CXXRecordDecl *Record) {
|
||||
auto PopulateHashes = [&ComputeSubDeclODRHash, FirstRecord](
|
||||
DeclHashes &Hashes, CXXRecordDecl *Record) {
|
||||
for (auto *D : Record->decls()) {
|
||||
// Due to decl merging, the first CXXRecordDecl is the parent of
|
||||
// Decls in both records.
|
||||
if (!ODRHash::isWhitelistedDecl(D, FirstRecord))
|
||||
continue;
|
||||
Hash.clear();
|
||||
Hash.AddSubDecl(D);
|
||||
Hashes.emplace_back(D, Hash.CalculateHash());
|
||||
Hashes.emplace_back(D, ComputeSubDeclODRHash(D));
|
||||
}
|
||||
};
|
||||
PopulateHashes(FirstHashes, FirstRecord);
|
||||
|
@ -9901,19 +9934,6 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
<< SecondModule << Range << DiffType;
|
||||
};
|
||||
|
||||
auto ComputeODRHash = [&Hash](const Stmt* S) {
|
||||
assert(S);
|
||||
Hash.clear();
|
||||
Hash.AddStmt(S);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
|
||||
Hash.clear();
|
||||
Hash.AddQualType(Ty);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
switch (FirstDiffType) {
|
||||
case Other:
|
||||
case EndOfClass:
|
||||
|
@ -10488,6 +10508,160 @@ void ASTReader::diagnoseOdrViolations() {
|
|||
<< Merge.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Issue ODR failures diagnostics for functions.
|
||||
for (auto &Merge : FunctionOdrMergeFailures) {
|
||||
enum ODRFunctionDifference {
|
||||
ReturnType,
|
||||
ParameterName,
|
||||
ParameterType,
|
||||
ParameterSingleDefaultArgument,
|
||||
ParameterDifferentDefaultArgument,
|
||||
FunctionBody,
|
||||
};
|
||||
|
||||
FunctionDecl *FirstFunction = Merge.first;
|
||||
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction);
|
||||
|
||||
bool Diagnosed = false;
|
||||
for (auto &SecondFunction : Merge.second) {
|
||||
|
||||
if (FirstFunction == SecondFunction)
|
||||
continue;
|
||||
|
||||
std::string SecondModule =
|
||||
getOwningModuleNameForDiagnostic(SecondFunction);
|
||||
|
||||
auto ODRDiagError = [FirstFunction, &FirstModule,
|
||||
this](SourceLocation Loc, SourceRange Range,
|
||||
ODRFunctionDifference DiffType) {
|
||||
return Diag(Loc, diag::err_module_odr_violation_function)
|
||||
<< FirstFunction << FirstModule.empty() << FirstModule << Range
|
||||
<< DiffType;
|
||||
};
|
||||
auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc,
|
||||
SourceRange Range,
|
||||
ODRFunctionDifference DiffType) {
|
||||
return Diag(Loc, diag::note_module_odr_violation_function)
|
||||
<< SecondModule << Range << DiffType;
|
||||
};
|
||||
|
||||
if (ComputeQualTypeODRHash(FirstFunction->getReturnType()) !=
|
||||
ComputeQualTypeODRHash(SecondFunction->getReturnType())) {
|
||||
ODRDiagError(FirstFunction->getReturnTypeSourceRange().getBegin(),
|
||||
FirstFunction->getReturnTypeSourceRange(), ReturnType)
|
||||
<< FirstFunction->getReturnType();
|
||||
ODRDiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(),
|
||||
SecondFunction->getReturnTypeSourceRange(), ReturnType)
|
||||
<< SecondFunction->getReturnType();
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
assert(FirstFunction->param_size() == SecondFunction->param_size() &&
|
||||
"Merged functions with different number of parameters");
|
||||
|
||||
auto ParamSize = FirstFunction->param_size();
|
||||
bool ParameterMismatch = false;
|
||||
for (unsigned I = 0; I < ParamSize; ++I) {
|
||||
auto *FirstParam = FirstFunction->getParamDecl(I);
|
||||
auto *SecondParam = SecondFunction->getParamDecl(I);
|
||||
|
||||
assert(getContext().hasSameType(FirstParam->getType(),
|
||||
SecondParam->getType()) &&
|
||||
"Merged function has different parameter types.");
|
||||
|
||||
if (FirstParam->getDeclName() != SecondParam->getDeclName()) {
|
||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
|
||||
ParameterName)
|
||||
<< I + 1 << FirstParam->getDeclName();
|
||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
|
||||
ParameterName)
|
||||
<< I + 1 << SecondParam->getDeclName();
|
||||
ParameterMismatch = true;
|
||||
break;
|
||||
};
|
||||
|
||||
QualType FirstParamType = FirstParam->getType();
|
||||
QualType SecondParamType = SecondParam->getType();
|
||||
if (FirstParamType != SecondParamType &&
|
||||
ComputeQualTypeODRHash(FirstParamType) !=
|
||||
ComputeQualTypeODRHash(SecondParamType)) {
|
||||
if (const DecayedType *ParamDecayedType =
|
||||
FirstParamType->getAs<DecayedType>()) {
|
||||
ODRDiagError(FirstParam->getLocation(),
|
||||
FirstParam->getSourceRange(), ParameterType)
|
||||
<< (I + 1) << FirstParamType << true
|
||||
<< ParamDecayedType->getOriginalType();
|
||||
} else {
|
||||
ODRDiagError(FirstParam->getLocation(),
|
||||
FirstParam->getSourceRange(), ParameterType)
|
||||
<< (I + 1) << FirstParamType << false;
|
||||
}
|
||||
|
||||
if (const DecayedType *ParamDecayedType =
|
||||
SecondParamType->getAs<DecayedType>()) {
|
||||
ODRDiagNote(SecondParam->getLocation(),
|
||||
SecondParam->getSourceRange(), ParameterType)
|
||||
<< (I + 1) << SecondParamType << true
|
||||
<< ParamDecayedType->getOriginalType();
|
||||
} else {
|
||||
ODRDiagNote(SecondParam->getLocation(),
|
||||
SecondParam->getSourceRange(), ParameterType)
|
||||
<< (I + 1) << SecondParamType << false;
|
||||
}
|
||||
ParameterMismatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const Expr *FirstInit = FirstParam->getInit();
|
||||
const Expr *SecondInit = SecondParam->getInit();
|
||||
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
|
||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
|
||||
ParameterSingleDefaultArgument)
|
||||
<< (I + 1) << (FirstInit == nullptr)
|
||||
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange());
|
||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
|
||||
ParameterSingleDefaultArgument)
|
||||
<< (I + 1) << (SecondInit == nullptr)
|
||||
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
|
||||
ParameterMismatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (FirstInit && SecondInit &&
|
||||
ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) {
|
||||
ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
|
||||
ParameterDifferentDefaultArgument)
|
||||
<< (I + 1) << FirstInit->getSourceRange();
|
||||
ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
|
||||
ParameterDifferentDefaultArgument)
|
||||
<< (I + 1) << SecondInit->getSourceRange();
|
||||
ParameterMismatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
assert(ComputeSubDeclODRHash(FirstParam) ==
|
||||
ComputeSubDeclODRHash(SecondParam) &&
|
||||
"Undiagnosed parameter difference.");
|
||||
}
|
||||
|
||||
if (ParameterMismatch) {
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If no error has been generated before now, assume the problem is in
|
||||
// the body and generate a message.
|
||||
ODRDiagError(FirstFunction->getLocation(),
|
||||
FirstFunction->getSourceRange(), FunctionBody);
|
||||
ODRDiagNote(SecondFunction->getLocation(),
|
||||
SecondFunction->getSourceRange(), FunctionBody);
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
assert(Diagnosed && "Unable to emit ODR diagnostic.");
|
||||
}
|
||||
}
|
||||
|
||||
void ASTReader::StartedDeserializing() {
|
||||
|
|
|
@ -796,6 +796,9 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
|
|||
FD->setCachedLinkage(Linkage(Record.readInt()));
|
||||
FD->EndRangeLoc = ReadSourceLocation();
|
||||
|
||||
FD->ODRHash = Record.readInt();
|
||||
FD->HasODRHash = true;
|
||||
|
||||
switch ((FunctionDecl::TemplatedKind)Record.readInt()) {
|
||||
case FunctionDecl::TK_NonTemplate:
|
||||
mergeRedeclarable(FD, Redecl);
|
||||
|
|
|
@ -538,6 +538,8 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
Record.push_back(D->getLinkageInternal());
|
||||
Record.AddSourceLocation(D->getLocEnd());
|
||||
|
||||
Record.push_back(D->getODRHash());
|
||||
|
||||
Record.push_back(D->getTemplatedKind());
|
||||
switch (D->getTemplatedKind()) {
|
||||
case FunctionDecl::TK_NonTemplate:
|
||||
|
@ -2074,6 +2076,7 @@ void ASTWriter::WriteDeclAbbrevs() {
|
|||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // TemplateKind
|
||||
// This Array slurps the rest of the record. Fortunately we want to encode
|
||||
// (nearly) all the remaining (variable number of) fields in the same way.
|
||||
|
|
|
@ -557,11 +557,11 @@ S10 s10;
|
|||
|
||||
#if defined(FIRST)
|
||||
struct S11 {
|
||||
void A(int x) {}
|
||||
void A(int x);
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S11 {
|
||||
void A(int y) {}
|
||||
void A(int y);
|
||||
};
|
||||
#else
|
||||
S11 s11;
|
||||
|
@ -571,11 +571,11 @@ S11 s11;
|
|||
|
||||
#if defined(FIRST)
|
||||
struct S12 {
|
||||
void A(int x) {}
|
||||
void A(int x);
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S12 {
|
||||
void A(int x = 1) {}
|
||||
void A(int x = 1);
|
||||
};
|
||||
#else
|
||||
S12 s12;
|
||||
|
@ -585,11 +585,11 @@ S12 s12;
|
|||
|
||||
#if defined(FIRST)
|
||||
struct S13 {
|
||||
void A(int x = 1 + 0) {}
|
||||
void A(int x = 1 + 0);
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S13 {
|
||||
void A(int x = 1) {}
|
||||
void A(int x = 1);
|
||||
};
|
||||
#else
|
||||
S13 s13;
|
||||
|
@ -599,11 +599,11 @@ S13 s13;
|
|||
|
||||
#if defined(FIRST)
|
||||
struct S14 {
|
||||
void A(int x[2]) {}
|
||||
void A(int x[2]);
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S14 {
|
||||
void A(int x[3]) {}
|
||||
void A(int x[3]);
|
||||
};
|
||||
#else
|
||||
S14 s14;
|
||||
|
@ -2751,14 +2751,14 @@ namespace DefaultArguments {
|
|||
template <typename T>
|
||||
struct S {
|
||||
struct R {
|
||||
void foo(T x = 0) {}
|
||||
void foo(T x = 0);
|
||||
};
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
template <typename T>
|
||||
struct S {
|
||||
struct R {
|
||||
void foo(T x = 1) {}
|
||||
void foo(T x = 1);
|
||||
};
|
||||
};
|
||||
#else
|
||||
|
@ -2771,13 +2771,13 @@ void run() {
|
|||
|
||||
#if defined(FIRST)
|
||||
template <typename alpha> struct Bravo {
|
||||
void charlie(bool delta = false) {}
|
||||
void charlie(bool delta = false);
|
||||
};
|
||||
typedef Bravo<char> echo;
|
||||
echo foxtrot;
|
||||
#elif defined(SECOND)
|
||||
template <typename alpha> struct Bravo {
|
||||
void charlie(bool delta = (false)) {}
|
||||
void charlie(bool delta = (false));
|
||||
};
|
||||
typedef Bravo<char> echo;
|
||||
echo foxtrot;
|
||||
|
@ -2788,6 +2788,142 @@ Bravo<char> golf;
|
|||
#endif
|
||||
} // namespace DefaultArguments
|
||||
|
||||
namespace FunctionDecl {
|
||||
#if defined(FIRST)
|
||||
struct S1 {};
|
||||
S1 s1a;
|
||||
#elif defined(SECOND)
|
||||
struct S1 {};
|
||||
#else
|
||||
S1 s1;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S2 {
|
||||
S2() = default;
|
||||
};
|
||||
S2 s2a = S2();
|
||||
#elif defined(SECOND)
|
||||
struct S2 {
|
||||
S2() = default;
|
||||
};
|
||||
#else
|
||||
S2 s2;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S3 {
|
||||
S3() = delete;
|
||||
};
|
||||
S3* s3c;
|
||||
#elif defined(SECOND)
|
||||
struct S3 {
|
||||
S3() = delete;
|
||||
};
|
||||
#else
|
||||
S3* s3;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST) || defined(SECOND)
|
||||
int F1(int x, float y = 2.7) { return 1; }
|
||||
#else
|
||||
int I1 = F1(1);
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F2() { return 1; }
|
||||
#elif defined(SECOND)
|
||||
double F2() { return 1; }
|
||||
#else
|
||||
int I2 = F2();
|
||||
// expected-error@-1 {{call to 'F2' is ambiguous}}
|
||||
// expected-note@first.h:* {{candidate function}}
|
||||
// expected-note@second.h:* {{candidate function}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F3(float) { return 1; }
|
||||
#elif defined(SECOND)
|
||||
int F3(double) { return 1; }
|
||||
#else
|
||||
int I3 = F3(1);
|
||||
// expected-error@-1 {{call to 'F3' is ambiguous}}
|
||||
// expected-note@first.h:* {{candidate function}}
|
||||
// expected-note@second.h:* {{candidate function}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F4(int x) { return 1; }
|
||||
#elif defined(SECOND)
|
||||
int F4(int y) { return 1; }
|
||||
#else
|
||||
int I4 = F4(1);
|
||||
// expected-error@second.h:* {{'FunctionDecl::F4' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with name 'y'}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with name 'x'}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F5(int x) { return 1; }
|
||||
#elif defined(SECOND)
|
||||
int F5(int x = 1) { return 1; }
|
||||
#else
|
||||
int I5 = F6(1);
|
||||
// expected-error@second.h:* {{'FunctionDecl::F5' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter without a default argument}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with a default argument}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F6(int x = 2) { return 1; }
|
||||
#elif defined(SECOND)
|
||||
int F6(int x = 1) { return 1; }
|
||||
#else
|
||||
int I6 = F6(1);
|
||||
// expected-error@second.h:* {{'FunctionDecl::F6' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with a default argument}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with a different default argument}}
|
||||
#endif
|
||||
|
||||
using I = int;
|
||||
#if defined(FIRST)
|
||||
I F7() { return 0; }
|
||||
#elif defined(SECOND)
|
||||
int F7() { return 0; }
|
||||
#else
|
||||
int I7 = F7();
|
||||
// expected-error@second.h:* {{'FunctionDecl::F7' has different definitions in different modules; definition in module 'SecondModule' first difference is return type is 'int'}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found different return type 'FunctionDecl::I' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F8(int) { return 0; }
|
||||
#elif defined(SECOND)
|
||||
int F8(I) { return 0; }
|
||||
#else
|
||||
int I8 = F8(1);
|
||||
// expected-error@second.h:* {{'FunctionDecl::F8' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with type 'FunctionDecl::I' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with type 'int'}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F9(int[1]) { return 0; }
|
||||
#elif defined(SECOND)
|
||||
int F9(int[2]) { return 0; }
|
||||
#else
|
||||
int I9 = F9(nullptr);
|
||||
// expected-error@second.h:* {{'FunctionDecl::F9' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with type 'int *' decayed from 'int [2]'}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with type 'int *' decayed from 'int [1]'}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
int F10() { return 1; }
|
||||
#elif defined(SECOND)
|
||||
int F10() { return 2; }
|
||||
#else
|
||||
int I10 = F10();
|
||||
#endif
|
||||
// expected-error@second.h:* {{'FunctionDecl::F10' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
|
||||
} // namespace FunctionDecl
|
||||
|
||||
// Keep macros contained to one file.
|
||||
#ifdef FIRST
|
||||
#undef FIRST
|
||||
|
|
Loading…
Reference in New Issue