@@ -1525,7 +1525,9 @@ class swift::MultiConformanceChecker {
1525
1525
NormalProtocolConformance *conformance, bool issueFixit);
1526
1526
1527
1527
// / Determine whether the given requirement was left unsatisfied.
1528
- bool isUnsatisfiedReq (NormalProtocolConformance *conformance, ValueDecl *req);
1528
+ bool isUnsatisfiedReq (
1529
+ ConformanceChecker &checker, NormalProtocolConformance *conformance,
1530
+ ValueDecl *req);
1529
1531
public:
1530
1532
MultiConformanceChecker (ASTContext &ctx) : Context(ctx) {}
1531
1533
@@ -1551,17 +1553,34 @@ class swift::MultiConformanceChecker {
1551
1553
};
1552
1554
1553
1555
bool MultiConformanceChecker::
1554
- isUnsatisfiedReq (NormalProtocolConformance *conformance, ValueDecl *req) {
1556
+ isUnsatisfiedReq (ConformanceChecker &checker,
1557
+ NormalProtocolConformance *conformance, ValueDecl *req) {
1555
1558
if (conformance->isInvalid ()) return false ;
1556
1559
if (isa<TypeDecl>(req)) return false ;
1557
1560
1558
1561
auto witness = conformance->hasWitness (req)
1559
1562
? conformance->getWitnessUncached (req).getDecl ()
1560
1563
: nullptr ;
1561
1564
1562
- // An optional requirement might not have a witness...
1563
- if (!witness)
1565
+ if (!witness) {
1566
+ // If another @objc requirement refers to the same Objective-C
1567
+ // method, this requirement isn't unsatisfied.
1568
+ if (conformance->getProtocol ()->isObjC () &&
1569
+ isa<AbstractFunctionDecl>(req)) {
1570
+ auto funcReq = cast<AbstractFunctionDecl>(req);
1571
+ auto key = checker.getObjCMethodKey (funcReq);
1572
+ for (auto otherReq : checker.getObjCRequirements (key)) {
1573
+ if (otherReq == req)
1574
+ continue ;
1575
+
1576
+ if (conformance->hasWitness (otherReq))
1577
+ return false ;
1578
+ }
1579
+ }
1580
+
1581
+ // An optional requirement might not have a witness.
1564
1582
return req->getAttrs ().hasAttribute <OptionalAttr>();
1583
+ }
1565
1584
1566
1585
// If the witness lands within the declaration context of the conformance,
1567
1586
// record it as a "covered" member.
@@ -1586,13 +1605,26 @@ void MultiConformanceChecker::checkAllConformances() {
1586
1605
continue ;
1587
1606
// Check whether there are any unsatisfied requirements.
1588
1607
auto proto = conformance->getProtocol ();
1608
+ Optional<ConformanceChecker> checker;
1609
+ auto getChecker = [&] () -> ConformanceChecker& {
1610
+ if (checker)
1611
+ return *checker;
1612
+
1613
+ if (!AllUsedCheckers.empty () &&
1614
+ AllUsedCheckers.back ().Conformance == conformance)
1615
+ return AllUsedCheckers.back ();
1616
+
1617
+ checker.emplace (getASTContext (), conformance, MissingWitnesses);
1618
+ return *checker;
1619
+ };
1620
+
1589
1621
for (auto member : proto->getMembers ()) {
1590
1622
auto req = dyn_cast<ValueDecl>(member);
1591
1623
if (!req || !req->isProtocolRequirement ()) continue ;
1592
1624
1593
1625
// If the requirement is unsatisfied, we might want to warn
1594
1626
// about near misses; record it.
1595
- if (isUnsatisfiedReq (conformance, req)) {
1627
+ if (isUnsatisfiedReq (getChecker (), conformance, req)) {
1596
1628
UnsatisfiedReqs.push_back (req);
1597
1629
continue ;
1598
1630
}
@@ -3100,10 +3132,108 @@ filterProtocolRequirements(
3100
3132
return Filtered;
3101
3133
}
3102
3134
3135
+ // / Prune the set of missing witnesses for the given conformance, eliminating
3136
+ // / any requirements that do not actually need to satisfied.
3137
+ static ArrayRef<MissingWitness> pruneMissingWitnesses (
3138
+ ConformanceChecker &checker,
3139
+ ProtocolDecl *proto,
3140
+ NormalProtocolConformance *conformance,
3141
+ ArrayRef<MissingWitness> missingWitnesses,
3142
+ SmallVectorImpl<MissingWitness> &scratch) {
3143
+ if (missingWitnesses.empty ())
3144
+ return missingWitnesses;
3145
+
3146
+ // For an @objc protocol defined in Objective-C, the Clang importer might
3147
+ // have imported the same underlying Objective-C declaration as more than
3148
+ // one Swift declaration. If we aren't in an imported @objc protocol, there
3149
+ // is nothing to do.
3150
+ if (!proto->isObjC ())
3151
+ return missingWitnesses;
3152
+
3153
+ // Consider each of the missing witnesses to remove any that should not
3154
+ // longer be considered "missing".
3155
+ llvm::SmallDenseSet<ConformanceChecker::ObjCMethodKey>
3156
+ alreadyReportedAsMissing;
3157
+ bool removedAny = false ;
3158
+ for (unsigned missingWitnessIdx : indices (missingWitnesses)) {
3159
+ const auto &missingWitness = missingWitnesses[missingWitnessIdx];
3160
+
3161
+ // Local function to skip this particular witness.
3162
+ auto skipWitness = [&] {
3163
+ if (removedAny)
3164
+ return ;
3165
+
3166
+ // This is the first witness we skipped. Copy all of the earlier
3167
+ // missing witnesses over.
3168
+ scratch.clear ();
3169
+ scratch.append (
3170
+ missingWitnesses.begin (),
3171
+ missingWitnesses.begin () + missingWitnessIdx);
3172
+ removedAny = true ;
3173
+ };
3174
+
3175
+ // Local function to add this particular witness.
3176
+ auto addWitness = [&] {
3177
+ if (removedAny)
3178
+ scratch.push_back (missingWitness);
3179
+ };
3180
+
3181
+ // We only care about functions
3182
+ auto funcRequirement = dyn_cast<AbstractFunctionDecl>(
3183
+ missingWitness.requirement );
3184
+ if (!funcRequirement) {
3185
+ addWitness ();
3186
+ continue ;
3187
+ }
3188
+
3189
+ // ... whose selector is one that maps to multiple requirement declarations.
3190
+ auto key = checker.getObjCMethodKey (funcRequirement);
3191
+ auto matchingRequirements = checker.getObjCRequirements (key);
3192
+ if (matchingRequirements.size () < 2 ) {
3193
+ addWitness ();
3194
+ continue ;
3195
+ }
3196
+
3197
+ // If we have already reported a function with this selector as missing,
3198
+ // don't do it again.
3199
+ if (!alreadyReportedAsMissing.insert (key).second ) {
3200
+ skipWitness ();
3201
+ continue ;
3202
+ }
3203
+
3204
+ // If there is a witness for any of the *other* requirements with this
3205
+ // same selector, don't report it.
3206
+ bool foundOtherWitness = false ;
3207
+ for (auto otherReq : matchingRequirements) {
3208
+ if (otherReq == funcRequirement)
3209
+ continue ;
3210
+
3211
+ if (conformance->getWitness (otherReq)) {
3212
+ foundOtherWitness = true ;
3213
+ break ;
3214
+ }
3215
+ }
3216
+
3217
+ if (foundOtherWitness)
3218
+ skipWitness ();
3219
+ else
3220
+ addWitness ();
3221
+ }
3222
+
3223
+ if (removedAny)
3224
+ return scratch;
3225
+
3226
+ return missingWitnesses;
3227
+ }
3228
+
3103
3229
bool ConformanceChecker::
3104
3230
diagnoseMissingWitnesses (MissingWitnessDiagnosisKind Kind) {
3105
3231
auto LocalMissing = getLocalMissingWitness ();
3106
3232
3233
+ SmallVector<MissingWitness, 4 > MissingWitnessScratch;
3234
+ LocalMissing = pruneMissingWitnesses (
3235
+ *this , Proto, Conformance, LocalMissing, MissingWitnessScratch);
3236
+
3107
3237
// If this conformance has nothing to complain, return.
3108
3238
if (LocalMissing.empty ())
3109
3239
return false ;
@@ -4451,6 +4581,41 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
4451
4581
}
4452
4582
}
4453
4583
4584
+ // / Retrieve the Objective-C method key from the given function.
4585
+ auto ConformanceChecker::getObjCMethodKey (AbstractFunctionDecl *func)
4586
+ -> ObjCMethodKey {
4587
+ return ObjCMethodKey (func->getObjCSelector (), func->isInstanceMember ());
4588
+ }
4589
+
4590
+ // / Retrieve the Objective-C requirements in this protocol that have the
4591
+ // / given Objective-C method key.
4592
+ ArrayRef<AbstractFunctionDecl *>
4593
+ ConformanceChecker::getObjCRequirements (ObjCMethodKey key) {
4594
+ auto proto = Conformance->getProtocol ();
4595
+ if (!proto->isObjC ())
4596
+ return { };
4597
+
4598
+ // Fill in the data structure if we haven't done so yet.
4599
+ if (!computedObjCMethodRequirements) {
4600
+ for (auto requirement : proto->getSemanticMembers ()) {
4601
+ auto funcRequirement = dyn_cast<AbstractFunctionDecl>(requirement);
4602
+ if (!funcRequirement)
4603
+ continue ;
4604
+
4605
+ objcMethodRequirements[getObjCMethodKey (funcRequirement)]
4606
+ .push_back (funcRequirement);
4607
+ }
4608
+
4609
+ computedObjCMethodRequirements = true ;
4610
+ }
4611
+
4612
+ auto known = objcMethodRequirements.find (key);
4613
+ if (known == objcMethodRequirements.end ())
4614
+ return { };
4615
+
4616
+ return known->second ;
4617
+ }
4618
+
4454
4619
void swift::diagnoseConformanceFailure (Type T,
4455
4620
ProtocolDecl *Proto,
4456
4621
DeclContext *DC,
0 commit comments