Skip to content

Commit e67e1df

Browse files
kavonmeg-gupta
authored andcommitted
Witness selection should pick exact matches for effect overloads
You can overload a function based on its `async`-ness, and resolution is carried out based on async-ness of calling context. But during protocol conformance checking, for an `async` requirement we were accidentally choosing the non-`async overload instead of the `async` one. The `async` one is the choice people would expect, since the `async` requirement is in essence the "context" that forwards to the underlying witness. This intended behavior is also inferred from: swiftlang#40088 The problem boiled down to a bad check when categorizing the witness matches prior to ranking them. Resolves rdar://109135488 / swiftlang#60318
1 parent 9e2c5b3 commit e67e1df

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,9 +1196,13 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11961196
matchKind = MatchKind::RenamedMatch;
11971197
else if (requiresNonSendable)
11981198
matchKind = MatchKind::RequiresNonSendable;
1199-
else if (getEffects(witness).containsOnly(getEffects(req)))
1199+
else if (getEffects(req) - getEffects(witness))
1200+
// when the difference is non-empty, the witness has fewer effects.
12001201
matchKind = MatchKind::FewerEffects;
12011202

1203+
assert(getEffects(req).contains(getEffects(witness))
1204+
&& "witness has more effects than requirement?");
1205+
12021206
// Success. Form the match result.
12031207
RequirementMatch result(witness,
12041208
matchKind,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -o %t/main
3+
// RUN: %target-codesign %t/main
4+
// RUN: %target-run %t/main | %FileCheck %s
5+
6+
// REQUIRES: concurrency
7+
// REQUIRES: executable_test
8+
// REQUIRES: concurrency_runtime
9+
10+
// Ensures the more exact witness from S is chosen
11+
// to fulfill the requirement from P.
12+
// See: https://github.com/apple/swift/issues/60318
13+
14+
protocol P {
15+
func foo() async -> String
16+
}
17+
18+
struct S: P {
19+
func foo() -> String { "plain" }
20+
func foo() async -> String { "async" }
21+
}
22+
23+
let p: P = S()
24+
let ans = await p.foo()
25+
print(ans)
26+
// CHECK: async

0 commit comments

Comments
 (0)