Skip to content

Commit a65d530

Browse files
committed
[ODRHash] Detect duplicate ObjCProtocolDecl ODR mismatches during parsing.
When during parsing we encountered a duplicate `ObjCProtocolDecl`, we were always emitting an error. With this change we accept * when a previous `ObjCProtocolDecl` is in a hidden [sub]module; * parsed `ObjCProtocolDecl` is the same as the previous one. And in case of mismatches we provide more detailed error messages. rdar://93069080 Differential Revision: https://reviews.llvm.org/D130327
1 parent 7059a6c commit a65d530

File tree

10 files changed

+208
-70
lines changed

10 files changed

+208
-70
lines changed

clang/include/clang/AST/DeclObjC.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,13 @@ class ObjCProtocolDecl : public ObjCContainerDecl,
22302230
/// Starts the definition of this Objective-C protocol.
22312231
void startDefinition();
22322232

2233+
/// Starts the definition without sharing it with other redeclarations.
2234+
/// Such definition shouldn't be used for anything but only to compare if
2235+
/// a duplicate is compatible with previous definition or if it is
2236+
/// a distinct duplicate.
2237+
void startDuplicateDefinitionForComparison();
2238+
void mergeDuplicateDefinitionWithCommon(const ObjCProtocolDecl *Definition);
2239+
22332240
/// Produce a name to be used for protocol's metadata. It comes either via
22342241
/// objc_runtime_name attribute or protocol name.
22352242
StringRef getObjCRuntimeNameAsString() const;

clang/include/clang/AST/ODRDiagsEmitter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ class ODRDiagsEmitter {
5555
const ObjCProtocolDecl *SecondProtocol,
5656
const struct ObjCProtocolDecl::DefinitionData *SecondDD) const;
5757

58+
/// Diagnose ODR mismatch between ObjCProtocolDecl with different definitions.
59+
bool diagnoseMismatch(const ObjCProtocolDecl *FirstProtocol,
60+
const ObjCProtocolDecl *SecondProtocol) const {
61+
assert(FirstProtocol->data().Definition !=
62+
SecondProtocol->data().Definition &&
63+
"Don't diagnose differences when definitions are merged already");
64+
return diagnoseMismatch(FirstProtocol, SecondProtocol,
65+
&SecondProtocol->data());
66+
}
67+
5868
/// Get the best name we know for the module that owns the given
5969
/// declaration, or an empty string if the declaration is not from a module.
6070
static std::string getOwningModuleNameForDiagnostic(const Decl *D);

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -622,10 +622,11 @@ def err_module_odr_violation_mismatch_decl : Error<
622622
"%select{end of class|public access specifier|private access specifier|"
623623
"protected access specifier|static assert|field|method|type alias|typedef|"
624624
"data member|friend declaration|function template|method|property}3">;
625-
def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
625+
def note_module_odr_violation_mismatch_decl : Note<
626+
"but in %select{'%1'|definition here}0 found "
626627
"%select{end of class|public access specifier|private access specifier|"
627628
"protected access specifier|static assert|field|method|type alias|typedef|"
628-
"data member|friend declaration|function template|method|property}1">;
629+
"data member|friend declaration|function template|method|property}2">;
629630

630631
def err_module_odr_violation_record : Error<
631632
"%q0 has different definitions in different modules; first difference is "
@@ -843,11 +844,12 @@ def err_module_odr_violation_referenced_protocols : Error <
843844
"%4 referenced %plural{1:protocol|:protocols}4|"
844845
"%ordinal4 referenced protocol with name %5"
845846
"}3">;
846-
def note_module_odr_violation_referenced_protocols : Note <"but in '%0' found "
847+
def note_module_odr_violation_referenced_protocols : Note <
848+
"but in %select{'%1'|definition here}0 found "
847849
"%select{"
848-
"%2 referenced %plural{1:protocol|:protocols}2|"
849-
"%ordinal2 referenced protocol with different name %3"
850-
"}1">;
850+
"%3 referenced %plural{1:protocol|:protocols}3|"
851+
"%ordinal3 referenced protocol with different name %4"
852+
"}2">;
851853

852854
def err_module_odr_violation_objc_method : Error<
853855
"%q0 has different definitions in different modules; first difference is "
@@ -860,15 +862,16 @@ def err_module_odr_violation_objc_method : Error<
860862
"%select{regular|direct}5 method %4|"
861863
"method %4"
862864
"}3">;
863-
def note_module_odr_violation_objc_method : Note<"but in '%0' found "
865+
def note_module_odr_violation_objc_method : Note<
866+
"but in %select{'%1'|definition here}0 found "
864867
"%select{"
865-
"method %2 with different return type %3|"
866-
"method %2 as %select{class|instance}3 method|"
867-
"%select{no|'required'|'optional'}2 method control|"
868-
"method %2 with %select{no designated initializer|designated initializer}3|"
869-
"%select{regular|direct}3 method %2|"
870-
"different method %2"
871-
"}1">;
868+
"method %3 with different return type %4|"
869+
"method %3 as %select{class|instance}4 method|"
870+
"%select{no|'required'|'optional'}3 method control|"
871+
"method %3 with %select{no designated initializer|designated initializer}4|"
872+
"%select{regular|direct}4 method %3|"
873+
"different method %3"
874+
"}2">;
872875

873876
def err_module_odr_violation_method_params : Error<
874877
"%q0 has different definitions in different modules; first difference is "
@@ -881,15 +884,16 @@ def err_module_odr_violation_method_params : Error<
881884
"%select{method %5|constructor|destructor}4 "
882885
"with %ordinal6 parameter named %7"
883886
"}3">;
884-
def note_module_odr_violation_method_params : Note<"but in '%0' found "
887+
def note_module_odr_violation_method_params : Note<
888+
"but in %select{'%1'|definition here}0 found "
885889
"%select{"
886-
"%select{method %3|constructor|destructor}2 "
887-
"that has %4 parameter%s4|"
888-
"%select{method %3|constructor|destructor}2 "
889-
"with %ordinal4 parameter of type %5%select{| decayed from %7}6|"
890-
"%select{method %3|constructor|destructor}2 "
891-
"with %ordinal4 parameter named %5"
892-
"}1">;
890+
"%select{method %4|constructor|destructor}3 "
891+
"that has %5 parameter%s5|"
892+
"%select{method %4|constructor|destructor}3 "
893+
"with %ordinal5 parameter of type %6%select{| decayed from %8}7|"
894+
"%select{method %4|constructor|destructor}3 "
895+
"with %ordinal5 parameter named %6"
896+
"}2">;
893897

894898
def err_module_odr_violation_objc_property : Error<
895899
"%q0 has different definitions in different modules; first difference is "
@@ -903,15 +907,15 @@ def err_module_odr_violation_objc_property : Error<
903907
"unsafe_unretained|nullability|null_resettable|class|direct}5' attribute"
904908
"}3">;
905909
def note_module_odr_violation_objc_property : Note<
906-
"but in '%0' found "
910+
"but in %select{'%1'|definition here}0 found "
907911
"%select{"
908-
"property %2|"
909-
"property %2 with type %3|"
910-
"%select{no|'required'|'optional'}2 property control|"
911-
"property %2 with different '%select{none|readonly|getter|assign|"
912+
"property %3|"
913+
"property %3 with type %4|"
914+
"%select{no|'required'|'optional'}3 property control|"
915+
"property %3 with different '%select{none|readonly|getter|assign|"
912916
"readwrite|retain|copy|nonatomic|setter|atomic|weak|strong|"
913-
"unsafe_unretained|nullability|null_resettable|class|direct}3' attribute"
914-
"}1">;
917+
"unsafe_unretained|nullability|null_resettable|class|direct}4' attribute"
918+
"}2">;
915919

916920
def err_module_odr_violation_mismatch_decl_unknown : Error<
917921
"%q0 %select{with definition in module '%2'|defined here}1 has different "
@@ -920,11 +924,11 @@ def err_module_odr_violation_mismatch_decl_unknown : Error<
920924
"friend declaration|function template|method|"
921925
"property|unexpected decl}3">;
922926
def note_module_odr_violation_mismatch_decl_unknown : Note<
923-
"but in '%0' found "
927+
"but in %select{'%1'|definition here}0 found "
924928
"%select{||||different static assert|different field|different method|"
925929
"different type alias|different typedef|different data member|"
926930
"different friend declaration|different function template|different method|"
927-
"different property|another unexpected decl}1">;
931+
"different property|another unexpected decl}2">;
928932

929933

930934
def remark_sanitize_address_insert_extra_padding_accepted : Remark<

clang/include/clang/Sema/Sema.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3411,6 +3411,20 @@ class Sema final {
34113411
/// in case of a structural mismatch.
34123412
bool ActOnDuplicateDefinition(Decl *Prev, SkipBodyInfo &SkipBody);
34133413

3414+
/// Check ODR hashes for C/ObjC when merging types from modules.
3415+
/// Differently from C++, actually parse the body and reject in case
3416+
/// of a mismatch.
3417+
template <typename T,
3418+
typename = std::enable_if_t<std::is_base_of<NamedDecl, T>::value>>
3419+
bool ActOnDuplicateODRHashDefinition(T *Duplicate, T *Previous) {
3420+
if (Duplicate->getODRHash() != Previous->getODRHash())
3421+
return false;
3422+
3423+
// Make the previous decl visible.
3424+
makeMergedDefinitionVisible(Previous);
3425+
return true;
3426+
}
3427+
34143428
typedef void *SkippedDefinitionContext;
34153429

34163430
/// Invoked when we enter a tag definition that we're skipping.
@@ -10118,7 +10132,8 @@ class Sema final {
1011810132
SourceLocation AtProtoInterfaceLoc, IdentifierInfo *ProtocolName,
1011910133
SourceLocation ProtocolLoc, Decl *const *ProtoRefNames,
1012010134
unsigned NumProtoRefs, const SourceLocation *ProtoLocs,
10121-
SourceLocation EndProtoLoc, const ParsedAttributesView &AttrList);
10135+
SourceLocation EndProtoLoc, const ParsedAttributesView &AttrList,
10136+
SkipBodyInfo *SkipBody);
1012210137

1012310138
ObjCCategoryDecl *ActOnStartCategoryInterface(
1012410139
SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName,

clang/lib/AST/DeclObjC.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,17 @@ void ObjCProtocolDecl::startDefinition() {
19971997
RD->Data = this->Data;
19981998
}
19991999

2000+
void ObjCProtocolDecl::startDuplicateDefinitionForComparison() {
2001+
Data.setPointer(nullptr);
2002+
allocateDefinitionData();
2003+
// Don't propagate data to other redeclarations.
2004+
}
2005+
2006+
void ObjCProtocolDecl::mergeDuplicateDefinitionWithCommon(
2007+
const ObjCProtocolDecl *Definition) {
2008+
Data = Definition->Data;
2009+
}
2010+
20002011
void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM) const {
20012012
if (const ObjCProtocolDecl *PDecl = getDefinition()) {
20022013
for (auto *Prop : PDecl->properties()) {

clang/lib/AST/ODRDiagsEmitter.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ static bool diagnoseSubMismatchMethodParameters(DiagnosticsEngine &Diags,
9090
DiagMethodType SecondMethodType = GetDiagMethodType(SecondMethod);
9191
return Diags.Report(SecondMethod->getLocation(),
9292
diag::note_module_odr_violation_method_params)
93-
<< SecondModule << SecondMethod->getSourceRange() << DiffType
94-
<< SecondMethodType << SecondName;
93+
<< SecondModule.empty() << SecondModule
94+
<< SecondMethod->getSourceRange() << DiffType << SecondMethodType
95+
<< SecondName;
9596
};
9697

9798
const unsigned FirstNumParameters = FirstMethod->param_size();
@@ -378,7 +379,7 @@ bool ODRDiagsEmitter::diagnoseSubMismatchProtocols(
378379
this](SourceLocation Loc, SourceRange Range,
379380
ODRReferencedProtocolDifference DiffType) {
380381
return Diag(Loc, diag::note_module_odr_violation_referenced_protocols)
381-
<< SecondModule << Range << DiffType;
382+
<< SecondModule.empty() << SecondModule << Range << DiffType;
382383
};
383384
auto GetProtoListSourceRange = [](const ObjCProtocolList &PL) {
384385
if (PL.empty())
@@ -440,7 +441,8 @@ bool ODRDiagsEmitter::diagnoseSubMismatchObjCMethod(
440441
this](ODRMethodDifference DiffType) {
441442
return Diag(SecondMethod->getLocation(),
442443
diag::note_module_odr_violation_objc_method)
443-
<< SecondModule << SecondMethod->getSourceRange() << DiffType;
444+
<< SecondModule.empty() << SecondModule
445+
<< SecondMethod->getSourceRange() << DiffType;
444446
};
445447

446448
if (computeODRHash(FirstMethod->getReturnType()) !=
@@ -517,7 +519,8 @@ bool ODRDiagsEmitter::diagnoseSubMismatchObjCProperty(
517519
auto DiagNote = [SecondModule, SecondProp,
518520
this](SourceLocation Loc, ODRPropertyDifference DiffType) {
519521
return Diag(Loc, diag::note_module_odr_violation_objc_property)
520-
<< SecondModule << SecondProp->getSourceRange() << DiffType;
522+
<< SecondModule.empty() << SecondModule
523+
<< SecondProp->getSourceRange() << DiffType;
521524
};
522525

523526
IdentifierInfo *FirstII = FirstProp->getIdentifier();
@@ -690,7 +693,8 @@ void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds(
690693
auto SecondDiagInfo =
691694
GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl);
692695
Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl)
693-
<< SecondModule << SecondDiagInfo.second << DR.SecondDiffType;
696+
<< SecondModule.empty() << SecondModule << SecondDiagInfo.second
697+
<< DR.SecondDiffType;
694698
}
695699

696700
bool ODRDiagsEmitter::diagnoseMismatch(
@@ -1538,7 +1542,8 @@ bool ODRDiagsEmitter::diagnoseMismatch(
15381542
<< FirstDecl->getSourceRange();
15391543
Diag(SecondDecl->getLocation(),
15401544
diag::note_module_odr_violation_mismatch_decl_unknown)
1541-
<< SecondModule << FirstDiffType << SecondDecl->getSourceRange();
1545+
<< SecondModule.empty() << SecondModule << FirstDiffType
1546+
<< SecondDecl->getSourceRange();
15421547
return true;
15431548
}
15441549

@@ -1908,6 +1913,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
19081913
<< FirstDecl->getSourceRange();
19091914
Diag(SecondDecl->getLocation(),
19101915
diag::note_module_odr_violation_mismatch_decl_unknown)
1911-
<< SecondModule << FirstDiffType << SecondDecl->getSourceRange();
1916+
<< SecondModule.empty() << SecondModule << FirstDiffType
1917+
<< SecondDecl->getSourceRange();
19121918
return true;
19131919
}

clang/lib/Parse/ParseObjc.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/AST/ASTContext.h"
14+
#include "clang/AST/ODRDiagsEmitter.h"
1415
#include "clang/AST/PrettyDeclStackTrace.h"
1516
#include "clang/Basic/CharInfo.h"
1617
#include "clang/Basic/TargetInfo.h"
@@ -2088,11 +2089,23 @@ Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
20882089
/*consumeLastToken=*/true))
20892090
return nullptr;
20902091

2091-
Decl *ProtoType = Actions.ActOnStartProtocolInterface(
2092+
Sema::SkipBodyInfo SkipBody;
2093+
ObjCProtocolDecl *ProtoType = Actions.ActOnStartProtocolInterface(
20922094
AtLoc, protocolName, nameLoc, ProtocolRefs.data(), ProtocolRefs.size(),
2093-
ProtocolLocs.data(), EndProtoLoc, attrs);
2095+
ProtocolLocs.data(), EndProtoLoc, attrs, &SkipBody);
20942096

20952097
ParseObjCInterfaceDeclList(tok::objc_protocol, ProtoType);
2098+
if (SkipBody.CheckSameAsPrevious) {
2099+
auto *PreviousDef = cast<ObjCProtocolDecl>(SkipBody.Previous);
2100+
if (Actions.ActOnDuplicateODRHashDefinition(ProtoType, PreviousDef)) {
2101+
ProtoType->mergeDuplicateDefinitionWithCommon(
2102+
PreviousDef->getDefinition());
2103+
} else {
2104+
ODRDiagsEmitter DiagsEmitter(Diags, Actions.getASTContext(),
2105+
getPreprocessor().getLangOpts());
2106+
DiagsEmitter.diagnoseMismatch(PreviousDef, ProtoType);
2107+
}
2108+
}
20962109
return Actions.ConvertDeclToDeclGroup(ProtoType);
20972110
}
20982111

clang/lib/Sema/SemaDeclObjC.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,31 +1213,37 @@ ObjCProtocolDecl *Sema::ActOnStartProtocolInterface(
12131213
SourceLocation AtProtoInterfaceLoc, IdentifierInfo *ProtocolName,
12141214
SourceLocation ProtocolLoc, Decl *const *ProtoRefs, unsigned NumProtoRefs,
12151215
const SourceLocation *ProtoLocs, SourceLocation EndProtoLoc,
1216-
const ParsedAttributesView &AttrList) {
1216+
const ParsedAttributesView &AttrList, SkipBodyInfo *SkipBody) {
12171217
bool err = false;
12181218
// FIXME: Deal with AttrList.
12191219
assert(ProtocolName && "Missing protocol identifier");
12201220
ObjCProtocolDecl *PrevDecl = LookupProtocol(ProtocolName, ProtocolLoc,
12211221
forRedeclarationInCurContext());
12221222
ObjCProtocolDecl *PDecl = nullptr;
12231223
if (ObjCProtocolDecl *Def = PrevDecl? PrevDecl->getDefinition() : nullptr) {
1224-
// If we already have a definition, complain.
1225-
Diag(ProtocolLoc, diag::warn_duplicate_protocol_def) << ProtocolName;
1226-
Diag(Def->getLocation(), diag::note_previous_definition);
1227-
12281224
// Create a new protocol that is completely distinct from previous
12291225
// declarations, and do not make this protocol available for name lookup.
12301226
// That way, we'll end up completely ignoring the duplicate.
12311227
// FIXME: Can we turn this into an error?
12321228
PDecl = ObjCProtocolDecl::Create(Context, CurContext, ProtocolName,
12331229
ProtocolLoc, AtProtoInterfaceLoc,
1234-
/*PrevDecl=*/nullptr);
1230+
/*PrevDecl=*/Def);
1231+
1232+
if (SkipBody && !hasVisibleDefinition(Def)) {
1233+
SkipBody->CheckSameAsPrevious = true;
1234+
SkipBody->New = PDecl;
1235+
SkipBody->Previous = Def;
1236+
} else {
1237+
// If we already have a definition, complain.
1238+
Diag(ProtocolLoc, diag::warn_duplicate_protocol_def) << ProtocolName;
1239+
Diag(Def->getLocation(), diag::note_previous_definition);
1240+
}
12351241

12361242
// If we are using modules, add the decl to the context in order to
12371243
// serialize something meaningful.
12381244
if (getLangOpts().Modules)
12391245
PushOnScopeChains(PDecl, TUScope);
1240-
PDecl->startDefinition();
1246+
PDecl->startDuplicateDefinitionForComparison();
12411247
} else {
12421248
if (PrevDecl) {
12431249
// Check for circular dependencies among protocol declarations. This can

0 commit comments

Comments
 (0)