Skip to content

Commit abab647

Browse files
authored
Merge pull request swiftlang#79430 from xedin/more-Sendable-to-Any-problems-6.1
[6.1][Concurrency] Fix a few issues with matching `any Sendable` to `Any`
2 parents cc31a21 + 0a237ae commit abab647

9 files changed

+160
-14
lines changed

lib/Sema/CSApply.cpp

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

7471+
case TypeKind::InOut: {
7472+
auto *inOutExpr = getAsExpr<InOutExpr>(expr);
7473+
if (!inOutExpr)
7474+
break;
7475+
7476+
// If there is an `any Sendable` -> `Any` mismatch here,
7477+
// the conversion should be performed on l-value and the
7478+
// address taken from that. This is something that is already
7479+
// done as part of implicit `inout` injection for operators
7480+
// and could be reused here.
7481+
return coerceToType(inOutExpr->getSubExpr(), toType, locator);
7482+
}
7483+
74717484
case TypeKind::Pack:
74727485
case TypeKind::PackElement: {
74737486
llvm_unreachable("Unimplemented!");
@@ -7819,7 +7832,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
78197832
#define TYPE(Name, Parent)
78207833
#include "swift/AST/TypeNodes.def"
78217834
case TypeKind::Error:
7822-
case TypeKind::InOut:
78237835
case TypeKind::Module:
78247836
case TypeKind::Enum:
78257837
case TypeKind::Struct:

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,8 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
10371037
break;
10381038
}
10391039

1040-
case ConstraintLocator::Member: {
1040+
case ConstraintLocator::Member:
1041+
case ConstraintLocator::UnresolvedMember: {
10411042
auto *memberLoc = getConstraintLocator(anchor, path);
10421043
auto selectedOverload = getOverloadChoiceIfAvailable(memberLoc);
10431044
if (!selectedOverload)

lib/Sema/CSSimplify.cpp

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

36693669
for (unsigned i = 0, n = path.size(); i < n; ++i) {
36703670
const auto &elt = path[i];
3671-
if (elt.is<LocatorPathElt::GenericType>()) {
3671+
if (elt.is<LocatorPathElt::GenericType>() ||
3672+
elt.is<LocatorPathElt::LValueConversion>()) {
36723673
if (!dropFromIdx)
36733674
dropFromIdx = i;
36743675
continue;
@@ -3709,6 +3710,12 @@ static bool matchSendableExistentialToAnyInGenericArgumentPosition(
37093710
return isPreconcurrencyContext(
37103711
cs.getConstraintLocator(simplifyLocatorToAnchor(locator)));
37113712

3713+
if (locator->directlyAt<InOutExpr>()) {
3714+
auto *IOE = castToExpr<InOutExpr>(locator->getAnchor());
3715+
return isPreconcurrencyContext(
3716+
cs.getConstraintLocator(IOE->getSubExpr()));
3717+
}
3718+
37123719
auto *calleeLoc = cs.getCalleeLocator(locator);
37133720
if (!calleeLoc)
37143721
return false;

lib/Sema/MiscDiagnostics.cpp

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

4336+
if (auto *ABIConv = dyn_cast<ABISafeConversionExpr>(E))
4337+
return markStoredOrInOutExpr(ABIConv->getSubExpr(), Flags);
4338+
43364339
// If we don't know what kind of expression this is, assume it's a reference
43374340
// and mark it as a read.
43384341
E->walk(*this);

lib/Sema/TypeCheckAvailability.cpp

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

36243624
ExprStack.push_back(E);
36253625

3626-
if (auto *apply = dyn_cast<ApplyExpr>(E)) {
3627-
bool preconcurrency = false;
3628-
auto *fn = apply->getFn();
3629-
if (auto *selfApply = dyn_cast<SelfApplyExpr>(fn)) {
3630-
fn = selfApply->getFn();
3631-
}
3632-
auto declRef = fn->getReferencedDecl();
3633-
if (auto *decl = declRef.getDecl()) {
3634-
preconcurrency = decl->preconcurrency();
3635-
}
3636-
PreconcurrencyCalleeStack.push_back(preconcurrency);
3626+
if (isa<ApplyExpr>(E)) {
3627+
PreconcurrencyCalleeStack.push_back(
3628+
hasReferenceToPreconcurrencyDecl(E));
36373629
}
36383630

36393631
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
@@ -3659,6 +3651,8 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
36593651
if (S->hasDecl()) {
36603652
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange(), S);
36613653
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
3654+
PreconcurrencyCalleeStack.push_back(
3655+
hasReferenceToPreconcurrencyDecl(S));
36623656
}
36633657
}
36643658

@@ -3707,6 +3701,11 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
37073701
maybeDiagKeyPath(KP);
37083702
}
37093703
if (auto A = dyn_cast<AssignExpr>(E)) {
3704+
// Attempting to assign to a @preconcurrency declaration should
3705+
// downgrade Sendable conformance mismatches to warnings.
3706+
PreconcurrencyCalleeStack.push_back(
3707+
hasReferenceToPreconcurrencyDecl(A->getDest()));
3708+
37103709
walkAssignExpr(A);
37113710
return Action::SkipChildren(E);
37123711
}
@@ -4038,6 +4037,41 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
40384037
Flags))
40394038
return;
40404039
}
4040+
4041+
/// Check whether the given expression references any
4042+
/// @preconcurrency declarations.
4043+
/// Calls, subscripts, member references can have @preconcurrency
4044+
/// declarations at any point in their base chain.
4045+
bool hasReferenceToPreconcurrencyDecl(Expr *expr) {
4046+
if (auto declRef = expr->getReferencedDecl()) {
4047+
if (declRef.getDecl()->preconcurrency())
4048+
return true;
4049+
}
4050+
4051+
if (auto *selfApply = dyn_cast<SelfApplyExpr>(expr)) {
4052+
if (hasReferenceToPreconcurrencyDecl(selfApply->getFn()))
4053+
return true;
4054+
4055+
// Base could be a preconcurrency declaration i.e.
4056+
//
4057+
// @preconcurrency var x: [any Sendable]
4058+
// x.append(...)
4059+
//
4060+
// If thought `append` might not be `@preconcurrency`
4061+
// the "base" is.
4062+
return hasReferenceToPreconcurrencyDecl(selfApply->getBase());
4063+
}
4064+
4065+
if (auto *LE = dyn_cast<LookupExpr>(expr)) {
4066+
// If subscript itself is not @preconcurrency, it's base could be.
4067+
return hasReferenceToPreconcurrencyDecl(LE->getBase());
4068+
}
4069+
4070+
if (auto *apply = dyn_cast<ApplyExpr>(expr))
4071+
return hasReferenceToPreconcurrencyDecl(apply->getFn());
4072+
4073+
return false;
4074+
}
40414075
};
40424076
} // end anonymous namespace
40434077

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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,29 @@ func test_subscript_computed_property_and_mutating_access(u: User) {
175175

176176
u.dict.testMutating() // Ok
177177
}
178+
179+
extension Dictionary where Key == String, Value == Any {
180+
init(age: Int) { // expected-note {{'init(age:)' declared here}}
181+
self.init()
182+
}
183+
}
184+
185+
extension User {
186+
convenience init(age: Int) {
187+
self.init()
188+
self.dict = .init(age: age)
189+
// expected-error@-1 {{referencing initializer 'init(age:)' on '[String : any Sendable].Type' requires the types 'any Sendable' and 'Any' be equivalent}}
190+
}
191+
}
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]] : $Array<any Sendable> 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]] : $Array<Any> 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)