Skip to content

Commit 4caaa3b

Browse files
authored
Merge pull request swiftlang#42194 from beccadax/witness-accounts-are-only-approximate
Make overloads and witnesses permit @sendable variance
2 parents ef2c999 + 4ab5233 commit 4caaa3b

File tree

8 files changed

+138
-18
lines changed

8 files changed

+138
-18
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 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,
@@ -2602,6 +2612,9 @@ WARNING(generic_param_usable_from_inline_warn,none,
26022612
ERROR(override_multiple_decls_base,none,
26032613
"declaration %0 cannot override more than one superclass declaration",
26042614
(DeclName))
2615+
ERROR(override_sendability_mismatch,none,
2616+
"declaration %0 has a type with different sendability from any potential "
2617+
"overrides", (DeclName))
26052618
ERROR(override_multiple_decls_arg_mismatch,none,
26062619
"declaration %0 has different argument labels from any potential "
26072620
"overrides", (DeclName))

include/swift/AST/Types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ enum class TypeMatchFlags {
295295
/// to be non-escaping, but Swift currently does not.
296296
IgnoreNonEscapingForOptionalFunctionParam = 1 << 4,
297297
/// Allow compatible opaque archetypes.
298-
AllowCompatibleOpaqueTypeArchetypes = 1 << 5
298+
AllowCompatibleOpaqueTypeArchetypes = 1 << 5,
299+
/// Ignore the @Sendable attributes on functions when matching types.
300+
IgnoreFunctionSendability = 1 << 6,
299301
};
300302
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
301303

lib/AST/Type.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3181,6 +3181,16 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
31813181
matchMode.contains(TypeMatchFlags::AllowABICompatible))) {
31823182
ext1 = ext1.withThrows(true);
31833183
}
3184+
3185+
// Removing '@Sendable' is ABI-compatible because there's nothing wrong with
3186+
// a function being sendable when it doesn't need to be.
3187+
if (!ext2.isSendable())
3188+
ext1 = ext1.withConcurrent(false);
3189+
}
3190+
3191+
if (matchMode.contains(TypeMatchFlags::IgnoreFunctionSendability)) {
3192+
ext1 = ext1.withConcurrent(false);
3193+
ext2 = ext2.withConcurrent(false);
31843194
}
31853195

31863196
// If specified, allow an escaping function parameter to override a

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ static bool noteFixableMismatchedTypes(ValueDecl *decl, const ValueDecl *base) {
518518
namespace {
519519
enum class OverrideCheckingAttempt {
520520
PerfectMatch,
521+
MismatchedSendability,
521522
MismatchedOptional,
522523
MismatchedTypes,
523524
BaseName,
@@ -547,6 +548,12 @@ static void diagnoseGeneralOverrideFailure(ValueDecl *decl,
547548
diags.diagnose(decl, diag::override_multiple_decls_base,
548549
decl->getName());
549550
break;
551+
case OverrideCheckingAttempt::MismatchedSendability:
552+
// FIXME: suppress if any matches brought in via @preconcurrency import?
553+
diags.diagnose(decl, diag::override_sendability_mismatch,
554+
decl->getName())
555+
.warnUntilSwiftVersion(6);
556+
break;
550557
case OverrideCheckingAttempt::BaseName:
551558
diags.diagnose(decl, diag::override_multiple_decls_arg_mismatch,
552559
decl->getName());
@@ -886,6 +893,7 @@ SmallVector<OverrideMatch, 2> OverrideMatcher::match(
886893
DeclName name;
887894
switch (attempt) {
888895
case OverrideCheckingAttempt::PerfectMatch:
896+
case OverrideCheckingAttempt::MismatchedSendability:
889897
case OverrideCheckingAttempt::MismatchedOptional:
890898
case OverrideCheckingAttempt::MismatchedTypes:
891899
name = decl->getName();
@@ -976,6 +984,8 @@ SmallVector<OverrideMatch, 2> OverrideMatcher::match(
976984
matchMode |= TypeMatchFlags::AllowNonOptionalForIUOParam;
977985
matchMode |= TypeMatchFlags::IgnoreNonEscapingForOptionalFunctionParam;
978986
}
987+
if (attempt == OverrideCheckingAttempt::MismatchedSendability)
988+
matchMode |= TypeMatchFlags::IgnoreFunctionSendability;
979989

980990
auto declFnTy = getDeclComparisonType()->getAs<AnyFunctionType>();
981991
auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl);
@@ -1277,8 +1287,15 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
12771287
if (emittedMatchError)
12781288
return true;
12791289

1290+
if (attempt == OverrideCheckingAttempt::MismatchedSendability) {
1291+
// FIXME: suppress if any matches brought in via @preconcurrency import?
1292+
diags.diagnose(decl, diag::override_sendability_mismatch,
1293+
decl->getName())
1294+
.warnUntilSwiftVersion(6);
1295+
diags.diagnose(baseDecl, diag::overridden_here);
1296+
}
12801297
// Catch-all to make sure we don't silently accept something we shouldn't.
1281-
if (attempt != OverrideCheckingAttempt::PerfectMatch) {
1298+
else if (attempt != OverrideCheckingAttempt::PerfectMatch) {
12821299
OverrideMatch match{decl, /*isExact=*/false};
12831300
diagnoseGeneralOverrideFailure(decl, match, attempt);
12841301
}
@@ -1374,11 +1391,12 @@ bool swift::checkOverrides(ValueDecl *decl) {
13741391
switch (attempt) {
13751392
case OverrideCheckingAttempt::PerfectMatch:
13761393
break;
1377-
case OverrideCheckingAttempt::MismatchedOptional:
1394+
case OverrideCheckingAttempt::MismatchedSendability:
13781395
// Don't keep looking if the user didn't indicate it's an override.
13791396
if (!decl->getAttrs().hasAttribute<OverrideAttr>())
13801397
return false;
13811398
break;
1399+
case OverrideCheckingAttempt::MismatchedOptional:
13821400
case OverrideCheckingAttempt::MismatchedTypes:
13831401
break;
13841402
case OverrideCheckingAttempt::BaseName:

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,25 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
155155
// function types that aren't in a parameter can be Sendable or not.
156156
// FIXME: Should we check for a Sendable bound on the requirement type?
157157
bool inRequirement = (adjustment != TypeAdjustment::NoescapeToEscaping);
158-
(void)adjustInferredAssociatedType(adjustment, reqtType, inRequirement);
158+
Type adjustedReqtType =
159+
adjustInferredAssociatedType(adjustment, reqtType, inRequirement);
159160

160161
bool inWitness = false;
161162
Type adjustedWitnessType =
162163
adjustInferredAssociatedType(adjustment, witnessType, inWitness);
163-
if (inWitness && !inRequirement)
164-
witnessType = adjustedWitnessType;
164+
165+
switch (variance) {
166+
case VarianceKind::None:
167+
break;
168+
case VarianceKind::Covariant:
169+
if (inRequirement && !inWitness)
170+
reqtType = adjustedReqtType;
171+
break;
172+
case VarianceKind::Contravariant:
173+
if (inWitness && !inRequirement)
174+
witnessType = adjustedWitnessType;
175+
break;
176+
}
165177
};
166178

167179
applyAdjustment(TypeAdjustment::NoescapeToEscaping);
@@ -1150,15 +1162,26 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11501162
return *result;
11511163
}
11521164
}
1153-
if (!solution || solution->Fixes.size())
1154-
return RequirementMatch(witness, MatchKind::TypeConflict,
1155-
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+
}
11561177

11571178
MatchKind matchKind = MatchKind::ExactMatch;
11581179
if (hasAnyError(optionalAdjustments))
11591180
matchKind = MatchKind::OptionalityConflict;
11601181
else if (anyRenaming)
11611182
matchKind = MatchKind::RenamedMatch;
1183+
else if (requiresNonSendable)
1184+
matchKind = MatchKind::RequiresNonSendable;
11621185
else if (getEffects(witness).containsOnly(getEffects(req)))
11631186
matchKind = MatchKind::FewerEffects;
11641187

@@ -2518,6 +2541,12 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
25182541
withAssocTypes);
25192542
break;
25202543

2544+
case MatchKind::RequiresNonSendable:
2545+
diags.diagnose(match.Witness, diag::protocol_witness_non_sendable,
2546+
withAssocTypes,
2547+
module->getASTContext().isSwiftVersionAtLeast(6));
2548+
break;
2549+
25212550
case MatchKind::RenamedMatch: {
25222551
auto diag = diags.diagnose(match.Witness, diag::protocol_witness_renamed,
25232552
req->getName(), withAssocTypes);
@@ -4125,6 +4154,19 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
41254154
requirement->getName());
41264155
});
41274156
}
4157+
if (best.Kind == MatchKind::RequiresNonSendable)
4158+
diagnoseOrDefer(requirement, getASTContext().isSwiftVersionAtLeast(6),
4159+
[this, requirement, witness]
4160+
(NormalProtocolConformance *conformance) {
4161+
auto &diags = DC->getASTContext().Diags;
4162+
4163+
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
4164+
diag::witness_not_as_sendable,
4165+
witness->getDescriptiveKind(), witness->getName(),
4166+
conformance->getProtocol()->getName())
4167+
.warnUntilSwiftVersion(6);
4168+
diags.diagnose(requirement, diag::less_sendable_reqt_here);
4169+
});
41284170

41294171
auto check = checkWitness(requirement, best);
41304172

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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,31 @@ func testExplicitConcurrentClosure() {
125125
}
126126
let _: String = fn // expected-error{{cannot convert value of type '@Sendable () -> Int' to specified type 'String'}}
127127
}
128+
129+
class SuperSendable {
130+
func runsInBackground(_: @Sendable () -> Void) {}
131+
func runsInForeground(_: () -> Void) {} // expected-note {{overridden declaration is here}}
132+
func runnableInBackground() -> @Sendable () -> Void { fatalError() } // expected-note {{overridden declaration is here}}
133+
func runnableInForeground() -> () -> Void { fatalError() }
134+
}
135+
136+
class SubSendable: SuperSendable {
137+
override func runsInBackground(_: () -> Void) {}
138+
override func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{declaration 'runsInForeground' has a type with different sendability from any potential overrides; this is an error in Swift 6}}
139+
override func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{declaration 'runnableInBackground()' has a type with different sendability from any potential overrides; this is an error in Swift 6}}
140+
override func runnableInForeground() -> @Sendable () -> Void { fatalError() }
141+
}
142+
143+
protocol AbstractSendable {
144+
func runsInBackground(_: @Sendable () -> Void)
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}}
147+
func runnableInForeground() -> () -> Void
148+
}
149+
150+
struct ConcreteSendable: AbstractSendable {
151+
func runsInBackground(_: () -> 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}}
154+
func runnableInForeground() -> @Sendable () -> Void { fatalError() }
155+
}

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)