Skip to content

Commit ffeb1d5

Browse files
authored
Merge pull request #35719 from kavon/objc-async-conformance-bugs-2-electric-boogaloo
2 parents 6bddba2 + 739617c commit ffeb1d5

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
@@ -1591,17 +1591,11 @@ isUnsatisfiedReq(ConformanceChecker &checker,
15911591
if (!witness) {
15921592
// If another @objc requirement refers to the same Objective-C
15931593
// method, this requirement isn't unsatisfied.
1594-
if (conformance->getProtocol()->isObjC() &&
1595-
isa<AbstractFunctionDecl>(req)) {
1596-
auto funcReq = cast<AbstractFunctionDecl>(req);
1597-
auto key = checker.getObjCMethodKey(funcReq);
1598-
for (auto otherReq : checker.getObjCRequirements(key)) {
1599-
if (otherReq == req)
1600-
continue;
1601-
1602-
if (conformance->getWitness(otherReq))
1603-
return false;
1604-
}
1594+
if (checker.getObjCRequirementSibling(
1595+
req, [conformance](AbstractFunctionDecl *cand) {
1596+
return static_cast<bool>(conformance->getWitness(cand));
1597+
})) {
1598+
return false;
16051599
}
16061600

16071601
// An optional requirement might not have a witness.
@@ -3331,46 +3325,36 @@ static ArrayRef<MissingWitness> pruneMissingWitnesses(
33313325
scratch.push_back(missingWitness);
33323326
};
33333327

3334-
// We only care about functions
3335-
auto funcRequirement = dyn_cast<AbstractFunctionDecl>(
3336-
missingWitness.requirement);
3337-
if (!funcRequirement) {
3328+
// We only care about functions.
3329+
if (!isa<AbstractFunctionDecl>(missingWitness.requirement)) {
33383330
addWitness();
33393331
continue;
33403332
}
33413333

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

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

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

3364-
if (conformance->getWitness(otherReq)) {
3365-
foundOtherWitness = true;
3366-
break;
3367-
}
3349+
if (!sibling) {
3350+
alreadyReportedAsMissing.insert(key);
3351+
addWitness();
3352+
continue;
33683353
}
33693354

3370-
if (foundOtherWitness)
3371-
skipWitness();
3372-
else
3373-
addWitness();
3355+
// Otherwise, there is a witness for any of the *other* requirements with
3356+
// this same selector, so prune it out.
3357+
skipWitness();
33743358
}
33753359

33763360
if (removedAny)
@@ -4623,6 +4607,22 @@ void ConformanceChecker::resolveValueWitnesses() {
46234607
if (isa<AccessorDecl>(requirement))
46244608
continue;
46254609

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

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

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)