Skip to content

Commit cbcb24f

Browse files
committed
[CSSimplify] Extend any Sendable <-> Any conversion support to function argument/result positions in generic argument context
1 parent c8f3524 commit cbcb24f

File tree

6 files changed

+148
-36
lines changed

6 files changed

+148
-36
lines changed

include/swift/AST/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
675675
/// Is this an existential containing only marker protocols?
676676
bool isMarkerExistential();
677677

678+
/// Is this `any Sendable` type?
679+
bool isSendableExistential();
680+
678681
bool isPlaceholder();
679682

680683
/// Returns true if this contextual type does not satisfy a conformance to

lib/AST/Type.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ bool TypeBase::isMarkerExistential() {
163163
return true;
164164
}
165165

166+
bool TypeBase::isSendableExistential() {
167+
Type constraint = this;
168+
if (auto existential = constraint->getAs<ExistentialType>())
169+
constraint = existential->getConstraintType();
170+
171+
if (!constraint->isConstraintType())
172+
return false;
173+
174+
return constraint->getKnownProtocol() == KnownProtocolKind::Sendable;
175+
}
176+
166177
bool TypeBase::isPlaceholder() {
167178
return is<PlaceholderType>();
168179
}

lib/Sema/CSSimplify.cpp

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3670,14 +3670,69 @@ static ConstraintSystem::TypeMatchResult matchDeepTypeArguments(
36703670
/// Note that it's currently impossible to figure out precisely
36713671
/// where `any Sendable` type came from.
36723672
static bool matchSendableExistentialToAnyInGenericArgumentPosition(
3673-
ConstraintSystem &cs, ConstraintLocatorBuilder locator) {
3673+
ConstraintSystem &cs, Type lhs, Type rhs,
3674+
ConstraintLocatorBuilder locator) {
36743675
auto &ctx = cs.getASTContext();
36753676
if (ctx.isSwiftVersionAtLeast(6) ||
36763677
ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)
36773678
return false;
36783679

3680+
// Avoid heavier checks if are not `any Sendable` and `Any`.
3681+
if (!(lhs->isSendableExistential() || lhs->isAny()) ||
3682+
!(rhs->isSendableExistential() || rhs->isAny()))
3683+
return false;
3684+
36793685
auto last = locator.last();
3680-
if (!last || !last->is<LocatorPathElt::GenericArgument>())
3686+
// `any Sendable` -> `Any` conversion is allowed for generic arguments
3687+
// and for function argument/result positions if generic argument is
3688+
// bound to a function type.
3689+
if (!last || !(last->is<LocatorPathElt::GenericArgument>() ||
3690+
last->is<LocatorPathElt::FunctionArgument>() ||
3691+
last->is<LocatorPathElt::FunctionResult>()))
3692+
return false;
3693+
3694+
SmallVector<LocatorPathElt, 4> path;
3695+
auto anchor = locator.getLocatorParts(path);
3696+
3697+
// Drop all of the elements that would get in a way of
3698+
// finding the underlying declaration reference first.
3699+
{
3700+
bool genericArgumentContext = false;
3701+
bool functionArgumentContext = false;
3702+
3703+
while (!path.empty()) {
3704+
auto last = path.back();
3705+
switch (last.getKind()) {
3706+
case ConstraintLocator::FunctionArgument:
3707+
functionArgumentContext = true;
3708+
path.pop_back();
3709+
continue;
3710+
case ConstraintLocator::GenericArgument:
3711+
genericArgumentContext = true;
3712+
LLVM_FALLTHROUGH;
3713+
case ConstraintLocator::GenericType:
3714+
case ConstraintLocator::FunctionResult:
3715+
path.pop_back();
3716+
continue;
3717+
3718+
default:
3719+
break;
3720+
}
3721+
3722+
break;
3723+
}
3724+
3725+
// If this isn't a generic argument context, stop.
3726+
if (!genericArgumentContext)
3727+
return false;
3728+
3729+
// `matchFunctionTypes` accounts for contravariance even under
3730+
// equality constraint (because it shouldn't matter), but it does
3731+
if (functionArgumentContext)
3732+
std::swap(lhs, rhs);
3733+
}
3734+
3735+
if (!(lhs->isSendableExistential() && rhs->isAny()))
36813736
return false;
36823737

36833738
std::function<bool(ConstraintLocator *)> isPreconcurrencyContext =
@@ -3707,21 +3762,6 @@ static bool matchSendableExistentialToAnyInGenericArgumentPosition(
37073762
return true;
37083763
};
37093764

3710-
SmallVector<LocatorPathElt> path;
3711-
auto anchor = locator.getLocatorParts(path);
3712-
3713-
// Drop all of the generic type and argument elements
3714-
// from the locator first.
3715-
while (!path.empty()) {
3716-
auto last = path.back();
3717-
if (last.is<LocatorPathElt::GenericArgument>() ||
3718-
last.is<LocatorPathElt::GenericType>()) {
3719-
path.pop_back();
3720-
continue;
3721-
}
3722-
break;
3723-
}
3724-
37253765
if (!isPreconcurrencyContext(cs.getConstraintLocator(anchor, path)))
37263766
return false;
37273767

@@ -3777,22 +3817,18 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
37773817
return result;
37783818
}
37793819

3820+
// `any Sendable` -> `Any`
3821+
if (matchSendableExistentialToAnyInGenericArgumentPosition(*this, type1,
3822+
type2, locator))
3823+
return getTypeMatchSuccess();
3824+
37803825
// Handle existential types.
37813826
if (auto *existential1 = type1->getAs<ExistentialType>()) {
37823827
auto existential2 = type2->castTo<ExistentialType>();
37833828

3784-
auto constraintTy1 = existential1->getConstraintType();
3785-
auto constraintTy2 = existential2->getConstraintType();
3786-
3787-
if (constraintTy1->getKnownProtocol() == KnownProtocolKind::Sendable &&
3788-
constraintTy2->isAny()) {
3789-
if (matchSendableExistentialToAnyInGenericArgumentPosition(*this,
3790-
locator))
3791-
return getTypeMatchSuccess();
3792-
}
3793-
37943829
auto result = matchTypes(
3795-
constraintTy1, constraintTy2, ConstraintKind::Bind, subflags,
3830+
existential1->getConstraintType(), existential2->getConstraintType(),
3831+
ConstraintKind::Bind, subflags,
37963832
locator.withPathElement(ConstraintLocator::ExistentialConstraintType));
37973833

37983834
if (result.isFailure())
@@ -4408,10 +4444,8 @@ ConstraintSystem::matchTypesBindTypeVar(
44084444
// with `any Sendable` and other concurrency attributes.
44094445
if (typeVar->getImpl().getGenericParameter() &&
44104446
!flags.contains(TMF_BindingTypeVariable) &&
4411-
type->isMarkerExistential()) {
4412-
auto constraintTy = type->castTo<ExistentialType>()->getConstraintType();
4413-
if (constraintTy->getKnownProtocol() == KnownProtocolKind::Sendable)
4414-
return formUnsolvedResult();
4447+
type->isSendableExistential()) {
4448+
return formUnsolvedResult();
44154449
}
44164450

44174451
// Attempt to fix situations where type variable can't be bound

test/Concurrency/sendable_to_any_for_generic_arguments.swift

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ func test_no_ambiguity_with_Sendable_extension(u: User) {
4848

4949
struct S<T> {
5050
// expected-note@-1 3 {{arguments to generic parameter 'T' ('any Sendable' and 'Any') are expected to be equal}}
51-
// expected-note@-2 4 {{arguments to generic parameter 'T' ('(any Sendable) -> Void' and '(Any) -> Void') are expected to be equal}}
51+
// expected-note@-2 2 {{arguments to generic parameter 'T' ('(any Sendable) -> Void' and '(Any) -> Void') are expected to be equal}}
5252
// expected-swift6-note@-3 3 {{arguments to generic parameter 'T' ('any Sendable' and 'Any') are expected to be equal}}
53+
// expected-swift6-note@-4 2 {{arguments to generic parameter 'T' ('(any Sendable) -> Void' and '(Any) -> Void') are expected to be equal}}
54+
// expected-note@-5 {{arguments to generic parameter 'T' ('(Any) -> Any' and '(any Sendable) -> Any') are expected to be equal}}
55+
// expected-note@-6 {{arguments to generic parameter 'T' ('(Any) -> Any' and '(any Sendable) -> any Sendable') are expected to be equal}}
56+
// expected-note@-7 {{arguments to generic parameter 'T' ('(Any) -> Any' and '(Any) -> any Sendable') are expected to be equal}}
5357
}
5458

5559
extension S where T == Any {
@@ -61,6 +65,8 @@ struct TestGeneral {
6165
@preconcurrency var optV: S<[(any Sendable)?]>
6266
@preconcurrency var nonOptV: S<[any Sendable]>
6367
@preconcurrency var funcV: S<((any Sendable)) -> Void>
68+
@preconcurrency var funcWithResult: S<() -> (any Sendable)?>
69+
@preconcurrency var anyFunc: S<(Any) -> Any>
6470

6571
var regularV: S<any Sendable>
6672
var regularOptV: S<[(any Sendable)?]>
@@ -69,6 +75,7 @@ struct TestGeneral {
6975
func accepts_any(_: S<Any>) {}
7076
func accepts_opt_any(_: S<[Any?]>) {}
7177
func accepts_func_any(_: S<(Any) -> Void>) {}
78+
func accepts_func_with_any_result(_: S<() -> Any?>) {}
7279

7380
func test_contextual() -> S<Any> {
7481
v // Ok with non-strict concurrency
@@ -129,12 +136,34 @@ struct TestGeneral {
129136
}
130137

131138
func test_no_function_conversions() {
132-
let _: S<(Any) -> Void> = funcV // expected-error {{cannot assign value of type 'S<(any Sendable) -> Void>' to type 'S<(Any) -> Void>'}}
139+
let _: S<(Any) -> Void> = funcV // Ok with non-strict concurrency
140+
// expected-swift6-error@-1 {{cannot assign value of type 'S<(any Sendable) -> Void>' to type 'S<(Any) -> Void>'}}
133141
let _: S<(Any) -> Void> = regularFuncV // expected-error {{cannot assign value of type 'S<(any Sendable) -> Void>' to type 'S<(Any) -> Void>'}}
134142

135-
accepts_func_any(funcV)
136-
// expected-error@-1 {{cannot convert value of type 'S<(any Sendable) -> Void>' to expected argument type 'S<(Any) -> Void>'}}
143+
accepts_func_any(funcV) // Ok with non-strict concurrency
144+
// expected-swift6-error@-1 {{cannot convert value of type 'S<(any Sendable) -> Void>' to expected argument type 'S<(Any) -> Void>'}}
137145
accepts_func_any(regularFuncV)
138146
// expected-error@-1 {{cannot convert value of type 'S<(any Sendable) -> Void>' to expected argument type 'S<(Any) -> Void>'}}
147+
148+
accepts_func_with_any_result(funcWithResult) // Ok with non-strict concurrency
149+
// expected-swift6-error@-1 {{cannot convert value of type 'S<() -> (any Sendable)?>' to expected argument type 'S<() -> Any?>'}}
150+
// expected-swift6-note@-2 {{arguments to generic parameter 'Wrapped' ('any Sendable' and 'Any') are expected to be equal}}
151+
152+
func sameType<T>(_: S<T>, _: T.Type) {}
153+
154+
// FIXME: This unfortunately cannot be supported at the momment because it would require delaying bindings to any generic parameter
155+
// that has `any Sendable` in some position which has performance implications.
156+
sameType(funcV, S<(Any) -> Void>.self)
157+
// expected-error@-1 {{cannot convert value of type 'S<(Any) -> Void>.Type' to expected argument type '((any Sendable) -> Void).Type'}}
158+
sameType(funcWithResult, S<() -> (any Sendable)?>.self)
159+
// expected-error@-1 {{cannot convert value of type 'S<() -> (any Sendable)?>.Type' to expected argument type '(() -> (any Sendable)?).Type'}}
160+
161+
// Make sure that we don't allow Any -> any Sendable
162+
let _: S<(any Sendable) -> Any> = anyFunc
163+
// expected-error@-1 {{cannot assign value of type 'S<(Any) -> Any>' to type 'S<(any Sendable) -> Any>'}}
164+
let _: S<(Any) -> any Sendable> = anyFunc
165+
// expected-error@-1 {{cannot assign value of type 'S<(Any) -> Any>' to type 'S<(Any) -> any Sendable>'}}
166+
let _: S<(any Sendable) -> any Sendable> = anyFunc
167+
// expected-error@-1 {{cannot assign value of type 'S<(Any) -> Any>' to type 'S<(any Sendable) -> any Sendable>'}}
139168
}
140169
}

test/Interpreter/sendable_erasure_to_any_in_preconcurrency.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct S<T> {
2424

2525
struct Test {
2626
@preconcurrency var data: S<any Sendable>
27+
@preconcurrency var funcRef: S<([any Sendable]) -> (any Sendable)?> = S(v: { $0.first })
2728
}
2829

2930

@@ -53,6 +54,13 @@ func test() {
5354
// CHECK: 42
5455
print(sameType(v2.data, with: Any.self))
5556
// CHECK: ultimate question
57+
58+
func expectsFuncAny(_ s: S<([Any]) -> Any?>) {
59+
print(s.v([42]) ?? 0)
60+
}
61+
62+
expectsFuncAny(v1.funcRef)
63+
// CHECK: 42
5664
}
5765

5866
test()

test/SILGen/sendable_to_any_for_generic_arguments.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extension S where T == Any {
6262
struct TestGeneral {
6363
@preconcurrency var v: S<any Sendable>
6464
@preconcurrency var optV: S<[(any Sendable)?]>
65+
@preconcurrency var funcV: S<([any Sendable]) -> (any Sendable)?>
6566

6667
func accepts_any(_: S<Any>) {}
6768
func accepts_opt_any(_: S<[Any?]>) {}
@@ -154,4 +155,30 @@ struct TestGeneral {
154155
let test = Test(data: V(v: 42))
155156
accepts_any(test.data)
156157
}
158+
159+
// CHECK-LABEL: sil hidden [ossa] @$s37sendable_to_any_for_generic_arguments11TestGeneralV023test_function_types_as_E5_argsyyF
160+
// CHECK: [[FUNCV_REF:%.*]] = struct_extract %0, #TestGeneral.funcV
161+
// CHECK-NEXT: %3 = unchecked_trivial_bit_cast [[FUNCV_REF]] to $S<(Array<Any>) -> Optional<Any>>
162+
// CHECK: [[DATA_REF:%.*]] = struct_extract %20, #<abstract function>Test.data
163+
// CHECK-NEXT: [[DATA_COPY:%.*]] = copy_value [[DATA_REF]]
164+
// CHECK-NEXT: [[DATA_ANY:%.*]] = unchecked_value_cast [[DATA_COPY]] to $V<(Array<Any>) -> Any>
165+
// CHECK: [[ACCEPTS_ANY:%.*]] = function_ref @$s37sendable_to_any_for_generic_arguments11TestGeneralV023test_function_types_as_E5_argsyyF08accepts_C0L_yyAcDyyF1VL_VyypSayypGcGF : $@convention(thin) (@guaranteed V<(Array<Any>) -> Any>) -> ()
166+
// CHECK-NEXT: {{.*}} = apply [[ACCEPTS_ANY]]([[DATA_ANY]]) : $@convention(thin) (@guaranteed V<(Array<Any>) -> Any>) -> ()
167+
func test_function_types_as_generic_args() {
168+
let _: S<([Any]) -> Any?> = funcV
169+
170+
struct V<T> {
171+
let v: T
172+
}
173+
174+
struct Test {
175+
@preconcurrency var data: V<([any Sendable]) -> any Sendable>
176+
}
177+
178+
179+
func accepts_any(_: V<([Any]) -> Any>) {}
180+
181+
let test = Test(data: V(v: { $0.first! }))
182+
accepts_any(test.data)
183+
}
157184
}

0 commit comments

Comments
 (0)