Skip to content

Commit 0e98bf2

Browse files
committed
Introduce missing Sendable conformances for existential conversions
When performing conversions to an existential that involves Sendable, introducing missing conformances as needed to allow the type-check to succeed and then (later) they'll be diagnosed appropriately. Fixes rdar://89992095.
1 parent 0fded01 commit 0e98bf2

File tree

5 files changed

+75
-8
lines changed

5 files changed

+75
-8
lines changed

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5401,7 +5401,7 @@ collectExistentialConformances(Type fromType, Type toType,
54015401
SmallVector<ProtocolConformanceRef, 4> conformances;
54025402
for (auto proto : layout.getProtocols()) {
54035403
conformances.push_back(TypeChecker::containsProtocol(
5404-
fromType, proto, module));
5404+
fromType, proto, module, false, /*allowMissing=*/true));
54055405
}
54065406

54075407
return toType->getASTContext().AllocateCopy(conformances);

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7106,7 +7106,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
71067106
case ConstraintKind::SelfObjectOfProtocol: {
71077107
auto conformance = TypeChecker::containsProtocol(
71087108
type, protocol, DC->getParentModule(),
7109-
/*skipConditionalRequirements=*/true);
7109+
/*skipConditionalRequirements=*/true,
7110+
/*allowMissing=*/true);
71107111
if (conformance) {
71117112
return recordConformance(conformance);
71127113
}

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5538,7 +5538,8 @@ void ConformanceChecker::emitDelayedDiags() {
55385538

55395539
ProtocolConformanceRef
55405540
TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5541-
bool skipConditionalRequirements) {
5541+
bool skipConditionalRequirements,
5542+
bool allowMissing) {
55425543
// Existential types don't need to conform, i.e., they only need to
55435544
// contain the protocol.
55445545
if (T->isExistentialType()) {
@@ -5560,8 +5561,9 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
55605561
if (auto superclass = layout.getSuperclass()) {
55615562
auto result =
55625563
(skipConditionalRequirements
5563-
? M->lookupConformance(superclass, Proto)
5564-
: TypeChecker::conformsToProtocol(superclass, Proto, M));
5564+
? M->lookupConformance(superclass, Proto, allowMissing)
5565+
: TypeChecker::conformsToProtocol(
5566+
superclass, Proto, M, allowMissing));
55655567
if (result) {
55665568
return result;
55675569
}
@@ -5579,13 +5581,22 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
55795581
return ProtocolConformanceRef(Proto);
55805582
}
55815583

5584+
// FIXME: Unify with shouldCreateMissingConformances
5585+
if (allowMissing &&
5586+
Proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
5587+
return ProtocolConformanceRef(
5588+
M->getASTContext().getBuiltinConformance(
5589+
T, Proto, GenericSignature(), { },
5590+
BuiltinConformanceKind::Missing));
5591+
}
5592+
55825593
return ProtocolConformanceRef::forInvalid();
55835594
}
55845595

55855596
// For non-existential types, this is equivalent to checking conformance.
55865597
return (skipConditionalRequirements
5587-
? M->lookupConformance(T, Proto)
5588-
: TypeChecker::conformsToProtocol(T, Proto, M));
5598+
? M->lookupConformance(T, Proto, allowMissing)
5599+
: TypeChecker::conformsToProtocol(T, Proto, M, allowMissing));
55895600
}
55905601

55915602
ProtocolConformanceRef

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,8 @@ Expr *addImplicitLoadExpr(
788788
/// an empty optional.
789789
ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto,
790790
ModuleDecl *M,
791-
bool skipConditionalRequirements=false);
791+
bool skipConditionalRequirements=false,
792+
bool allowMissing=false);
792793

793794
/// Determine whether the given type conforms to the given protocol.
794795
///
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %target-typecheck-verify-swift -strict-concurrency=targeted
2+
// REQUIRES: concurrency
3+
// REQUIRES: OS=macosx
4+
5+
@preconcurrency func send(_: Sendable) { }
6+
func sendOpt(_: Sendable?) { }
7+
8+
enum E {
9+
case something(Sendable)
10+
}
11+
12+
@available(SwiftStdlib 5.1, *)
13+
func testE(a: Any, aOpt: Any?) async {
14+
send(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
15+
sendOpt(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
16+
sendOpt(aOpt) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
17+
18+
let _: E = .something(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
19+
_ = E.something(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
20+
21+
var sendable: Sendable
22+
sendable = a // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}}
23+
24+
var arrayOfSendable: [Sendable]
25+
arrayOfSendable = [a, a] // expected-warning 2{{type 'Any' does not conform to the 'Sendable' protocol}}
26+
27+
func localFunc() { }
28+
sendable = localFunc // expected-warning{{type '() -> ()' does not conform to the 'Sendable' protocol}}
29+
// expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
30+
31+
_ = sendable
32+
_ = arrayOfSendable
33+
}
34+
35+
func testESilently(a: Any, aOpt: Any?) {
36+
send(a)
37+
sendOpt(a)
38+
sendOpt(aOpt)
39+
40+
let _: E = .something(a)
41+
_ = E.something(a)
42+
43+
var sendable: Sendable
44+
sendable = a
45+
46+
var arrayOfSendable: [Sendable]
47+
arrayOfSendable = [a, a]
48+
49+
func localFunc() { }
50+
sendable = localFunc
51+
52+
_ = sendable
53+
_ = arrayOfSendable
54+
}

0 commit comments

Comments
 (0)