Skip to content

Commit c845332

Browse files
authored
Merge pull request #70569 from tshortli/dont-be-such-a-completionist
AST: Improve handling of missing @objc async protocol witnesses
2 parents ba4cedd + 950c5e9 commit c845332

File tree

2 files changed

+59
-39
lines changed

2 files changed

+59
-39
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3804,7 +3804,7 @@ filterProtocolRequirements(
38043804

38053805
/// Prune the set of missing witnesses for the given conformance, eliminating
38063806
/// any requirements that do not actually need to satisfied.
3807-
static ArrayRef<MissingWitness> pruneMissingWitnesses( // FIXME: this does not remove the missing witness note here!!!
3807+
static ArrayRef<MissingWitness> pruneMissingWitnesses(
38083808
ConformanceChecker &checker,
38093809
ProtocolDecl *proto,
38103810
NormalProtocolConformance *conformance,
@@ -3814,14 +3814,6 @@ static ArrayRef<MissingWitness> pruneMissingWitnesses( // FIXME: this does not r
38143814
return missingWitnesses;
38153815
}
38163816

3817-
// For an @objc protocol defined in Objective-C, the Clang importer might
3818-
// have imported the same underlying Objective-C declaration as more than
3819-
// one Swift declaration. If we aren't in an imported @objc protocol, there
3820-
// is nothing to do.
3821-
if (!proto->isObjC()) {
3822-
return missingWitnesses;
3823-
}
3824-
38253817
// Consider each of the missing witnesses to remove any that should not
38263818
// longer be considered "missing".
38273819
llvm::SmallDenseSet<ConformanceChecker::ObjCMethodKey>
@@ -3857,30 +3849,46 @@ static ArrayRef<MissingWitness> pruneMissingWitnesses( // FIXME: this does not r
38573849
continue;
38583850
}
38593851

3852+
// For an @objc protocol defined in Objective-C, the Clang importer might
3853+
// have imported the same underlying Objective-C declaration as more than
3854+
// one Swift declaration. Only one of those requirements should be witnessed
3855+
// so if the requirement sibling is witnessed then this requirement is not
3856+
// missing.
3857+
3858+
// Prune requirements that come from other protocols - we don't want to
3859+
// attempt to diagnose those here since we won't be able to find their
3860+
// siblings.
3861+
if (proto != missingWitness.requirement->getDeclContext()->getAsDecl()) {
3862+
skipWitness();
3863+
continue;
3864+
}
3865+
3866+
if (!proto->isObjC()) {
3867+
addWitness();
3868+
continue;
3869+
}
3870+
38603871
auto fnRequirement = cast<AbstractFunctionDecl>(missingWitness.requirement);
38613872
auto key = checker.getObjCMethodKey(fnRequirement);
38623873

3863-
// If we have already reported a function with this selector as missing,
3864-
// don't do it again.
3865-
if (alreadyReportedAsMissing.count(key)) {
3874+
if (checker.getObjCRequirementSibling(
3875+
fnRequirement, [conformance](AbstractFunctionDecl *candidate) {
3876+
return static_cast<bool>(conformance->getWitness(candidate));
3877+
})) {
38663878
skipWitness();
38673879
continue;
38683880
}
38693881

3870-
auto sibling = checker.getObjCRequirementSibling(
3871-
fnRequirement, [conformance](AbstractFunctionDecl *candidate) {
3872-
return static_cast<bool>(conformance->getWitness(candidate));
3873-
});
3874-
3875-
if (!sibling) {
3876-
alreadyReportedAsMissing.insert(key);
3877-
addWitness();
3882+
// If we have already reported a function with this selector as missing,
3883+
// don't do it again.
3884+
if (alreadyReportedAsMissing.count(key)) {
3885+
skipWitness();
38783886
continue;
38793887
}
38803888

3881-
// Otherwise, there is a witness for any of the *other* requirements with
3882-
// this same selector, so prune it out.
3883-
skipWitness();
3889+
// There is really a missing requirement for this witness.
3890+
alreadyReportedAsMissing.insert(key);
3891+
addWitness();
38843892
}
38853893

38863894
if (removedAny) {
@@ -5402,22 +5410,6 @@ void ConformanceChecker::resolveValueWitnesses() {
54025410
continue;
54035411
}
54045412

5405-
// If this requirement is part of a pair of imported async requirements,
5406-
// where one has already been witnessed, we can skip it.
5407-
//
5408-
// This situation primarily arises when the ClangImporter translates an
5409-
// async-looking ObjC protocol method requirement into two Swift protocol
5410-
// requirements: an async version and a sync version. Exactly one of the two
5411-
// must be witnessed by the conformer.
5412-
if (!requirement->isImplicit() && getObjCRequirementSibling(
5413-
requirement, [this](AbstractFunctionDecl *cand) {
5414-
return !cand->getAttrs().hasAttribute<OptionalAttr>() &&
5415-
!cand->isImplicit() &&
5416-
this->Conformance->hasWitness(cand);
5417-
})) {
5418-
continue;
5419-
}
5420-
54215413
// Try substituting into the requirement's interface type. If we fail,
54225414
// either a generic requirement was not satisfied or we tripped on an
54235415
// invalid type witness, and there's no point in resolving a witness.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-emit-module-interface(%t/Conformance.swiftinterface) -module-name Conformance -I %t %t/Conformance.swift
4+
// RUN: %target-swift-frontend -compile-module-from-interface %t/Conformance.swiftinterface -module-name Conformance -o /dev/null -I %t
5+
// REQUIRES: objc_interop
6+
7+
//--- module.modulemap
8+
module ObjCProto {
9+
header "objc_proto.h"
10+
export *
11+
}
12+
13+
//--- objc_proto.h
14+
@protocol Doable
15+
- (void)doItWithCompletion:(void (^)())completion;
16+
@end
17+
18+
//--- Conformance.swift
19+
import ObjCProto
20+
21+
public final class ConformsToDoableWithCompletionHandler: Doable {
22+
public func doIt(completion: @escaping () -> Void) {}
23+
}
24+
25+
@available(SwiftStdlib 5.5, *)
26+
public final class ConformsToDoableWithAsync:Doable {
27+
public func doIt() async {}
28+
}

0 commit comments

Comments
 (0)