Skip to content

Commit e2e111d

Browse files
xedinktoso
authored andcommitted
[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 299bd80 commit e2e111d

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
@@ -732,6 +732,12 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(_local, KnownToBeLocal,
732732
APIBreakingToAdd | APIBreakingToRemove,
733733
130)
734734

735+
SIMPLE_DECL_ATTR(_distributed_thunk, DistributedThunk,
736+
OnFunc | UserInaccessible |
737+
ABIBreakingToAdd | ABIBreakingToRemove |
738+
APIBreakingToAdd | APIBreakingToRemove,
739+
131)
740+
735741
// If you're adding a new underscored attribute here, please document it in
736742
// docs/ReferenceGuides/UnderscoredAttributes.md.
737743

include/swift/AST/Decl.h

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

6422+
/// Is this a thunk function used to access a distributed method outside
6423+
/// of its actor isolation context?
6424+
bool isDistributedThunk() const;
6425+
64226426
/// For a 'distributed' target (func or computed property),
64236427
/// get the 'thunk' responsible for performing the 'remoteCall'.
64246428
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4633,6 +4633,9 @@ ERROR(distributed_actor_isolated_method,none,
46334633
ERROR(distributed_local_cannot_be_used,none,
46344634
"'local' cannot be used in user-defined code currently",
46354635
())
4636+
ERROR(distributed_thunk_cannot_be_used,none,
4637+
"'distributed_thunk' cannot be used in user-defined code",
4638+
())
46364639
ERROR(distributed_actor_func_param_not_codable,none,
46374640
"parameter '%0' of type %1 in %2 does not conform to serialization requirement '%3'",
46384641
(StringRef, Type, DescriptiveDeclKind, StringRef))

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,9 @@ namespace {
874874
if (D->isDistributed()) {
875875
PrintWithColorRAII(OS, ExprModifierColor) << " distributed";
876876
}
877+
if (D->isDistributedThunk()) {
878+
PrintWithColorRAII(OS, ExprModifierColor) << " distributed-thunk";
879+
}
877880

878881
if (auto fac = D->getForeignAsyncConvention()) {
879882
OS << " foreign_async=";
@@ -1337,6 +1340,10 @@ void ValueDecl::dumpRef(raw_ostream &os) const {
13371340
os << " known-to-be-local";
13381341
}
13391342

1343+
if (getAttrs().hasAttribute<DistributedThunkAttr>()) {
1344+
os << " distributed-thunk";
1345+
}
1346+
13401347
// Print location.
13411348
auto &srcMgr = getASTContext().SourceMgr;
13421349
if (getLoc().isValid()) {

lib/AST/DistributedDecl.cpp

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

1333+
bool AbstractFunctionDecl::isDistributedThunk() const {
1334+
return getAttrs().hasAttribute<DistributedThunkAttr>();
1335+
}
1336+
13331337
ConstructorDecl *
13341338
NominalTypeDecl::getDistributedRemoteCallTargetInitFunction() const {
13351339
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

@@ -7793,14 +7793,14 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
77937793
cs.setType(apply, fnType->getResult());
77947794

77957795
// If this is a call to a distributed method thunk,
7796-
// let's mark the call as implicitly throwing.
7797-
if (isDistributedThunk(callee, apply->getFn())) {
7798-
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
7799-
if (!FD->hasThrows())
7796+
// let's mark the call as implicitly throwing/async.
7797+
if (isa<SelfApplyExpr>(apply->getFn())) {
7798+
auto *FD = dyn_cast<FuncDecl>(callee.getDecl());
7799+
if (FD && FD->isDistributedThunk()) {
78007800
apply->setImplicitlyThrows(true);
7801-
if (!FD->hasAsync())
78027801
apply->setImplicitlyAsync(ImplicitActorHopTarget::forInstanceSelf());
7803-
apply->setUsesDistributedThunk(true);
7802+
apply->setUsesDistributedThunk(true);
7803+
}
78047804
}
78057805

78067806
solution.setExprTypes(apply);
@@ -7919,14 +7919,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
79197919
return ctorCall;
79207920
}
79217921

7922-
bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
7923-
if (!isa<SelfApplyExpr>(context))
7924-
return false;
7925-
7926-
return requiresDistributedThunk(cast<SelfApplyExpr>(context)->getBase(),
7927-
context->getLoc(), ref);
7928-
}
7929-
79307922
bool ExprRewriter::requiresDistributedThunk(Expr *base, SourceLoc memberLoc,
79317923
ConcreteDeclRef memberRef) {
79327924

lib/Sema/CodeSynthesisDistributedActor.cpp

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

704+
thunk->getAttrs().add(new (C) DistributedThunkAttr(/*isImplicit=*/true));
704705
thunk->setGenericSignature(baseSignature);
705706
thunk->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false);
706707
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
@@ -5817,6 +5819,11 @@ void AttributeChecker::visitSendableAttr(SendableAttr *attr) {
58175819
}
58185820
}
58195821

5822+
void AttributeChecker::visitDistributedThunkAttr(DistributedThunkAttr *attr) {
5823+
if (!D->isImplicit())
5824+
diagnoseAndRemoveAttr(attr, diag::distributed_thunk_cannot_be_used);
5825+
}
5826+
58205827
void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
58215828
// 'nonisolated' can be applied to global and static/class variables
58225829
// 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
@@ -2859,8 +2859,16 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28592859
// callee is isolated to an actor.
28602860
auto callee = call->getCalledValue();
28612861
if (callee) {
2862+
auto declKind = callee->getDescriptiveKind();
2863+
if (call->usesDistributedThunk()) {
2864+
// We need to determine whether this is a method or a property.
2865+
// Computed properties always form an implicit call to the thunk.
2866+
declKind = call->isImplicit()
2867+
? DescriptiveDeclKind::DistributedProperty
2868+
: DescriptiveDeclKind::DistributedMethod;
2869+
}
28622870
Ctx.Diags.diagnose(diag.expr.getStartLoc(), diag::actor_isolated_sync_func,
2863-
callee->getDescriptiveKind(), callee->getName());
2871+
declKind, callee->getName());
28642872
} else {
28652873
Ctx.Diags.diagnose(
28662874
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)