Skip to content

Commit 739617c

Browse files
committed
objc protocol requirements with same selector need only one witness
This really only happens when the ClangImporter imports an ObjC protocol that has an async-looking method, which yields two requirements in the protocol. Only one of these requirements will be witnessed. fixes rdar://73326224
1 parent 7fdc65d commit 739617c

File tree

4 files changed

+85
-38
lines changed

4 files changed

+85
-38
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,17 +1589,11 @@ isUnsatisfiedReq(ConformanceChecker &checker,
15891589
if (!witness) {
15901590
// If another @objc requirement refers to the same Objective-C
15911591
// method, this requirement isn't unsatisfied.
1592-
if (conformance->getProtocol()->isObjC() &&
1593-
isa<AbstractFunctionDecl>(req)) {
1594-
auto funcReq = cast<AbstractFunctionDecl>(req);
1595-
auto key = checker.getObjCMethodKey(funcReq);
1596-
for (auto otherReq : checker.getObjCRequirements(key)) {
1597-
if (otherReq == req)
1598-
continue;
1599-
1600-
if (conformance->getWitness(otherReq))
1601-
return false;
1602-
}
1592+
if (checker.getObjCRequirementSibling(
1593+
req, [conformance](AbstractFunctionDecl *cand) {
1594+
return static_cast<bool>(conformance->getWitness(cand));
1595+
})) {
1596+
return false;
16031597
}
16041598

16051599
// An optional requirement might not have a witness.
@@ -3329,46 +3323,36 @@ static ArrayRef<MissingWitness> pruneMissingWitnesses(
33293323
scratch.push_back(missingWitness);
33303324
};
33313325

3332-
// We only care about functions
3333-
auto funcRequirement = dyn_cast<AbstractFunctionDecl>(
3334-
missingWitness.requirement);
3335-
if (!funcRequirement) {
3326+
// We only care about functions.
3327+
if (!isa<AbstractFunctionDecl>(missingWitness.requirement)) {
33363328
addWitness();
33373329
continue;
33383330
}
33393331

3340-
// ... whose selector is one that maps to multiple requirement declarations.
3341-
auto key = checker.getObjCMethodKey(funcRequirement);
3342-
auto matchingRequirements = checker.getObjCRequirements(key);
3343-
if (matchingRequirements.size() < 2) {
3344-
addWitness();
3345-
continue;
3346-
}
3332+
auto fnRequirement = cast<AbstractFunctionDecl>(missingWitness.requirement);
3333+
auto key = checker.getObjCMethodKey(fnRequirement);
33473334

33483335
// If we have already reported a function with this selector as missing,
33493336
// don't do it again.
3350-
if (!alreadyReportedAsMissing.insert(key).second) {
3337+
if (alreadyReportedAsMissing.count(key)) {
33513338
skipWitness();
33523339
continue;
33533340
}
33543341

3355-
// If there is a witness for any of the *other* requirements with this
3356-
// same selector, don't report it.
3357-
bool foundOtherWitness = false;
3358-
for (auto otherReq : matchingRequirements) {
3359-
if (otherReq == funcRequirement)
3360-
continue;
3342+
auto sibling = checker.getObjCRequirementSibling(
3343+
fnRequirement, [conformance](AbstractFunctionDecl *candidate) {
3344+
return static_cast<bool>(conformance->getWitness(candidate));
3345+
});
33613346

3362-
if (conformance->getWitness(otherReq)) {
3363-
foundOtherWitness = true;
3364-
break;
3365-
}
3347+
if (!sibling) {
3348+
alreadyReportedAsMissing.insert(key);
3349+
addWitness();
3350+
continue;
33663351
}
33673352

3368-
if (foundOtherWitness)
3369-
skipWitness();
3370-
else
3371-
addWitness();
3353+
// Otherwise, there is a witness for any of the *other* requirements with
3354+
// this same selector, so prune it out.
3355+
skipWitness();
33723356
}
33733357

33743358
if (removedAny)
@@ -4621,6 +4605,22 @@ void ConformanceChecker::resolveValueWitnesses() {
46214605
if (isa<AccessorDecl>(requirement))
46224606
continue;
46234607

4608+
// If this requirement is part of a pair of imported async requirements,
4609+
// where one has already been witnessed, we can skip it.
4610+
//
4611+
// This situation primarily arises when the ClangImporter translates an
4612+
// async-looking ObjC protocol method requirement into two Swift protocol
4613+
// requirements: an async version and a sync version. Exactly one of the two
4614+
// must be witnessed by the conformer.
4615+
if (!requirement->isImplicit() && getObjCRequirementSibling(
4616+
requirement, [this](AbstractFunctionDecl *cand) {
4617+
return !cand->getAttrs().hasAttribute<OptionalAttr>() &&
4618+
!cand->isImplicit() &&
4619+
this->Conformance->hasWitness(cand);
4620+
})) {
4621+
continue;
4622+
}
4623+
46244624
// Try to resolve the witness.
46254625
switch (resolveWitnessTryingAllStrategies(requirement)) {
46264626
case ResolveWitnessResult::Success:
@@ -4638,6 +4638,33 @@ void ConformanceChecker::resolveValueWitnesses() {
46384638
}
46394639
}
46404640

4641+
ValueDecl *ConformanceChecker::getObjCRequirementSibling(ValueDecl *requirement,
4642+
llvm::function_ref<bool(AbstractFunctionDecl*)> predicate) {
4643+
if (!Proto->isObjC())
4644+
return nullptr;
4645+
4646+
assert(requirement->isProtocolRequirement());
4647+
assert(Proto == requirement->getDeclContext()->getAsDecl());
4648+
4649+
// We only care about functions
4650+
if (auto fnRequirement = dyn_cast<AbstractFunctionDecl>(requirement)) {
4651+
auto fnSelector = getObjCMethodKey(fnRequirement);
4652+
auto similarRequirements = getObjCRequirements(fnSelector);
4653+
// ... whose selector is one that maps to multiple requirement declarations.
4654+
for (auto candidate : similarRequirements) {
4655+
if (candidate == fnRequirement)
4656+
continue; // skip the requirement we're trying to resolve.
4657+
4658+
if (!predicate(candidate))
4659+
continue; // skip if doesn't match requirements
4660+
4661+
return candidate;
4662+
}
4663+
}
4664+
4665+
return nullptr;
4666+
}
4667+
46414668
void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
46424669
assert(!Conformance->isComplete() && "Conformance is already complete");
46434670

lib/Sema/TypeCheckProtocol.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,14 @@ class ConformanceChecker : public WitnessChecker {
851851
/// Retrieve the Objective-C requirements in this protocol that have the
852852
/// given Objective-C method key.
853853
ArrayRef<AbstractFunctionDecl *> getObjCRequirements(ObjCMethodKey key);
854+
855+
/// @returns a non-null requirement if the given requirement is part of a
856+
/// group of ObjC requirements that share the same ObjC method key.
857+
/// The first such requirement that the predicate function returns true for
858+
/// is the requirement required by this function. Otherwise, nullptr is
859+
/// returned.
860+
ValueDecl *getObjCRequirementSibling(ValueDecl *requirement,
861+
llvm::function_ref<bool(AbstractFunctionDecl *)>predicate);
854862
};
855863

856864
/// Captures the state needed to infer associated types.

test/ClangImporter/objc_async_conformance.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension C2 {
2222
// a version of C2 that requires both sync and async methods (differing only by
2323
// completion handler) in ObjC, is not possible to conform to with 'async' in
2424
// a Swift protocol
25-
class C3 : NSObject, RequiredObserver {} // expected-error {{type 'C3' does not conform to protocol 'RequiredObserver'}}
25+
class C3 : NSObject, RequiredObserver {}
2626
extension C3 {
2727
func hello() -> Bool { true } // expected-note {{'hello()' previously declared here}}
2828
func hello() async -> Bool { true } // expected-error {{invalid redeclaration of 'hello()'}}
@@ -35,6 +35,12 @@ extension C4 {
3535
func hello(_ completion : @escaping (Bool) -> Void) -> Void { completion(true) }
3636
}
3737

38+
protocol Club : ObjCClub {}
39+
40+
class ConformsToSync : NSObject, Club {
41+
func activate( completion: @escaping ( Error? ) -> Void ) { }
42+
}
43+
3844
///////
3945
// selector conflicts
4046

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
9797
- (void)rollWithCompletionHandler: (void (^)(void))completionHandler;
9898
@end
9999

100+
typedef void ( ^ObjCErrorHandler )( NSError * _Nullable inError );
101+
102+
@protocol ObjCClub
103+
- (void) activateWithCompletion:(ObjCErrorHandler) inCompletion;
104+
@end
105+
100106
#define MAGIC_NUMBER 42
101107

102108
#pragma clang assume_nonnull end

0 commit comments

Comments
 (0)