@@ -1525,7 +1525,9 @@ class swift::MultiConformanceChecker {
15251525 NormalProtocolConformance *conformance, bool issueFixit);
15261526
15271527 // / 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);
15291531public:
15301532 MultiConformanceChecker (ASTContext &ctx) : Context(ctx) {}
15311533
@@ -1551,17 +1553,34 @@ class swift::MultiConformanceChecker {
15511553};
15521554
15531555bool MultiConformanceChecker::
1554- isUnsatisfiedReq (NormalProtocolConformance *conformance, ValueDecl *req) {
1556+ isUnsatisfiedReq (ConformanceChecker &checker,
1557+ NormalProtocolConformance *conformance, ValueDecl *req) {
15551558 if (conformance->isInvalid ()) return false ;
15561559 if (isa<TypeDecl>(req)) return false ;
15571560
15581561 auto witness = conformance->hasWitness (req)
15591562 ? conformance->getWitnessUncached (req).getDecl ()
15601563 : nullptr ;
15611564
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.
15641582 return req->getAttrs ().hasAttribute <OptionalAttr>();
1583+ }
15651584
15661585 // If the witness lands within the declaration context of the conformance,
15671586 // record it as a "covered" member.
@@ -1586,13 +1605,26 @@ void MultiConformanceChecker::checkAllConformances() {
15861605 continue ;
15871606 // Check whether there are any unsatisfied requirements.
15881607 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+
15891621 for (auto member : proto->getMembers ()) {
15901622 auto req = dyn_cast<ValueDecl>(member);
15911623 if (!req || !req->isProtocolRequirement ()) continue ;
15921624
15931625 // If the requirement is unsatisfied, we might want to warn
15941626 // about near misses; record it.
1595- if (isUnsatisfiedReq (conformance, req)) {
1627+ if (isUnsatisfiedReq (getChecker (), conformance, req)) {
15961628 UnsatisfiedReqs.push_back (req);
15971629 continue ;
15981630 }
@@ -3100,10 +3132,108 @@ filterProtocolRequirements(
31003132 return Filtered;
31013133}
31023134
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+
31033229bool ConformanceChecker::
31043230diagnoseMissingWitnesses (MissingWitnessDiagnosisKind Kind) {
31053231 auto LocalMissing = getLocalMissingWitness ();
31063232
3233+ SmallVector<MissingWitness, 4 > MissingWitnessScratch;
3234+ LocalMissing = pruneMissingWitnesses (
3235+ *this , Proto, Conformance, LocalMissing, MissingWitnessScratch);
3236+
31073237 // If this conformance has nothing to complain, return.
31083238 if (LocalMissing.empty ())
31093239 return false ;
@@ -4451,6 +4581,41 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
44514581 }
44524582}
44534583
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+
44544619void swift::diagnoseConformanceFailure (Type T,
44554620 ProtocolDecl *Proto,
44564621 DeclContext *DC,
0 commit comments