Skip to content

Commit fa2e64c

Browse files
committed
[Distributed] Sema: Add a new distributed-thunk attribute
The attribute comes handy during solution application to determine whether the call is using a distributed thunk.
1 parent 2078aa9 commit fa2e64c

11 files changed

+60
-31
lines changed

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,12 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(_local, KnownToBeLocal,
735735
APIBreakingToAdd | APIBreakingToRemove,
736736
130)
737737

738+
SIMPLE_DECL_ATTR(_distributed_thunk, DistributedThunk,
739+
OnFunc | UserInaccessible |
740+
ABIBreakingToAdd | ABIBreakingToRemove |
741+
APIBreakingToAdd | APIBreakingToRemove,
742+
131)
743+
738744
// If you're adding a new underscored attribute here, please document it in
739745
// docs/ReferenceGuides/UnderscoredAttributes.md.
740746

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6436,6 +6436,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
64366436
/// Returns 'true' if the function is distributed.
64376437
bool isDistributed() const;
64386438

6439+
/// Is this a thunk function used to access a distributed method outside
6440+
/// of its actor isolation context?
6441+
bool isDistributedThunk() const;
6442+
64396443
/// For a 'distributed' target (func or computed property),
64406444
/// get the 'thunk' responsible for performing the 'remoteCall'.
64416445
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4604,6 +4604,9 @@ ERROR(distributed_actor_isolated_method,none,
46044604
ERROR(distributed_local_cannot_be_used,none,
46054605
"'local' cannot be used in user-defined code currently",
46064606
())
4607+
ERROR(distributed_thunk_cannot_be_used,none,
4608+
"'distributed_thunk' cannot be used in user-defined code",
4609+
())
46074610
ERROR(distributed_actor_func_param_not_codable,none,
46084611
"parameter '%0' of type %1 in %2 does not conform to serialization requirement '%3'",
46094612
(StringRef, Type, DescriptiveDeclKind, StringRef))

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,9 @@ namespace {
877877
if (D->isDistributed()) {
878878
PrintWithColorRAII(OS, ExprModifierColor) << " distributed";
879879
}
880+
if (D->isDistributedThunk()) {
881+
PrintWithColorRAII(OS, ExprModifierColor) << " distributed-thunk";
882+
}
880883

881884
if (auto fac = D->getForeignAsyncConvention()) {
882885
OS << " foreign_async=";
@@ -1350,6 +1353,10 @@ void ValueDecl::dumpRef(raw_ostream &os) const {
13501353
os << " known-to-be-local";
13511354
}
13521355

1356+
if (getAttrs().hasAttribute<DistributedThunkAttr>()) {
1357+
os << " distributed-thunk";
1358+
}
1359+
13531360
// Print location.
13541361
auto &srcMgr = getASTContext().SourceMgr;
13551362
if (getLoc().isValid()) {

lib/AST/DistributedDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,10 @@ bool AbstractFunctionDecl::isDistributed() const {
12901290
return getAttrs().hasAttribute<DistributedActorAttr>();
12911291
}
12921292

1293+
bool AbstractFunctionDecl::isDistributedThunk() const {
1294+
return getAttrs().hasAttribute<DistributedThunkAttr>();
1295+
}
1296+
12931297
ConstructorDecl *
12941298
NominalTypeDecl::getDistributedRemoteCallTargetInitFunction() const {
12951299
auto mutableThis = const_cast<NominalTypeDecl *>(this);

lib/Sema/CSApply.cpp

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -516,10 +516,6 @@ namespace {
516516
LookUpConformanceInModule(dc->getParentModule()));
517517
}
518518

519-
/// Determine whether the given reference is to a method on
520-
/// a remote distributed actor in the given context.
521-
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
522-
523519
/// Determine whether the given reference on the given
524520
/// base has to be replaced with a distributed thunk instead.
525521
bool requiresDistributedThunk(Expr *base, SourceLoc memberLoc,
@@ -1649,6 +1645,10 @@ namespace {
16491645

16501646
ConcreteDeclRef thunkRef{thunkDecl, memberRef.getSubstitutions()};
16511647

1648+
// Update member reference to point to the thunk.
1649+
CachedConcreteRefs[cs.getConstraintLocator(memberLocator)] =
1650+
thunkRef;
1651+
16521652
auto declRefExpr = new (context) DeclRefExpr(
16531653
thunkRef, memberLoc, Implicit, AccessSemantics::DirectToStorage);
16541654

@@ -7790,14 +7790,14 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
77907790
cs.setType(apply, fnType->getResult());
77917791

77927792
// If this is a call to a distributed method thunk,
7793-
// let's mark the call as implicitly throwing.
7794-
if (isDistributedThunk(callee, apply->getFn())) {
7795-
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
7796-
if (!FD->hasThrows())
7793+
// let's mark the call as implicitly throwing/async.
7794+
if (isa<SelfApplyExpr>(apply->getFn())) {
7795+
auto *FD = dyn_cast<FuncDecl>(callee.getDecl());
7796+
if (FD && FD->isDistributedThunk()) {
77977797
apply->setImplicitlyThrows(true);
7798-
if (!FD->hasAsync())
77997798
apply->setImplicitlyAsync(ImplicitActorHopTarget::forInstanceSelf());
7800-
apply->setUsesDistributedThunk(true);
7799+
apply->setUsesDistributedThunk(true);
7800+
}
78017801
}
78027802

78037803
solution.setExprTypes(apply);
@@ -7916,14 +7916,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
79167916
return ctorCall;
79177917
}
79187918

7919-
bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
7920-
if (!isa<SelfApplyExpr>(context))
7921-
return false;
7922-
7923-
return requiresDistributedThunk(cast<SelfApplyExpr>(context)->getBase(),
7924-
context->getLoc(), ref);
7925-
}
7926-
79277919
bool ExprRewriter::requiresDistributedThunk(Expr *base, SourceLoc memberLoc,
79287920
ConcreteDeclRef memberRef) {
79297921

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) {
736736
if (isa<ClassDecl>(DC))
737737
thunk->getAttrs().add(new (C) FinalAttr(/*isImplicit=*/true));
738738

739+
thunk->getAttrs().add(new (C) DistributedThunkAttr(/*isImplicit=*/true));
739740
thunk->setGenericSignature(baseSignature);
740741
thunk->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false);
741742
thunk->setBodySynthesizer(deriveBodyDistributed_thunk, func);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
322322
void visitKnownToBeLocalAttr(KnownToBeLocalAttr *attr);
323323

324324
void visitSendableAttr(SendableAttr *attr);
325+
326+
void visitDistributedThunkAttr(DistributedThunkAttr *attr);
325327
};
326328

327329
} // end anonymous namespace
@@ -5934,6 +5936,11 @@ void AttributeChecker::visitSendableAttr(SendableAttr *attr) {
59345936
}
59355937
}
59365938

5939+
void AttributeChecker::visitDistributedThunkAttr(DistributedThunkAttr *attr) {
5940+
if (!D->isImplicit())
5941+
diagnoseAndRemoveAttr(attr, diag::distributed_thunk_cannot_be_used);
5942+
}
5943+
59375944
void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
59385945
// 'nonisolated' can be applied to global and static/class variables
59395946
// that do not have storage.

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,7 @@ namespace {
16131613
UNINTERESTING_ATTR(UnsafeInheritExecutor)
16141614
UNINTERESTING_ATTR(CompilerInitialized)
16151615

1616+
UNINTERESTING_ATTR(DistributedThunk)
16161617
#undef UNINTERESTING_ATTR
16171618

16181619
void visitAvailableAttr(AvailableAttr *attr) {

lib/Sema/TypeCheckEffects.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2878,8 +2878,16 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28782878
// callee is isolated to an actor.
28792879
auto callee = call->getCalledValue();
28802880
if (callee) {
2881+
auto declKind = callee->getDescriptiveKind();
2882+
if (call->usesDistributedThunk()) {
2883+
// We need to determine whether this is a method or a property.
2884+
// Computed properties always form an implicit call to the thunk.
2885+
declKind = call->isImplicit()
2886+
? DescriptiveDeclKind::DistributedProperty
2887+
: DescriptiveDeclKind::DistributedMethod;
2888+
}
28812889
Ctx.Diags.diagnose(diag.expr.getStartLoc(), diag::actor_isolated_sync_func,
2882-
callee->getDescriptiveKind(), callee->getName());
2890+
declKind, callee->getName());
28832891
} else {
28842892
Ctx.Diags.diagnose(
28852893
diag.expr.getStartLoc(), diag::actor_isolated_sync_func_value,

test/Distributed/distributed_actor_func_implicitly_async_throws.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ func test_not_distributed_funcs(distributed: D) async {
3737
func test_outside(distributed: D) async throws {
3838
distributed.distHello() // expected-error{{expression is 'async' but is not marked with 'await'}}
3939
// expected-error@-1{{call can throw but is not marked with 'try'}}
40-
// expected-note@-2{{calls to instance method 'distHello()' from outside of its actor context are implicitly asynchronous}}
40+
// expected-note@-2{{calls to distributed instance method 'distHello()' from outside of its actor context are implicitly asynchronous}}
4141
// expected-note@-3{{did you mean to use 'try'?}}
4242
// expected-note@-4{{did you mean to disable error propagation?}}
4343
// expected-note@-5{{did you mean to handle error as optional value?}}
4444
try distributed.distHello() // expected-error{{expression is 'async' but is not marked with 'await'}}
45-
// expected-note@-1{{calls to instance method 'distHello()' from outside of its actor context are implicitly asynchronous}}
45+
// expected-note@-1{{calls to distributed instance method 'distHello()' from outside of its actor context are implicitly asynchronous}}
4646
await distributed.distHello() // expected-error{{call can throw but is not marked with 'try'}}
4747
// expected-note@-1{{did you mean to use 'try'?}}
4848
// expected-note@-2{{did you mean to disable error propagation?}}
@@ -51,14 +51,12 @@ func test_outside(distributed: D) async throws {
5151

5252
distributed.distHelloAsync()// expected-error{{expression is 'async' but is not marked with 'await'}}
5353
// expected-error@-1{{call can throw but is not marked with 'try'}}
54-
// expected-note@-2{{call is 'async'}}
54+
// expected-note@-2{{calls to distributed instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous}}
5555
// expected-note@-3{{did you mean to use 'try'?}}
5656
// expected-note@-4{{did you mean to disable error propagation?}}
5757
// expected-note@-5{{did you mean to handle error as optional value?}}
58-
// FIXME(distributed): we lost this diagnosis in recent rework: calls to distributed instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous
5958
try distributed.distHelloAsync() // expected-error{{expression is 'async' but is not marked with 'await'}}
60-
// expected-note@-1{{call is 'async'}}
61-
// FIXME(distributed): we lost this diagnosis in recent rework: calls to instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous
59+
// expected-note@-1{{calls to distributed instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous}}
6260
await distributed.distHelloAsync() // expected-error{{call can throw but is not marked with 'try'}}
6361
// expected-note@-1{{did you mean to use 'try'?}}
6462
// expected-note@-2{{did you mean to disable error propagation?}}
@@ -67,12 +65,12 @@ func test_outside(distributed: D) async throws {
6765

6866
distributed.distHelloThrows() // expected-error{{expression is 'async' but is not marked with 'await'}}
6967
// expected-error@-1{{call can throw but is not marked with 'try'}}
70-
// expected-note@-2{{calls to instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}}
68+
// expected-note@-2{{calls to distributed instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}}
7169
// expected-note@-3{{did you mean to use 'try'?}}
7270
// expected-note@-4{{did you mean to disable error propagation?}}
7371
// expected-note@-5{{did you mean to handle error as optional value?}}
7472
try distributed.distHelloThrows() // expected-error{{expression is 'async' but is not marked with 'await'}}
75-
// expected-note@-1{{calls to instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}}
73+
// expected-note@-1{{calls to distributed instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}}
7674
await distributed.distHelloThrows() // expected-error{{call can throw but is not marked with 'try'}}
7775
// expected-note@-1{{did you mean to use 'try'?}}
7876
// expected-note@-2{{did you mean to disable error propagation?}}
@@ -81,14 +79,12 @@ func test_outside(distributed: D) async throws {
8179

8280
distributed.distHelloAsyncThrows() // expected-error{{expression is 'async' but is not marked with 'await'}}
8381
// expected-error@-1{{call can throw but is not marked with 'try'}}
84-
// expected-note@-2{{call is 'async'}}
82+
// expected-note@-2{{calls to distributed instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous}}
8583
// expected-note@-3{{did you mean to use 'try'?}}
8684
// expected-note@-4{{did you mean to disable error propagation?}}
8785
// expected-note@-5{{did you mean to handle error as optional value?}}
88-
// FIXME(distributed): we lost this diagnosis in recent rework: {{calls to instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous
8986
try distributed.distHelloAsyncThrows() // expected-error{{expression is 'async' but is not marked with 'await'}}
90-
// expected-note@-1{{call is 'async'}}
91-
// FIXME(distributed): we lost this diagnosis in recent rework: calls to instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous
87+
// expected-note@-1{{calls to distributed instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous}}
9288
await distributed.distHelloAsyncThrows() // expected-error{{call can throw but is not marked with 'try'}}
9389
// expected-note@-1{{did you mean to use 'try'?}}
9490
// expected-note@-2{{did you mean to disable error propagation?}}

0 commit comments

Comments
 (0)