Skip to content

Commit 233eccd

Browse files
committed
mark the nearest CallExpr as implicitly async; not just the nearest ApplyExpr.
1 parent 276e1a2 commit 233eccd

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,38 @@ namespace {
621621
contextStack.push_back(dc);
622622
}
623623

624+
/// Searches the applyStack from back to front for the inner-most CallExpr
625+
/// and marks that CallExpr as implicitly async.
626+
///
627+
/// NOTE: Crashes if no CallExpr was found.
628+
///
629+
/// For example, for global actor function `curryAdd`, if we have:
630+
/// ((curryAdd 1) 2)
631+
/// then we want to mark the inner-most CallExpr, `(curryAdd 1)`.
632+
///
633+
/// The same goes for calls to member functions, such as calc.add(1, 2),
634+
/// aka ((add calc) 1 2), looks like this:
635+
///
636+
/// (call_expr
637+
/// (dot_syntax_call_expr
638+
/// (declref_expr add)
639+
/// (declref_expr calc))
640+
/// (tuple_expr
641+
/// ...))
642+
///
643+
/// and we reach up to mark the CallExpr.
644+
void markNearestCallAsImplicitlyAsync() {
645+
assert(applyStack.size() > 0 && "not contained within an Apply?");
646+
647+
const auto End = applyStack.rend();
648+
for (auto I = applyStack.rbegin(); I != End; ++I)
649+
if (auto call = dyn_cast<CallExpr>(*I)) {
650+
call->setImplicitlyAsync(true);
651+
return;
652+
}
653+
llvm_unreachable("expected a CallExpr in applyStack!");
654+
}
655+
624656
bool shouldWalkCaptureInitializerExpressions() override { return true; }
625657

626658
bool shouldWalkIntoTapExpression() override { return false; }
@@ -661,10 +693,9 @@ namespace {
661693
if (auto memberRef = findMemberReference(partialApply->fn)) {
662694
// NOTE: partially-applied thunks are never annotated as
663695
// implicitly async, regardless of whether they are escaping.
664-
// So, we do not pass the ApplyExpr along to checkMemberReference.
665696
checkMemberReference(
666697
partialApply->base, memberRef->first, memberRef->second,
667-
partialApply->isEscaping);
698+
partialApply->isEscaping, /*maybeImplicitAsync=*/false);
668699

669700
partialApply->base->walk(*this);
670701

@@ -683,7 +714,7 @@ namespace {
683714
if (auto memberRef = findMemberReference(fn)) {
684715
checkMemberReference(
685716
call->getArg(), memberRef->first, memberRef->second,
686-
/*isEscapingPartialApply=*/false, call);
717+
/*isEscapingPartialApply=*/false, /*maybeImplicitAsync=*/true);
687718

688719
call->getArg()->walk(*this);
689720

@@ -903,7 +934,7 @@ namespace {
903934
auto concDecl = memberRef->first;
904935
if (value == concDecl.getDecl() && !apply->implicitlyAsync()) {
905936
// then this ValueDecl appears as the called value of the ApplyExpr.
906-
apply->setImplicitlyAsync(true);
937+
markNearestCallAsImplicitlyAsync();
907938
return true;
908939
}
909940
}
@@ -1012,7 +1043,7 @@ namespace {
10121043
bool checkMemberReference(
10131044
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
10141045
bool isEscapingPartialApply = false,
1015-
ApplyExpr *maybeImplicitAsync = nullptr) {
1046+
bool maybeImplicitAsync = false) {
10161047
if (!base || !memberRef)
10171048
return false;
10181049

@@ -1028,7 +1059,7 @@ namespace {
10281059
if (!selfVar) {
10291060
// actor-isolated non-self calls are implicitly async and thus OK.
10301061
if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1031-
maybeImplicitAsync->setImplicitlyAsync(true);
1062+
markNearestCallAsImplicitlyAsync();
10321063
return false;
10331064
}
10341065
ctx.Diags.diagnose(

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,34 @@ func blender(_ peeler : () -> Void) {
184184

185185
@OrangeActor func quinoa() async {
186186
rice() // expected-error {{call is 'async' but is not marked with 'await'}}
187+
}
188+
189+
///////////
190+
// check various curried applications to ensure we mark the right expression.
191+
192+
actor class Calculator {
193+
func addCurried(_ x : Int) -> ((Int) -> Int) {
194+
return { (_ y : Int) in x + y }
195+
}
196+
197+
func add(_ x : Int, _ y : Int) -> Int {
198+
return x + y
199+
}
200+
}
201+
202+
@BananaActor func bananaAdd(_ x : Int) -> ((Int) -> Int) {
203+
return { (_ y : Int) in x + y }
204+
}
205+
206+
@OrangeActor func doSomething() async {
207+
let _ = (await bananaAdd(1))(2)
208+
let _ = await (await bananaAdd(1))(2) // expected-warning{{no calls to 'async' functions occur within 'await' expression}}
209+
210+
let calc = Calculator()
211+
212+
let _ = (await calc.addCurried(1))(2)
213+
let _ = await (await calc.addCurried(1))(2) // expected-warning{{no calls to 'async' functions occur within 'await' expression}}
214+
215+
let plusOne = await calc.addCurried(await calc.add(0, 1))
216+
let _ = plusOne(2)
187217
}

0 commit comments

Comments
 (0)