Skip to content

Commit 9cbd8e1

Browse files
authored
Merge pull request #79382 from xedin/more-Sendable-to-Any-problems
[Concurrency] Fix a few issues with `Senable` and `Any`
2 parents 788d0f9 + f474588 commit 9cbd8e1

8 files changed

+144
-13
lines changed

lib/Sema/CSApply.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7414,6 +7414,19 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
74147414
/*isImplicit*/ true));
74157415
}
74167416

7417+
case TypeKind::InOut: {
7418+
auto *inOutExpr = getAsExpr<InOutExpr>(expr);
7419+
if (!inOutExpr)
7420+
break;
7421+
7422+
// If there is an `any Sendable` -> `Any` mismatch here,
7423+
// the conversion should be performed on l-value and the
7424+
// address taken from that. This is something that is already
7425+
// done as part of implicit `inout` injection for operators
7426+
// and could be reused here.
7427+
return coerceToType(inOutExpr->getSubExpr(), toType, locator);
7428+
}
7429+
74177430
case TypeKind::Pack:
74187431
case TypeKind::PackElement: {
74197432
llvm_unreachable("Unimplemented!");
@@ -7777,7 +7790,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
77777790
#define TYPE(Name, Parent)
77787791
#include "swift/AST/TypeNodes.def"
77797792
case TypeKind::Error:
7780-
case TypeKind::InOut:
77817793
case TypeKind::Module:
77827794
case TypeKind::Enum:
77837795
case TypeKind::Struct:

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3733,7 +3733,8 @@ static bool matchSendableExistentialToAnyInGenericArgumentPosition(
37333733

37343734
for (unsigned i = 0, n = path.size(); i < n; ++i) {
37353735
const auto &elt = path[i];
3736-
if (elt.is<LocatorPathElt::GenericType>()) {
3736+
if (elt.is<LocatorPathElt::GenericType>() ||
3737+
elt.is<LocatorPathElt::LValueConversion>()) {
37373738
if (!dropFromIdx)
37383739
dropFromIdx = i;
37393740
continue;
@@ -3774,6 +3775,12 @@ static bool matchSendableExistentialToAnyInGenericArgumentPosition(
37743775
return isPreconcurrencyContext(
37753776
cs.getConstraintLocator(simplifyLocatorToAnchor(locator)));
37763777

3778+
if (locator->directlyAt<InOutExpr>()) {
3779+
auto *IOE = castToExpr<InOutExpr>(locator->getAnchor());
3780+
return isPreconcurrencyContext(
3781+
cs.getConstraintLocator(IOE->getSubExpr()));
3782+
}
3783+
37773784
auto *calleeLoc = cs.getCalleeLocator(locator);
37783785
if (!calleeLoc)
37793786
return false;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4351,6 +4351,9 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
43514351
if (auto *expr = OpaqueValueMap[OVE])
43524352
return markStoredOrInOutExpr(expr, Flags);
43534353

4354+
if (auto *ABIConv = dyn_cast<ABISafeConversionExpr>(E))
4355+
return markStoredOrInOutExpr(ABIConv->getSubExpr(), Flags);
4356+
43544357
// If we don't know what kind of expression this is, assume it's a reference
43554358
// and mark it as a read.
43564359
E->walk(*this);

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,17 +3593,9 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
35933593

35943594
ExprStack.push_back(E);
35953595

3596-
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3597-
bool preconcurrency = false;
3598-
auto *fn = apply->getFn();
3599-
if (auto *selfApply = dyn_cast<SelfApplyExpr>(fn)) {
3600-
fn = selfApply->getFn();
3601-
}
3602-
auto declRef = fn->getReferencedDecl();
3603-
if (auto *decl = declRef.getDecl()) {
3604-
preconcurrency = decl->preconcurrency();
3605-
}
3606-
PreconcurrencyCalleeStack.push_back(preconcurrency);
3596+
if (isa<ApplyExpr>(E)) {
3597+
PreconcurrencyCalleeStack.push_back(
3598+
hasReferenceToPreconcurrencyDecl(E));
36073599
}
36083600

36093601
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
@@ -3629,6 +3621,8 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
36293621
if (S->hasDecl()) {
36303622
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange(), S);
36313623
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
3624+
PreconcurrencyCalleeStack.push_back(
3625+
hasReferenceToPreconcurrencyDecl(S));
36323626
}
36333627
}
36343628

@@ -3677,6 +3671,11 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
36773671
maybeDiagKeyPath(KP);
36783672
}
36793673
if (auto A = dyn_cast<AssignExpr>(E)) {
3674+
// Attempting to assign to a @preconcurrency declaration should
3675+
// downgrade Sendable conformance mismatches to warnings.
3676+
PreconcurrencyCalleeStack.push_back(
3677+
hasReferenceToPreconcurrencyDecl(A->getDest()));
3678+
36803679
walkAssignExpr(A);
36813680
return Action::SkipChildren(E);
36823681
}
@@ -4007,6 +4006,41 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
40074006
Flags))
40084007
return;
40094008
}
4009+
4010+
/// Check whether the given expression references any
4011+
/// @preconcurrency declarations.
4012+
/// Calls, subscripts, member references can have @preconcurrency
4013+
/// declarations at any point in their base chain.
4014+
bool hasReferenceToPreconcurrencyDecl(Expr *expr) {
4015+
if (auto declRef = expr->getReferencedDecl()) {
4016+
if (declRef.getDecl()->preconcurrency())
4017+
return true;
4018+
}
4019+
4020+
if (auto *selfApply = dyn_cast<SelfApplyExpr>(expr)) {
4021+
if (hasReferenceToPreconcurrencyDecl(selfApply->getFn()))
4022+
return true;
4023+
4024+
// Base could be a preconcurrency declaration i.e.
4025+
//
4026+
// @preconcurrency var x: [any Sendable]
4027+
// x.append(...)
4028+
//
4029+
// If thought `append` might not be `@preconcurrency`
4030+
// the "base" is.
4031+
return hasReferenceToPreconcurrencyDecl(selfApply->getBase());
4032+
}
4033+
4034+
if (auto *LE = dyn_cast<LookupExpr>(expr)) {
4035+
// If subscript itself is not @preconcurrency, it's base could be.
4036+
return hasReferenceToPreconcurrencyDecl(LE->getBase());
4037+
}
4038+
4039+
if (auto *apply = dyn_cast<ApplyExpr>(expr))
4040+
return hasReferenceToPreconcurrencyDecl(apply->getFn());
4041+
4042+
return false;
4043+
}
40104044
};
40114045
} // end anonymous namespace
40124046

test/Concurrency/predates_concurrency_swift6.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,25 @@ func testErasureDowngrade(ns: NotSendable, us: UnavailableSendable, c: C) {
289289
// expected-error@-1 {{conformance of 'UnavailableSendable' to 'Sendable' is unavailable}}
290290
}
291291
}
292+
293+
// The member itself could be non-preconcurrency but the base could be.
294+
do {
295+
@preconcurrency var d: [String: any Sendable] = [:]
296+
297+
let data: [String: Any] = [:]
298+
d.merge(data, uniquingKeysWith: { _, rhs in rhs})
299+
// expected-warning@-1 {{type 'Any' does not conform to the 'Sendable' protocol}}
300+
301+
struct Test {
302+
@preconcurrency var info: [String: any Sendable] = [:]
303+
}
304+
305+
func test(s: inout Test) {
306+
s.info["hello"] = { }
307+
// expected-warning@-1 {{type '() -> ()' does not conform to the 'Sendable' protocol}}
308+
// expected-note@-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
309+
}
310+
311+
// If destination is @preconcurrency the Sendable conformance error should be downgraded
312+
d = data // expected-warning {{type 'Any' does not conform to the 'Sendable' protocol}}
313+
}

test/Concurrency/sendable_to_any_for_generic_arguments.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,15 @@ extension User {
189189
// expected-error@-1 {{referencing initializer 'init(age:)' on '[String : any Sendable].Type' requires the types 'any Sendable' and 'Any' be equivalent}}
190190
}
191191
}
192+
193+
// https://github.com/swiftlang/swift/issues/79361
194+
do {
195+
@preconcurrency var d = Dictionary<String, any Sendable>()
196+
197+
func test(_ dict: inout Dictionary<String, Any>) {}
198+
test(&d) // Ok
199+
200+
@preconcurrency var a = Array<any Sendable>()
201+
let values: [Any] = []
202+
a += values // Ok
203+
}

test/Interpreter/sendable_erasure_to_any_in_preconcurrency.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ struct Test {
4141
@preconcurrency var funcRef: S<([any Sendable]) -> (any Sendable)?> = S(v: { $0.first })
4242
}
4343

44+
func testInOut(_ dict: inout Dictionary<String, Any>) {
45+
dict["inout"] = "yes"
46+
}
47+
48+
func testInOut(_ arr: inout [Any]) {
49+
arr.append("inout")
50+
}
4451

4552
func test() {
4653
var c = C()
@@ -98,6 +105,19 @@ func test() {
98105

99106
expectsFuncAny(v1.funcRef)
100107
// CHECK: 42
108+
109+
testInOut(&c.dict)
110+
print(c.dict["inout"] ?? "no")
111+
// CHECK: yes
112+
113+
testInOut(&c.arr)
114+
print(c.arr.contains(where: { $0 as? String == "inout" }))
115+
// CHECK: true
116+
117+
var newValues: [Any] = ["other inout"]
118+
c.arr += newValues // checks implicit inout injection via operator
119+
print(c.arr.contains(where: { $0 as? String == "other inout" }))
120+
// CHECK: true
101121
}
102122

103123
test()

test/SILGen/sendable_to_any_for_generic_arguments.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,24 @@ func test_subscript_computed_property_and_mutating_access(u: User) {
261261
// CHECK-NEXT: assign [[COPIED_SENDABLE_DICT]] to [[DICT]]
262262
u.dict.testMutating()
263263
}
264+
265+
// CHECK-LABEL: sil hidden [ossa] @$s37sendable_to_any_for_generic_arguments15test_inout_usesyyF
266+
// CHECK: [[SENDABLE_ARR_REF:%.*]] = begin_access [modify] [unknown] %2
267+
// CHECK-NEXT: [[ANY_ARR:%.*]] = alloc_stack $Array<Any>
268+
// CHECK-NEXT: [[SENDABLE_ARR:%.*]] = load [copy] [[SENDABLE_ARR_REF]]
269+
// CHECK-NEXT: [[ANY_ARR_CAST:%.*]] = unchecked_bitwise_cast [[SENDABLE_ARR]] to $Array<Any>
270+
// CHECK-NEXT: [[ANY_ARR_COPY:%.*]] = copy_value [[ANY_ARR_CAST]]
271+
// CHECK-NEXT: store [[ANY_ARR_COPY]] to [init] [[ANY_ARR]]
272+
// CHECK: [[INOUT_FUNC:%.*]] = function_ref @$s37sendable_to_any_for_generic_arguments15test_inout_usesyyF0G0L_yySayypGzF : $@convention(thin) (@inout Array<Any>) -> ()
273+
// CHECK-NEXT: {{.*}} = apply [[INOUT_FUNC]]([[ANY_ARR]]) : $@convention(thin) (@inout Array<Any>) -> ()
274+
// CHECK-NEXT: [[ANY_ARR_VALUE:%.*]] = load [take] [[ANY_ARR]]
275+
// CHECK-NEXT: [[SENDABLE_ARR_VALUE:%.*]] = unchecked_bitwise_cast [[ANY_ARR_VALUE]] to $Array<any Sendable>
276+
// CHECK-NEXT: [[SENDABLE_ARR_VALUE_COPY:%.*]] = copy_value [[SENDABLE_ARR_VALUE]]
277+
// CHECK-NEXT: assign [[SENDABLE_ARR_VALUE_COPY]] to [[SENDABLE_ARR_REF]]
278+
func test_inout_uses() {
279+
func test(_ arr: inout [Any]) {
280+
}
281+
282+
@preconcurrency var arr: [any Sendable] = []
283+
test(&arr)
284+
}

0 commit comments

Comments
 (0)