Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 35 additions & 28 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2290,12 +2290,6 @@ static bool safeToDropGlobalActor(
if (otherIsolation.isErased())
return true;

// We currently allow unconditional dropping of global actors from
// async function types, despite this confusing Sendable checking
// in light of SE-338.
if (funcTy->isAsync())
return true;

// If the argument is passed over an isolation boundary, it's not
// safe to erase actor isolation, because the callee can call the
// function synchronously from outside the isolation domain.
Expand Down Expand Up @@ -2645,7 +2639,8 @@ namespace {
/// be correct AND the solver doesn't know, so we must emit a diagnostic.
void checkFunctionConversion(Expr *funcConv, Type fromType, Type toType) {
auto diagnoseNonSendableParametersAndResult =
[&](FunctionType *fnType, bool downgradeToWarning = false) {
[&](FunctionType *fnType,
std::optional<unsigned> warnUntilSwiftMode = std::nullopt) {
auto *dc = getDeclContext();
llvm::SmallPtrSet<Type, 2> nonSendableTypes;

Expand Down Expand Up @@ -2675,8 +2670,8 @@ namespace {
diag::invalid_function_conversion_with_non_sendable,
fromType, toType);

if (downgradeToWarning)
diag.warnUntilFutureSwiftVersion();
if (warnUntilSwiftMode)
diag.warnUntilSwiftVersion(*warnUntilSwiftMode);
}

for (auto type : nonSendableTypes) {
Expand All @@ -2693,20 +2688,27 @@ namespace {
auto toIsolation = toFnType->getIsolation();

if (auto fromActor = fromFnType->getGlobalActor()) {
if (toFnType->hasGlobalActor() ||
(toFnType->isAsync() && !toIsolation.isNonIsolatedCaller()))
return;
if (!toFnType->hasGlobalActor()) {
auto dc = const_cast<DeclContext *>(getDeclContext());
// If it's unsafe to drop global actor attribute:
// - for Sendable types we are going to perform Sendability
// checking of parameters/result.
// - for non-Sendable types we either leave it to region-based
// isolation to determine whether it's okay or not or
// diagnose if types are not-async.
if (safeToDropGlobalActor(dc, fromActor, toType,
getImmediateApply())) {
return;
}

auto dc = const_cast<DeclContext*>(getDeclContext());
if (!safeToDropGlobalActor(dc, fromActor, toType,
getImmediateApply())) {
// otherwise, it's not a safe cast.
ctx.Diags
.diagnose(funcConv->getLoc(),
diag::converting_func_loses_global_actor, fromType,
toType, fromActor)
.warnUntilSwiftVersion(6);
return;
if (!toFnType->isAsync()) {
ctx.Diags
.diagnose(funcConv->getLoc(),
diag::converting_func_loses_global_actor,
fromType, toType, fromActor)
.warnUntilSwiftVersion(6);
return;
}
}
}

Expand Down Expand Up @@ -2772,19 +2774,21 @@ namespace {
case FunctionTypeIsolation::Kind::NonIsolatedCaller:
case FunctionTypeIsolation::Kind::Erased:
diagnoseNonSendableParametersAndResult(
toFnType, /*downgradeToWarning=*/true);
toFnType, version::Version::getFutureMajorLanguageVersion());
break;

case FunctionTypeIsolation::Kind::GlobalActor: {
// Handled above by `safeToDropGlobalActor` check.
diagnoseNonSendableParametersAndResult(toFnType,
/*warnUntilSwiftMode*/ 6);
break;
}

case FunctionTypeIsolation::Kind::NonIsolated: {
// nonisolated synchronous <-> @concurrent
if (fromFnType->isAsync() != toFnType->isAsync()) {
diagnoseNonSendableParametersAndResult(
toFnType, /*downgradeToWarning=*/true);
toFnType,
version::Version::getFutureMajorLanguageVersion());
}
break;
}
Expand All @@ -2799,7 +2803,7 @@ namespace {
case FunctionTypeIsolation::Kind::Parameter:
case FunctionTypeIsolation::Kind::Erased:
diagnoseNonSendableParametersAndResult(
toFnType, /*downgradeToWarning=*/true);
toFnType, version::Version::getFutureMajorLanguageVersion());
break;

case FunctionTypeIsolation::Kind::NonIsolated: {
Expand All @@ -2809,7 +2813,8 @@ namespace {
// actor isolation.
if (fromFnType->isAsync()) {
diagnoseNonSendableParametersAndResult(
toFnType, /*downgradeToWarning=*/true);
toFnType,
version::Version::getFutureMajorLanguageVersion());
break;
}
// Runs on the actor.
Expand All @@ -2821,7 +2826,9 @@ namespace {
break;

case FunctionTypeIsolation::Kind::GlobalActor:
llvm_unreachable("invalid conversion");
diagnoseNonSendableParametersAndResult(
toFnType, version::Version::getFutureMajorLanguageVersion());
break;
}
break;
}
Expand Down
5 changes: 5 additions & 0 deletions test/Concurrency/attr_execution/conversions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ func testNonSendableDiagnostics(
let _: nonisolated(nonsending) () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
// expected-error@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}

let _: @concurrent (NonSendable) async -> Void = globalActor1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
// expected-warning@-1 {{cannot convert '@MainActor @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
let _: @concurrent () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
// expected-warning@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}

let _: nonisolated(nonsending) (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
// expected-error@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
let _: nonisolated(nonsending) () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
Expand Down
14 changes: 4 additions & 10 deletions test/Concurrency/attr_execution/conversions_silgen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,8 @@ func globalActorConversions3(_ x: @escaping @concurrent (SendableKlass) async ->
_ = await v5(SendableKlass())
}

// CHECK-LABEL: sil hidden [ossa] @$s21attr_execution_silgen26conversionsFromSyncToAsyncyyyAA16NonSendableKlassCYbc_yAA0jK0CYbScMYccyADYbScMYcctYaF : $@convention(thin) @async (@guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> (), @guaranteed @Sendable @callee_guaranteed (@guaranteed SendableKlass) -> (), @guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> ()) -> () {
// CHECK: bb0([[X:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> (), [[Y:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed SendableKlass) -> (), [[Z:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> ()):
// CHECK-LABEL: sil hidden [ossa] @$s21attr_execution_silgen26conversionsFromSyncToAsyncyyyAA16NonSendableKlassCYbc_yAA0jK0CYbScMYcctYaF : $@convention(thin) @async (@guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> (), @guaranteed @Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()) -> ()
// CHECK: bb0([[X:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> (), [[Y:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()):

// CHECK: [[X_C:%.*]] = copy_value [[X]]
// CHECK: [[THUNK:%.*]] = function_ref @$s21attr_execution_silgen16NonSendableKlassCIeghg_ScA_pSgACIegHgg_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @guaranteed NonSendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> ()) -> ()
Expand All @@ -411,19 +411,13 @@ func globalActorConversions3(_ x: @escaping @concurrent (SendableKlass) async ->
// CHECK: [[THUNK:%.*]] = function_ref @$s21attr_execution_silgen13SendableKlassCIeghg_ACIegHg_TRScMTU : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()) -> ()
// CHECK: [[PAI:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[Y_C]])

// CHECK: [[Z_C:%.*]] = copy_value [[Z]]
// CHECK: [[THUNK:%.*]] = function_ref @$s21attr_execution_silgen16NonSendableKlassCIeghg_ACIegHg_TRScMTU : $@convention(thin) @async (@guaranteed NonSendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> ()) -> ()
// CHECK: [[PAI:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[Z_C]])

// CHECK: } // end sil function '$s21attr_execution_silgen26conversionsFromSyncToAsyncyyyAA16NonSendableKlassCYbc_yAA0jK0CYbScMYccyADYbScMYcctYaF'
// CHECK: } // end sil function '$s21attr_execution_silgen26conversionsFromSyncToAsyncyyyAA16NonSendableKlassCYbc_yAA0jK0CYbScMYcctYaF'

func conversionsFromSyncToAsync(_ x: @escaping @Sendable (NonSendableKlass) -> Void,
_ y: @escaping @MainActor @Sendable (SendableKlass) -> Void,
_ z: @escaping @MainActor @Sendable (NonSendableKlass) -> Void) async {
_ y: @escaping @MainActor @Sendable (SendableKlass) -> Void) async {
let _: nonisolated(nonsending) (NonSendableKlass) async -> Void = x
let _: nonisolated(nonsending) (SendableKlass) async -> Void = y
let _: @concurrent (SendableKlass) async -> Void = y
let _: @concurrent (NonSendableKlass) async -> Void = z
}

func testThatClosuresAssumeIsolation(fn: inout nonisolated(nonsending) (Int) async -> Void) {
Expand Down
15 changes: 15 additions & 0 deletions test/Concurrency/global_actor_function_types_swift6.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -language-mode 6

final class NonSendable {
}

@available(*, unavailable)
extension NonSendable: Sendable {}

actor Test {
func testNonSendableCrossingIsolationinAsync(v: NonSendable) {
let _: () async -> NonSendable = { @MainActor in v }
// expected-error@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
// expected-note@-2 {{type 'NonSendable' does not conform to 'Sendable' protocol}}
}
}