Skip to content

Commit 4ab5233

Browse files
committed
Warn about incorrect Sendable for witnesses
1 parent bbec066 commit 4ab5233

File tree

5 files changed

+64
-17
lines changed

5 files changed

+64
-17
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,13 @@ ERROR(witness_not_accessible_proto,none,
21552155
"requirement in %select{private|fileprivate|internal|public|%error}4 protocol "
21562156
"%5",
21572157
(RequirementKind, DeclName, bool, AccessLevel, AccessLevel, Identifier))
2158+
ERROR(witness_not_as_sendable,none,
2159+
"sendability of function types in %0 %1 does not match requirement in "
2160+
"protocol %2",
2161+
(DescriptiveDeclKind, DeclName, Identifier))
2162+
NOTE(less_sendable_reqt_here,none,
2163+
"expected sendability to match requirement here",
2164+
())
21582165
ERROR(witness_not_accessible_type,none,
21592166
"%select{initializer %1|method %1|%select{|setter for }2property %1"
21602167
"|subscript%select{| setter}2}0 must be as accessible as its enclosing "
@@ -2303,6 +2310,9 @@ NOTE(ambiguous_witnesses_type,none,
23032310
"multiple matching types named %0", (Identifier))
23042311
NOTE(protocol_witness_exact_match,none,
23052312
"candidate exactly matches%0", (StringRef))
2313+
NOTE(protocol_witness_non_sendable,none,
2314+
"candidate matches except for closure sendability%0%select{; this will be "
2315+
"an error in Swift 6|}1", (StringRef, bool))
23062316
NOTE(protocol_witness_renamed,none,
23072317
"rename to %0 to satisfy this requirement%1", (DeclName, StringRef))
23082318
NOTE(protocol_witness_kind_conflict,none,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,15 +1162,26 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11621162
return *result;
11631163
}
11641164
}
1165-
if (!solution || solution->Fixes.size())
1166-
return RequirementMatch(witness, MatchKind::TypeConflict,
1167-
witnessType);
1165+
bool requiresNonSendable = false;
1166+
if (!solution || solution->Fixes.size()) {
1167+
/// If the *only* problems are that `@Sendable` attributes are missing,
1168+
/// allow the match in some circumstances.
1169+
requiresNonSendable = solution
1170+
&& llvm::all_of(solution->Fixes, [](constraints::ConstraintFix *fix) {
1171+
return fix->getKind() == constraints::FixKind::AddSendableAttribute;
1172+
});
1173+
if (!requiresNonSendable)
1174+
return RequirementMatch(witness, MatchKind::TypeConflict,
1175+
witnessType);
1176+
}
11681177

11691178
MatchKind matchKind = MatchKind::ExactMatch;
11701179
if (hasAnyError(optionalAdjustments))
11711180
matchKind = MatchKind::OptionalityConflict;
11721181
else if (anyRenaming)
11731182
matchKind = MatchKind::RenamedMatch;
1183+
else if (requiresNonSendable)
1184+
matchKind = MatchKind::RequiresNonSendable;
11741185
else if (getEffects(witness).containsOnly(getEffects(req)))
11751186
matchKind = MatchKind::FewerEffects;
11761187

@@ -2530,6 +2541,12 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
25302541
withAssocTypes);
25312542
break;
25322543

2544+
case MatchKind::RequiresNonSendable:
2545+
diags.diagnose(match.Witness, diag::protocol_witness_non_sendable,
2546+
withAssocTypes,
2547+
module->getASTContext().isSwiftVersionAtLeast(6));
2548+
break;
2549+
25332550
case MatchKind::RenamedMatch: {
25342551
auto diag = diags.diagnose(match.Witness, diag::protocol_witness_renamed,
25352552
req->getName(), withAssocTypes);
@@ -4271,6 +4288,19 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
42714288
requirement->getName());
42724289
});
42734290
}
4291+
if (best.Kind == MatchKind::RequiresNonSendable)
4292+
diagnoseOrDefer(requirement, getASTContext().isSwiftVersionAtLeast(6),
4293+
[this, requirement, witness]
4294+
(NormalProtocolConformance *conformance) {
4295+
auto &diags = DC->getASTContext().Diags;
4296+
4297+
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
4298+
diag::witness_not_as_sendable,
4299+
witness->getDescriptiveKind(), witness->getName(),
4300+
conformance->getProtocol()->getName())
4301+
.warnUntilSwiftVersion(6);
4302+
diags.diagnose(requirement, diag::less_sendable_reqt_here);
4303+
});
42744304

42754305
auto check = checkWitness(requirement, best);
42764306

lib/Sema/TypeCheckProtocol.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ enum class MatchKind : uint8_t {
189189
/// The witness has fewer effects than the requirement, which is okay.
190190
FewerEffects,
191191

192+
/// The witness is @Sendable and the requirement is not. Okay in certain
193+
/// language modes.
194+
RequiresNonSendable,
195+
192196
/// There is a difference in optionality.
193197
OptionalityConflict,
194198

@@ -474,6 +478,7 @@ struct RequirementMatch {
474478
switch(Kind) {
475479
case MatchKind::ExactMatch:
476480
case MatchKind::FewerEffects:
481+
case MatchKind::RequiresNonSendable:
477482
return true;
478483

479484
case MatchKind::OptionalityConflict:
@@ -510,6 +515,7 @@ struct RequirementMatch {
510515
switch(Kind) {
511516
case MatchKind::ExactMatch:
512517
case MatchKind::FewerEffects:
518+
case MatchKind::RequiresNonSendable:
513519
case MatchKind::OptionalityConflict:
514520
case MatchKind::RenamedMatch:
515521
return true;
@@ -545,6 +551,7 @@ struct RequirementMatch {
545551
switch(Kind) {
546552
case MatchKind::ExactMatch:
547553
case MatchKind::FewerEffects:
554+
case MatchKind::RequiresNonSendable:
548555
case MatchKind::RenamedMatch:
549556
case MatchKind::TypeConflict:
550557
case MatchKind::MissingRequirement:

test/attr/attr_concurrent.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ class SubSendable: SuperSendable {
142142

143143
protocol AbstractSendable {
144144
func runsInBackground(_: @Sendable () -> Void)
145-
func runsInForeground(_: () -> Void) // expected-note {{protocol requires function 'runsInForeground' with type '(() -> Void) -> ()'; do you want to add a stub?}}
146-
func runnableInBackground() -> @Sendable () -> Void // expected-note {{protocol requires function 'runnableInBackground()' with type '() -> @Sendable () -> Void'; do you want to add a stub?}}
145+
func runsInForeground(_: () -> Void) // expected-note {{expected sendability to match requirement here}}
146+
func runnableInBackground() -> @Sendable () -> Void // expected-note {{expected sendability to match requirement here}}
147147
func runnableInForeground() -> () -> Void
148148
}
149149

150-
struct ConcreteSendable: AbstractSendable { // expected-error {{type 'ConcreteSendable' does not conform to protocol 'AbstractSendable'}}
150+
struct ConcreteSendable: AbstractSendable {
151151
func runsInBackground(_: () -> Void) {}
152-
func runsInForeground(_: @Sendable () -> Void) {} // expected-note {{candidate has non-matching type '(@Sendable () -> Void) -> ()'}}
153-
func runnableInBackground() -> () -> Void { fatalError() } // expected-note {{candidate has non-matching type '() -> () -> Void'}}
152+
func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'runsInForeground' does not match requirement in protocol 'AbstractSendable'; this is an error in Swift 6}}
153+
func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{sendability of function types in instance method 'runnableInBackground()' does not match requirement in protocol 'AbstractSendable'; this is an error in Swift 6}}
154154
func runnableInForeground() -> @Sendable () -> Void { fatalError() }
155155
}

test/decl/protocol/conforms/associated_type.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Foo: FooType {
3737
protocol P1 {
3838
associatedtype A
3939

40-
func f(_: A) // expected-note {{protocol requires function 'f' with type '(@escaping SendableX1b.A) -> ()' (aka '(@escaping @Sendable (Int) -> Int) -> ()'); do you want to add a stub?}}
40+
func f(_: A) // expected-note {{expected sendability to match requirement here}}
4141
}
4242

4343
struct X1a : P1 {
@@ -61,10 +61,10 @@ struct X1b : P1 {
6161
// instead of adjusting types before adding them to the constraint
6262
// graph, we should introduce a new constraint kind that allows only a
6363
// witness's adjustments.
64-
struct SendableX1b : P1 { // expected-error {{type 'SendableX1b' does not conform to protocol 'P1'}}
64+
struct SendableX1b : P1 {
6565
typealias A = @Sendable (Int) -> Int
6666

67-
func f(_: (Int) -> Int) { } // expected-note {{candidate has non-matching type '((Int) -> Int) -> ()'}}
67+
func f(_: (Int) -> Int) { } // expected-warning {{sendability of function types in instance method 'f' does not match requirement in protocol 'P1'; this is an error in Swift 6}}
6868
}
6969

7070
struct X1c : P1 {
@@ -78,8 +78,8 @@ struct X1d : P1 {
7878
}
7979

8080
protocol P2 {
81-
func f(_: (Int) -> Int) // expected-note 3 {{protocol requires function 'f' with type '((Int) -> Int) -> ()'; do you want to add a stub?}}
82-
func g(_: @escaping (Int) -> Int) // expected-note 2 {{protocol requires function 'g' with type '(@escaping (Int) -> Int) -> ()'; do you want to add a stub?}}
81+
func f(_: (Int) -> Int) // expected-note{{expected sendability to match requirement here}} expected-note 2{{protocol requires function 'f' with type '((Int) -> Int) -> ()'; do you want to add a stub?}}
82+
func g(_: @escaping (Int) -> Int) // expected-note 2 {{expected sendability to match requirement here}}
8383
func h(_: @Sendable (Int) -> Int) // expected-note 2 {{protocol requires function 'h' with type '(@Sendable (Int) -> Int) -> ()'; do you want to add a stub?}}
8484
func i(_: @escaping @Sendable (Int) -> Int)
8585
}
@@ -98,16 +98,16 @@ struct X2b : P2 { // expected-error{{type 'X2b' does not conform to protocol 'P2
9898
func i(_: @escaping (Int) -> Int) { }
9999
}
100100

101-
struct X2c : P2 { // expected-error{{type 'X2c' does not conform to protocol 'P2'}}
102-
func f(_: @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@Sendable (Int) -> Int) -> ()'}}
103-
func g(_: @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@Sendable (Int) -> Int) -> ()'}}
101+
struct X2c : P2 {
102+
func f(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'f' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
103+
func g(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
104104
func h(_: @Sendable (Int) -> Int) { }
105105
func i(_: @Sendable (Int) -> Int) { }
106106
}
107107

108108
struct X2d : P2 { // expected-error{{type 'X2d' does not conform to protocol 'P2'}}
109109
func f(_: @escaping @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping @Sendable (Int) -> Int) -> ()'}}
110-
func g(_: @escaping @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping @Sendable (Int) -> Int) -> ()'}}
110+
func g(_: @escaping @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
111111
func h(_: @escaping @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping @Sendable (Int) -> Int) -> ()'}}
112112
func i(_: @escaping @Sendable (Int) -> Int) { }
113113
}

0 commit comments

Comments
 (0)