Skip to content

Ensure that distributed functions with typed throws can be called from outside the actor #79144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 5, 2025
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
3 changes: 0 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5656,9 +5656,6 @@ ERROR(distributed_actor_func_unsupported_specifier, none,
ERROR(distributed_actor_func_variadic, none,
"cannot declare variadic argument %0 in %kind1",
(DeclName, const ValueDecl *))
ERROR(distributed_actor_func_typed_throws, none,
"cannot declare distributed function with typed throws",
())
NOTE(actor_mutable_state,none,
"mutation of this %0 is only permitted within the actor",
(DescriptiveDeclKind))
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8397,8 +8397,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
// let's mark the call as implicitly throwing.
if (isDistributedThunk(callee, apply->getFn())) {
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
if (!FD->hasThrows())
apply->setImplicitlyThrows(true);
apply->setImplicitlyThrows(true);
}

solution.setExprTypes(apply);
Expand Down
8 changes: 2 additions & 6 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3554,18 +3554,14 @@ namespace {
}

return std::make_pair(
/*setThrows=*/!afd->hasThrows(),
/*setThrows=*/true,
/*isDistributedThunk=*/true);
}

if (auto *var = dyn_cast<VarDecl>(decl)) {
if (var->isDistributed()) {
bool explicitlyThrowing = false;
if (auto getter = var->getAccessor(swift::AccessorKind::Get)) {
explicitlyThrowing = getter->hasThrows();
}
return std::make_pair(
/*setThrows*/ !explicitlyThrowing,
/*setThrows*/ true,
/*isDistributedThunk=*/true);
}

Expand Down
13 changes: 0 additions & 13 deletions lib/Sema/TypeCheckDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,19 +618,6 @@ bool CheckDistributedFunctionRequest::evaluate(
return true;
}

// TODO: rdar://136467591 Currently typed throws were not implemented for distributed methods
if (func->hasThrows()) {
if (auto thrownError = func->getEffectiveThrownErrorType()) {
// Basically we only support throwing `any Error` out of a distributed
// function because then the effective error thrown by thunk calls naturally
// is correct and the same `any Error`
if (thrownError.has_value() &&
!(*thrownError)->isEqual(C.getErrorExistentialType())) {
func->diagnose(diag::distributed_actor_func_typed_throws);
}
}
}

return false;
}

Expand Down
75 changes: 75 additions & 0 deletions test/Distributed/distributed_actor_typed_throws.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// RUN: %target-typecheck-verify-swift
// RUN: %target-swift-frontend -emit-sil -DMAKE_CORRECT %s -o - | %FileCheck %s

// UNSUPPORTED: back_deploy_concurrency
// REQUIRES: concurrency
// REQUIRES: distributed

import Distributed

typealias DefaultDistributedActorSystem = LocalTestingDistributedActorSystem

distributed actor Foo {
distributed func alwaysThrows() throws(FooError) { }

func alwaysPropagates() throws(FooError) {
// okay, produces FooError
try alwaysThrows()
_ = try value
}

distributed var value: String {
get throws(FooError) {
throw FooError()
}
}
}

struct FooError: Codable, Error { }
struct RemoteInvocationError: Codable, Error { }

#if !MAKE_CORRECT
func testBad(foo: Foo) async throws(FooError) {
try await foo.alwaysThrows() // expected-error{{thrown expression type 'any Error' cannot be converted to error type 'FooError'}}

_ = try await foo.value // expected-error{{thrown expression type 'any Error' cannot be converted to error type 'FooError'}}
}

func testBadDoCatch(foo: Foo) async throws {
do {
try await foo.alwaysThrows()
_ = try await foo.value
} catch let error {
let _: Int = error // expected-error{{cannot convert value of type 'any Error' to specified type 'Int'}}
}
}
#endif

// Distributed thunk for calling alwaysThrows() handles the translation.
// CHECK-LABEL: sil hidden [thunk] [distributed] [ref_adhoc_requirement_witness "$s11Distributed29LocalTestingInvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"] @$s30distributed_actor_typed_throws3FooC12alwaysThrowsyyYaKFTE
// CHECK: [[LOCAL_FN:%.*]] = function_ref @$s30distributed_actor_typed_throws3FooC12alwaysThrowsyyAA0E5ErrorVYKF : $@convention(method) (@sil_isolated @guaranteed Foo) -> @error FooError
// CHECK-NEXT: hop_to_executor [[FOO:%[0-9]+]]
// CHECK-NEXT: try_apply [[LOCAL_FN]]([[FOO]]) : $@convention(method) (@sil_isolated @guaranteed Foo) -> @error FooError, normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]]
// CHECK: [[ERROR_BB]]([[FOO_ERROR:%.*]] : $FooError):
// CHECK: alloc_existential_box $any Error, $FooError
// CHECK-NEXT: project_existential_box $FooError
// CHECK: store [[FOO_ERROR]]

// CHECK-LABEL: sil hidden @$s30distributed_actor_typed_throws8testGood3fooyAA3FooC_tYaKF : $@convention(thin) @async (@guaranteed Foo) -> @error any Error
func testGood(foo: Foo) async throws {
// CHECK: function_ref @$s30distributed_actor_typed_throws3FooC12alwaysThrowsyyYaKFTE : $@convention(method) @async (@guaranteed Foo) -> @error any Error
try await foo.alwaysThrows()

_ = try await foo.value
}

func testDoCatch(foo: Foo) async throws(FooError) {
do {
try await foo.alwaysThrows()
_ = try await foo.value
} catch let error {
if let fe = error as? FooError {
throw fe
}
}
}

This file was deleted.