mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
[ODRHash] Merge the two function hashes into one.
Functions that are a sub-Decl of a record were hashed differently than other functions. This change keeps the AddFunctionDecl function and the hash of records now calls this function. In addition, AddFunctionDecl has an option to perform a hash as if the body was absent, which is required for some checks after loading modules. Additional logic prevents multiple error message from being printed. llvm-svn: 336632
This commit is contained in:
parent
2a3036fb1d
commit
27c1b1a638
@ -2504,6 +2504,10 @@ public:
|
||||
/// stored on first call, then the stored value returned on the other calls.
|
||||
unsigned getODRHash();
|
||||
|
||||
/// Returns cached ODRHash of the function. This must have been previously
|
||||
/// computed and stored.
|
||||
unsigned getODRHash() const;
|
||||
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) {
|
||||
|
@ -54,8 +54,9 @@ public:
|
||||
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);
|
||||
// more information than the AddDecl class. SkipBody will process the
|
||||
// hash as if the function has no body.
|
||||
void AddFunctionDecl(const FunctionDecl *Function, bool SkipBody = false);
|
||||
|
||||
// Process SubDecls of the main Decl. This method calls the DeclVisitor
|
||||
// while AddDecl does not.
|
||||
|
@ -191,6 +191,8 @@ def err_module_odr_violation_mismatch_decl_diff : Error<
|
||||
"%select{method %5|constructor|destructor}4|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"is %select{not deleted|deleted}6|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"is %select{not defaulted|defaulted}6|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"is %select{|pure }6%select{not virtual|virtual}7|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
@ -217,6 +219,10 @@ def err_module_odr_violation_mismatch_decl_diff : Error<
|
||||
"with %6 template argument%s6|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"with %6 for %ordinal7 template argument|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"with %select{no body|body}6|"
|
||||
"%select{method %5|constructor|destructor}4 "
|
||||
"with body|"
|
||||
"%select{typedef|type alias}4 name %5|"
|
||||
"%select{typedef|type alias}4 %5 with underlying type %6|"
|
||||
"data member with name %4|"
|
||||
@ -256,6 +262,8 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
|
||||
"%select{method %3|constructor|destructor}2|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"is %select{not deleted|deleted}4|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"is %select{not defaulted|defaulted}4|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"is %select{|pure }4%select{not virtual|virtual}5|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
@ -282,6 +290,10 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
|
||||
"with %4 template argument%s4|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"with %4 for %ordinal5 template argument|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"with %select{no body|body}4|"
|
||||
"%select{method %3|constructor|destructor}2 "
|
||||
"with different body|"
|
||||
"%select{typedef|type alias}2 name %3|"
|
||||
"%select{typedef|type alias}2 %3 with different underlying type %4|"
|
||||
"data member with name %2|"
|
||||
|
@ -3658,18 +3658,15 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned FunctionDecl::getODRHash() const {
|
||||
assert(HasODRHash);
|
||||
return ODRHash;
|
||||
}
|
||||
|
||||
unsigned FunctionDecl::getODRHash() {
|
||||
if (HasODRHash)
|
||||
return ODRHash;
|
||||
|
||||
if (FunctionDecl *Definition = getDefinition()) {
|
||||
if (Definition != this) {
|
||||
HasODRHash = true;
|
||||
ODRHash = Definition->getODRHash();
|
||||
return ODRHash;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *FT = getInstantiatedFromMemberFunction()) {
|
||||
HasODRHash = true;
|
||||
ODRHash = FT->getODRHash();
|
||||
|
@ -317,35 +317,14 @@ public:
|
||||
}
|
||||
|
||||
void VisitFunctionDecl(const FunctionDecl *D) {
|
||||
ID.AddInteger(D->getStorageClass());
|
||||
Hash.AddBoolean(D->isInlineSpecified());
|
||||
Hash.AddBoolean(D->isVirtualAsWritten());
|
||||
Hash.AddBoolean(D->isPure());
|
||||
Hash.AddBoolean(D->isDeletedAsWritten());
|
||||
|
||||
ID.AddInteger(D->param_size());
|
||||
|
||||
for (auto *Param : D->parameters()) {
|
||||
Hash.AddSubDecl(Param);
|
||||
}
|
||||
|
||||
AddQualType(D->getReturnType());
|
||||
|
||||
const auto* SpecializationArgs = D->getTemplateSpecializationArgs();
|
||||
Hash.AddBoolean(SpecializationArgs);
|
||||
if (SpecializationArgs) {
|
||||
ID.AddInteger(SpecializationArgs->size());
|
||||
for (const TemplateArgument &TA : SpecializationArgs->asArray()) {
|
||||
Hash.AddTemplateArgument(TA);
|
||||
}
|
||||
}
|
||||
// Handled by the ODRHash for FunctionDecl
|
||||
ID.AddInteger(D->getODRHash());
|
||||
|
||||
Inherited::VisitFunctionDecl(D);
|
||||
}
|
||||
|
||||
void VisitCXXMethodDecl(const CXXMethodDecl *D) {
|
||||
Hash.AddBoolean(D->isConst());
|
||||
Hash.AddBoolean(D->isVolatile());
|
||||
// Handled by the ODRHash for FunctionDecl
|
||||
|
||||
Inherited::VisitCXXMethodDecl(D);
|
||||
}
|
||||
@ -425,7 +404,6 @@ public:
|
||||
}
|
||||
|
||||
void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
|
||||
Visit(D->getTemplatedDecl());
|
||||
AddDecl(D->getTemplatedDecl());
|
||||
Inherited::VisitFunctionTemplateDecl(D);
|
||||
}
|
||||
@ -479,9 +457,13 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
|
||||
// Filter out sub-Decls which will not be processed in order to get an
|
||||
// accurate count of Decl's.
|
||||
llvm::SmallVector<const Decl *, 16> Decls;
|
||||
for (const Decl *SubDecl : Record->decls()) {
|
||||
for (Decl *SubDecl : Record->decls()) {
|
||||
if (isWhitelistedDecl(SubDecl, Record)) {
|
||||
Decls.push_back(SubDecl);
|
||||
if (auto *Function = dyn_cast<FunctionDecl>(SubDecl)) {
|
||||
// Compute/Preload ODRHash into FunctionDecl.
|
||||
Function->getODRHash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,25 +487,48 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
|
||||
}
|
||||
}
|
||||
|
||||
void ODRHash::AddFunctionDecl(const FunctionDecl *Function) {
|
||||
void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
|
||||
bool SkipBody) {
|
||||
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;
|
||||
if (auto *F = dyn_cast<FunctionDecl>(DC)) {
|
||||
if (F->isFunctionTemplateSpecialization()) {
|
||||
if (!isa<CXXMethodDecl>(DC)) return;
|
||||
if (DC->getLexicalParent()->isFileContext()) return;
|
||||
// Inline method specializations are the only supported
|
||||
// specialization for now.
|
||||
}
|
||||
}
|
||||
DC = DC->getParent();
|
||||
}
|
||||
|
||||
ID.AddInteger(Function->getDeclKind());
|
||||
|
||||
const auto *SpecializationArgs = Function->getTemplateSpecializationArgs();
|
||||
AddBoolean(SpecializationArgs);
|
||||
if (SpecializationArgs) {
|
||||
ID.AddInteger(SpecializationArgs->size());
|
||||
for (const TemplateArgument &TA : SpecializationArgs->asArray()) {
|
||||
AddTemplateArgument(TA);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
|
||||
AddBoolean(Method->isConst());
|
||||
AddBoolean(Method->isVolatile());
|
||||
}
|
||||
|
||||
ID.AddInteger(Function->getStorageClass());
|
||||
AddBoolean(Function->isInlineSpecified());
|
||||
AddBoolean(Function->isVirtualAsWritten());
|
||||
AddBoolean(Function->isPure());
|
||||
AddBoolean(Function->isDeletedAsWritten());
|
||||
AddBoolean(Function->isExplicitlyDefaulted());
|
||||
|
||||
AddDecl(Function);
|
||||
|
||||
AddQualType(Function->getReturnType());
|
||||
@ -532,7 +537,21 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function) {
|
||||
for (auto Param : Function->parameters())
|
||||
AddSubDecl(Param);
|
||||
|
||||
AddStmt(Function->getBody());
|
||||
if (SkipBody) {
|
||||
AddBoolean(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool HasBody = Function->isThisDeclarationADefinition() &&
|
||||
!Function->isDefaulted() && !Function->isDeleted() &&
|
||||
!Function->isLateTemplateParsed();
|
||||
AddBoolean(HasBody);
|
||||
if (HasBody) {
|
||||
auto *Body = Function->getBody();
|
||||
AddBoolean(Body);
|
||||
if (Body)
|
||||
AddStmt(Body);
|
||||
}
|
||||
}
|
||||
|
||||
void ODRHash::AddDecl(const Decl *D) {
|
||||
|
@ -9397,7 +9397,15 @@ void ASTReader::finishPendingActions() {
|
||||
if (!FD->isLateTemplateParsed() &&
|
||||
!NonConstDefn->isLateTemplateParsed() &&
|
||||
FD->getODRHash() != NonConstDefn->getODRHash()) {
|
||||
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
|
||||
if (!isa<CXXMethodDecl>(FD)) {
|
||||
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
|
||||
} else if (FD->getLexicalParent()->isFileContext() &&
|
||||
NonConstDefn->getLexicalParent()->isFileContext()) {
|
||||
// Only diagnose out-of-line method definitions. If they are
|
||||
// in class definitions, then an error will be generated when
|
||||
// processing the class bodies.
|
||||
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@ -10069,6 +10077,7 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
FieldDifferentInitializers,
|
||||
MethodName,
|
||||
MethodDeleted,
|
||||
MethodDefaulted,
|
||||
MethodVirtual,
|
||||
MethodStatic,
|
||||
MethodVolatile,
|
||||
@ -10082,6 +10091,8 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
MethodNoTemplateArguments,
|
||||
MethodDifferentNumberTemplateArguments,
|
||||
MethodDifferentTemplateArgument,
|
||||
MethodSingleBody,
|
||||
MethodDifferentBody,
|
||||
TypedefName,
|
||||
TypedefType,
|
||||
VarName,
|
||||
@ -10315,8 +10326,8 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
break;
|
||||
}
|
||||
|
||||
const bool FirstDeleted = FirstMethod->isDeleted();
|
||||
const bool SecondDeleted = SecondMethod->isDeleted();
|
||||
const bool FirstDeleted = FirstMethod->isDeletedAsWritten();
|
||||
const bool SecondDeleted = SecondMethod->isDeletedAsWritten();
|
||||
if (FirstDeleted != SecondDeleted) {
|
||||
ODRDiagError(FirstMethod->getLocation(),
|
||||
FirstMethod->getSourceRange(), MethodDeleted)
|
||||
@ -10329,6 +10340,20 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
break;
|
||||
}
|
||||
|
||||
const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted();
|
||||
const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted();
|
||||
if (FirstDefaulted != SecondDefaulted) {
|
||||
ODRDiagError(FirstMethod->getLocation(),
|
||||
FirstMethod->getSourceRange(), MethodDefaulted)
|
||||
<< FirstMethodType << FirstName << FirstDefaulted;
|
||||
|
||||
ODRDiagNote(SecondMethod->getLocation(),
|
||||
SecondMethod->getSourceRange(), MethodDefaulted)
|
||||
<< SecondMethodType << SecondName << SecondDefaulted;
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const bool FirstVirtual = FirstMethod->isVirtualAsWritten();
|
||||
const bool SecondVirtual = SecondMethod->isVirtualAsWritten();
|
||||
const bool FirstPure = FirstMethod->isPure();
|
||||
@ -10594,6 +10619,44 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the hash of the method as if it has no body.
|
||||
auto ComputeCXXMethodODRHash = [&Hash](const CXXMethodDecl *D) {
|
||||
Hash.clear();
|
||||
Hash.AddFunctionDecl(D, true /*SkipBody*/);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
// Compare the hash generated to the hash stored. A difference means
|
||||
// that a body was present in the original source. Due to merging,
|
||||
// the stardard way of detecting a body will not work.
|
||||
const bool HasFirstBody =
|
||||
ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash();
|
||||
const bool HasSecondBody =
|
||||
ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash();
|
||||
|
||||
if (HasFirstBody != HasSecondBody) {
|
||||
ODRDiagError(FirstMethod->getLocation(),
|
||||
FirstMethod->getSourceRange(), MethodSingleBody)
|
||||
<< FirstMethodType << FirstName << HasFirstBody;
|
||||
ODRDiagNote(SecondMethod->getLocation(),
|
||||
SecondMethod->getSourceRange(), MethodSingleBody)
|
||||
<< SecondMethodType << SecondName << HasSecondBody;
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (HasFirstBody && HasSecondBody) {
|
||||
ODRDiagError(FirstMethod->getLocation(),
|
||||
FirstMethod->getSourceRange(), MethodDifferentBody)
|
||||
<< FirstMethodType << FirstName;
|
||||
ODRDiagNote(SecondMethod->getLocation(),
|
||||
SecondMethod->getSourceRange(), MethodDifferentBody)
|
||||
<< SecondMethodType << SecondName;
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case TypeAlias:
|
||||
|
@ -660,6 +660,202 @@ Invalid1* i1;
|
||||
#undef DECLS
|
||||
} // namespace Method
|
||||
|
||||
namespace MethodBody {
|
||||
#if defined(FIRST)
|
||||
struct S1 {
|
||||
int A() { return 0; }
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S1 {
|
||||
int A() { return 0; }
|
||||
};
|
||||
#else
|
||||
S1 s1;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S2 {
|
||||
int BothBodies() { return 0; }
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S2 {
|
||||
int BothBodies() { return 1; }
|
||||
};
|
||||
#else
|
||||
S2 s2;
|
||||
// expected-error@first.h:* {{'MethodBody::S2' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'BothBodies' with body}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found method 'BothBodies' with different body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S3 {
|
||||
int FirstBody() { return 0; }
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S3 {
|
||||
int FirstBody();
|
||||
};
|
||||
#else
|
||||
S3 s3;
|
||||
// expected-error@first.h:* {{'MethodBody::S3' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstBody' with body}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found method 'FirstBody' with no body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S4 {
|
||||
int SecondBody();
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S4 {
|
||||
int SecondBody() { return 0; }
|
||||
};
|
||||
#else
|
||||
S4 s4;
|
||||
// expected-error@first.h:* {{'MethodBody::S4' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'SecondBody' with no body}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found method 'SecondBody' with body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S5 {
|
||||
int FirstBodySecondOutOfLine() { return 0; }
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S5 {
|
||||
int FirstBodySecondOutOfLine();
|
||||
};
|
||||
int S5::FirstBodySecondOutOfLine() { return 0; }
|
||||
#else
|
||||
S5 s5;
|
||||
// expected-error@second.h:* {{'MethodBody::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'FirstBodySecondOutOfLine' with no body}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found method 'FirstBodySecondOutOfLine' with body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S6 {
|
||||
int FirstOutOfLineSecondBody();
|
||||
};
|
||||
int S6::FirstOutOfLineSecondBody() { return 0; }
|
||||
#elif defined(SECOND)
|
||||
struct S6 {
|
||||
int FirstOutOfLineSecondBody() { return 0; }
|
||||
};
|
||||
#else
|
||||
S6 s6;
|
||||
// expected-error@first.h:* {{'MethodBody::S6' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstOutOfLineSecondBody' with no body}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found method 'FirstOutOfLineSecondBody' with body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S7 {
|
||||
int BothOutOfLine();
|
||||
};
|
||||
int S7::BothOutOfLine() { return 1; }
|
||||
#elif defined(SECOND)
|
||||
struct S7 {
|
||||
int BothOutOfLine();
|
||||
};
|
||||
int S7::BothOutOfLine() { return 0; }
|
||||
#else
|
||||
S7 s7;
|
||||
// expected-error@second.h:* {{'MethodBody::S7::BothOutOfLine' 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}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S8 {
|
||||
int FirstBodySecondOutOfLine() { return 0; }
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S8 {
|
||||
int FirstBodySecondOutOfLine();
|
||||
};
|
||||
int S8::FirstBodySecondOutOfLine() { return 1; }
|
||||
#else
|
||||
S8 s8;
|
||||
// expected-error@second.h:* {{'MethodBody::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'FirstBodySecondOutOfLine' with no body}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found method 'FirstBodySecondOutOfLine' with body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S9 {
|
||||
int FirstOutOfLineSecondBody();
|
||||
};
|
||||
int S9::FirstOutOfLineSecondBody() { return 1; }
|
||||
#elif defined(SECOND)
|
||||
struct S9 {
|
||||
int FirstOutOfLineSecondBody() { return 0; }
|
||||
};
|
||||
#else
|
||||
S9 s9;
|
||||
// expected-error@first.h:* {{'MethodBody::S9' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstOutOfLineSecondBody' with no body}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found method 'FirstOutOfLineSecondBody' with body}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S10 {
|
||||
S10(int);
|
||||
S10() = delete;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S10 {
|
||||
S10(int);
|
||||
S10();
|
||||
};
|
||||
#else
|
||||
S10 s10(10);
|
||||
// expected-error@first.h:* {{'MethodBody::S10' has different definitions in different modules; first difference is definition in module 'FirstModule' found constructor is deleted}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found constructor is not deleted}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S11 {
|
||||
S11() = default;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S11 {
|
||||
S11();
|
||||
};
|
||||
#else
|
||||
S11 s11;
|
||||
// expected-error@first.h:* {{'MethodBody::S11' has different definitions in different modules; first difference is definition in module 'FirstModule' found constructor is defaulted}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found constructor is not defaulted}}
|
||||
#endif
|
||||
|
||||
#define DECLS(CLASSNAME) \
|
||||
CLASSNAME() = default; \
|
||||
~CLASSNAME() = delete; \
|
||||
void A(); \
|
||||
void B() { return; }; \
|
||||
void C(); \
|
||||
void D();
|
||||
|
||||
#define OUTOFLINEDEFS(CLASSNAME) \
|
||||
void CLASSNAME::C() {} \
|
||||
void CLASSNAME::D() { return; }
|
||||
|
||||
#if defined(FIRST) || defined(SECOND)
|
||||
struct Valid1 {
|
||||
DECLS(Valid1)
|
||||
};
|
||||
OUTOFLINEDEFS(Valid1)
|
||||
#else
|
||||
Valid1* v1;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST) || defined(SECOND)
|
||||
struct Invalid1 {
|
||||
DECLS(Invalid1)
|
||||
ACCESS
|
||||
};
|
||||
OUTOFLINEDEFS(Invalid1)
|
||||
#else
|
||||
Invalid1* i1;
|
||||
// expected-error@first.h:* {{'MethodBody::Invalid1' has different definitions in different modules; first difference is definition in module 'FirstModule' found public access specifier}}
|
||||
// expected-note@second.h:* {{but in 'SecondModule' found private access specifier}}
|
||||
#endif
|
||||
#undef DECLS
|
||||
} // namespace MethodBody
|
||||
|
||||
namespace Constructor {
|
||||
#if defined(FIRST)
|
||||
struct S1 {
|
||||
@ -3631,6 +3827,32 @@ template <int> void S11::foo() {}
|
||||
S11 s11;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S12 {
|
||||
void foo(int x);
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct S12 {
|
||||
void foo(int x);
|
||||
};
|
||||
void S12::foo(int y) {}
|
||||
#else
|
||||
S12 s12;
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct S13 {
|
||||
void foo(int x);
|
||||
};
|
||||
void S13::foo(int y) {}
|
||||
#elif defined(SECOND)
|
||||
struct S13 {
|
||||
void foo(int x);
|
||||
};
|
||||
void S13::foo(int y) {}
|
||||
#else
|
||||
S13 s13;
|
||||
#endif
|
||||
} // namespace FunctionDecl
|
||||
|
||||
namespace DeclTemplateArguments {
|
||||
|
Loading…
Reference in New Issue
Block a user