Skip to content

Commit b09073e

Browse files
committed
[sending] Do not let protocol function reqs with a sending result be witnessed by a function without a sending result.
This is important since the witness is not promising to return a disconnected value and the caller of the requirement is going to accept that. rdar://127675288
1 parent e248ebf commit b09073e

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,13 @@ RequirementMatch swift::matchWitness(
743743
reqTypeIsIUO != witnessTypeIsIUO)
744744
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
745745

746+
// If our requirement says that it has a sending result, then our witness
747+
// must also have a sending result since otherwise, in generic contexts,
748+
// we would be returning non-disconnected values as disconnected.
749+
if (reqFnType->hasExtInfo() && reqFnType->hasSendingResult() &&
750+
(!witnessFnType->hasExtInfo() || !witnessFnType->hasSendingResult()))
751+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
752+
746753
if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
747754
return std::move(result.value());
748755
}
@@ -775,6 +782,12 @@ RequirementMatch swift::matchWitness(
775782
if (reqParams[i].isInOut() != witnessParams[i].isInOut())
776783
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
777784

785+
// If we have a requirement without sending and our witness expects a
786+
// sending parameter, error.
787+
if (!reqParams[i].getParameterFlags().isSending() &&
788+
witnessParams[i].getParameterFlags().isSending())
789+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
790+
778791
auto reqParamDecl = reqParamList->get(i);
779792
auto witnessParamDecl = witnessParamList->get(i);
780793

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-swift-frontend -swift-version 6 -verify -c %s
2+
3+
// READ THIS! This file only contains tests that validate that the relevant
4+
// function subtyping rules for sending work. Please do not put other tests in
5+
// the file!
6+
7+
// REQUIRES: concurrency
8+
// REQUIRES: asserts
9+
10+
////////////////////////
11+
// MARK: Declarations //
12+
////////////////////////
13+
14+
class NonSendableKlass {}
15+
16+
protocol ProtocolWithSendingReqs {
17+
func sendingResult() -> sending NonSendableKlass // expected-note {{}}
18+
func nonSendingParam(_ x: NonSendableKlass) // expected-note {{}}
19+
}
20+
21+
protocol ProtocolWithMixedReqs {
22+
func nonSendingParamAndSendingResult(_ x: NonSendableKlass) -> sending NonSendableKlass // expected-note 4{{}}
23+
}
24+
25+
/////////////////
26+
// MARK: Tests //
27+
/////////////////
28+
29+
struct MatchSuccess : ProtocolWithSendingReqs, ProtocolWithMixedReqs {
30+
func sendingResult() -> sending NonSendableKlass { fatalError() }
31+
func nonSendingParam(_ x: NonSendableKlass) -> () { fatalError() }
32+
func nonSendingParamAndSendingResult(_ x: NonSendableKlass) -> sending NonSendableKlass { fatalError() }
33+
}
34+
35+
struct FailToMatch : ProtocolWithSendingReqs, ProtocolWithMixedReqs { // expected-error 2{{}}
36+
func sendingResult() -> NonSendableKlass { fatalError() }
37+
// expected-note @-1 {{candidate has non-matching type '() -> NonSendableKlass'}}
38+
func nonSendingParam(_ x: sending NonSendableKlass) -> () { fatalError() }
39+
// expected-note @-1 {{candidate has non-matching type '(sending NonSendableKlass) -> ()'}}
40+
func nonSendingParamAndSendingResult(_ x: sending NonSendableKlass) -> NonSendableKlass { fatalError() }
41+
// expected-note @-1 {{candidate has non-matching type '(sending NonSendableKlass) -> NonSendableKlass'}}
42+
}
43+
44+
struct FailToMatch2 : ProtocolWithMixedReqs { // expected-error {{}}
45+
func nonSendingParamAndSendingResult(_ x: sending NonSendableKlass) -> NonSendableKlass { fatalError() }
46+
// expected-note @-1 {{candidate has non-matching type '(sending NonSendableKlass) -> NonSendableKlass'}}
47+
}
48+
49+
struct FailToMatch3 : ProtocolWithMixedReqs { // expected-error {{}}
50+
func nonSendingParamAndSendingResult(_ x: NonSendableKlass) -> NonSendableKlass { fatalError() }
51+
// expected-note @-1 {{candidate has non-matching type '(NonSendableKlass) -> NonSendableKlass'}}
52+
}
53+
54+
struct FailToMatch4 : ProtocolWithMixedReqs { // expected-error {{}}
55+
func nonSendingParamAndSendingResult(_ x: sending NonSendableKlass) -> sending NonSendableKlass { fatalError() }
56+
// expected-note @-1 {{candidate has non-matching type '(sending NonSendableKlass) -> sending NonSendableKlass'}}
57+
}

0 commit comments

Comments
 (0)