Skip to content

Commit e892448

Browse files
authored
Merge pull request #34526 from meg-gupta/asyncverify
SILVerifier: async functions can be called from async functions only
2 parents e29c19c + 288ef58 commit e892448

File tree

5 files changed

+199
-10
lines changed

5 files changed

+199
-10
lines changed

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ static llvm::cl::opt<bool> AbortOnFailure(
5858
"verify-abort-on-failure",
5959
llvm::cl::init(true));
6060

61+
static llvm::cl::opt<bool> ContinueOnFailure("verify-continue-on-failure",
62+
llvm::cl::init(false));
63+
6164
static llvm::cl::opt<bool> VerifyDIHoles(
6265
"verify-di-holes",
6366
llvm::cl::init(true));
@@ -682,9 +685,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
682685
const std::function<void()> &extraContext = nullptr) {
683686
if (condition) return;
684687

685-
llvm::dbgs() << "SIL verification failed: " << complaint << "\n";
688+
StringRef funcName;
689+
if (CurInstruction)
690+
funcName = CurInstruction->getFunction()->getName();
691+
else if (CurArgument)
692+
funcName = CurArgument->getFunction()->getName();
693+
if (ContinueOnFailure) {
694+
llvm::dbgs() << "Begin Error in function " << funcName << "\n";
695+
}
686696

687-
if (extraContext) extraContext();
697+
llvm::dbgs() << "SIL verification failed: " << complaint << "\n";
698+
if (extraContext)
699+
extraContext();
688700

689701
if (CurInstruction) {
690702
llvm::dbgs() << "Verifying instruction:\n";
@@ -693,6 +705,11 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
693705
llvm::dbgs() << "Verifying argument:\n";
694706
CurArgument->printInContext(llvm::dbgs());
695707
}
708+
if (ContinueOnFailure) {
709+
llvm::dbgs() << "End Error in function " << funcName << "\n";
710+
return;
711+
}
712+
696713
llvm::dbgs() << "In function:\n";
697714
F.print(llvm::dbgs());
698715
llvm::dbgs() << "In module:\n";
@@ -1460,6 +1477,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14601477

14611478
require(!calleeConv.funcTy->isCoroutine(),
14621479
"cannot call coroutine with normal apply");
1480+
require(!calleeConv.funcTy->isAsync() || AI->getFunction()->isAsync(),
1481+
"cannot call an async function from a non async function");
14631482

14641483
// Check that if the apply is of a noreturn callee, make sure that an
14651484
// unreachable is the next instruction.
@@ -1478,6 +1497,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14781497
require(!calleeConv.funcTy->isCoroutine(),
14791498
"cannot call coroutine with normal apply");
14801499

1500+
require(!calleeConv.funcTy->isAsync() || AI->getFunction()->isAsync(),
1501+
"cannot call an async function from a non async function");
1502+
14811503
auto normalBB = AI->getNormalBB();
14821504
require(normalBB->args_size() == 1,
14831505
"normal destination of try_apply must take one argument");
@@ -1522,6 +1544,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
15221544

15231545
require(calleeConv.funcTy->getCoroutineKind() == SILCoroutineKind::YieldOnce,
15241546
"must call yield_once coroutine with begin_apply");
1547+
require(!calleeConv.funcTy->isAsync() || AI->getFunction()->isAsync(),
1548+
"cannot call an async function from a non async function");
15251549
}
15261550

15271551
void checkAbortApplyInst(AbortApplyInst *AI) {
@@ -1571,6 +1595,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
15711595
verifySILFunctionType(resultInfo);
15721596
require(resultInfo->getExtInfo().hasContext(),
15731597
"result of closure cannot have a thin function type");
1598+
require(!resultInfo->isAsync() || PAI->getFunction()->isAsync(),
1599+
"cannot call an async function from a non async function");
15741600

15751601
checkApplyTypeDependentArguments(PAI);
15761602

@@ -1653,7 +1679,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
16531679
"function");
16541680
}
16551681
}
1656-
1682+
16571683
// TODO: Impose additional constraints when partial_apply when the
16581684
// -disable-sil-partial-apply flag is enabled. We want to reduce
16591685
// partial_apply to being only a means of associating a closure invocation
@@ -1719,6 +1745,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
17191745
"result of function_ref");
17201746
require(!fnType->getExtInfo().hasContext(),
17211747
"function_ref should have a context-free function result");
1748+
require(!fnType->isAsync() || FRI->getFunction()->isAsync(),
1749+
"cannot call an async function from a non-async function");
17221750

17231751
// Note: in SingleFunction mode, we relax some of these checks because
17241752
// we may not have linked everything yet.
@@ -2893,6 +2921,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
28932921
void checkWitnessMethodInst(WitnessMethodInst *AMI) {
28942922
auto methodType = requireObjectType(SILFunctionType, AMI,
28952923
"result of witness_method");
2924+
require(!methodType->isAsync() || AMI->getFunction()->isAsync(),
2925+
"cannot call an async function from a non-async function");
28962926

28972927
auto *protocol
28982928
= dyn_cast<ProtocolDecl>(AMI->getMember().getDecl()->getDeclContext());
@@ -3048,6 +3078,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
30483078
"result of class_method");
30493079
require(!methodType->getExtInfo().hasContext(),
30503080
"result method must be of a context-free function type");
3081+
require(!methodType->isAsync() || CMI->getFunction()->isAsync(),
3082+
"cannot call an async function from a non-async function");
3083+
30513084
SILType operandType = CMI->getOperand()->getType();
30523085
require(operandType.isClassOrClassMetatype(),
30533086
"operand must be of a class type");
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xllvm -verify-continue-on-failure=true -parse-sil %s -emit-sil -I %t -L %t -o /dev/null 2>&1 | %FileCheck %s
2+
// REQUIRES: asserts
3+
// REQUIRES: concurrency
4+
5+
import Swift
6+
import Builtin
7+
import _Concurrency
8+
9+
sil @create_error : $@convention(thin) () -> @owned Error
10+
11+
sil [ossa] @asynccallee1 : $@async @convention(thin) () -> () {
12+
bb0:
13+
%result = tuple()
14+
return %result : $()
15+
}
16+
17+
sil [ossa] @asynccallee2 : $@async @convention(thin) () -> ((), @error Error) {
18+
bb0:
19+
cond_br undef, bb1, bb2
20+
bb1:
21+
%result = tuple()
22+
return %result : $()
23+
bb2:
24+
%0 = function_ref @create_error : $@convention(thin) () -> @owned Error
25+
%1 = apply %0() : $@convention(thin) () -> @owned Error
26+
throw %1 : $Error
27+
}
28+
29+
// CHECK-NOT: Function: 'async_apply_caller'
30+
sil [ossa] @async_apply_caller : $@async @convention(thin) () -> () {
31+
bb0:
32+
%call1 = function_ref @asynccallee1 : $@async @convention(thin) () -> ()
33+
%callres1 = apply %call1() : $@async @convention(thin) () -> ()
34+
%result = tuple()
35+
return %result : $()
36+
}
37+
38+
// CHECK-LABEL: Begin Error in function nonasync_apply_caller
39+
// CHECK: SIL verification failed: cannot call an async function from a non-async function: !fnType->isAsync() || FRI->getFunction()->isAsync()
40+
// CHECK: Verifying instruction:
41+
// CHECK: -> // function_ref asynccallee1
42+
// CHECK: %0 = function_ref @asynccallee1 : $@convention(thin) @async () -> () // user: %1
43+
// CHECK: %1 = apply %0() : $@convention(thin) @async () -> ()
44+
// CHECK-LABEL: End Error in function nonasync_apply_caller
45+
sil [ossa] @nonasync_apply_caller : $@convention(thin) () -> () {
46+
bb0:
47+
%call = function_ref @asynccallee1 : $@async @convention(thin) () -> ()
48+
%callres = apply %call() : $@async @convention(thin) () -> ()
49+
%result = tuple()
50+
return %result : $()
51+
}
52+
53+
// CHECK-NOT: Function: 'async_try_apply_caller'
54+
sil [ossa] @async_try_apply_caller : $@async @convention(thin) () -> () {
55+
bb0:
56+
%call1 = function_ref @asynccallee2 : $@async @convention(thin) () -> ((), @error Error)
57+
try_apply %call1() : $@async @convention(thin) () -> ((), @error Error), normal bb1, error bb2
58+
bb1(%result : $()):
59+
return %result : $()
60+
bb2(%result_error : $Error):
61+
unreachable
62+
}
63+
64+
// CHECK-LABEL: Begin Error in function nonasync_try_apply_caller
65+
// CHECK: SIL verification failed: cannot call an async function from a non-async function: !fnType->isAsync() || FRI->getFunction()->isAsync()
66+
// CHECK: Verifying instruction:
67+
// CHECK: -> // function_ref asynccallee2
68+
// CHECK: %0 = function_ref @asynccallee2 : $@convention(thin) @async () -> ((), @error Error) // user: %1
69+
// CHECK: try_apply %0() : $@convention(thin) @async () -> ((), @error Error), normal bb1, error bb2 // id: %1
70+
// CHECK-LABEL: End Error in function nonasync_try_apply_caller
71+
sil [ossa] @nonasync_try_apply_caller : $@convention(thin) () -> () {
72+
bb0:
73+
%call1 = function_ref @asynccallee2 : $@async @convention(thin) () -> ((), @error Error)
74+
try_apply %call1() : $@async @convention(thin) () -> ((), @error Error), normal bb1, error bb2
75+
bb1(%result : $()):
76+
return %result : $()
77+
bb2(%result_error : $Error):
78+
unreachable
79+
}
80+
81+
// CHECK-NOT: Function: 'async_partial_apply_caller'
82+
sil [ossa] @async_partial_apply_caller : $@async @convention(thin) () -> @owned @async @callee_owned () -> () {
83+
bb0:
84+
%1 = function_ref @asynccallee1 : $@async @convention(thin) () -> ()
85+
%2 = partial_apply %1() : $@async @convention(thin) () -> ()
86+
return %2 : $@async @callee_owned () -> ()
87+
}
88+
89+
// CHECK-LABEL: Begin Error in function nonasync_partial_apply_caller
90+
// CHECK: SIL verification failed: cannot call an async function from a non-async function: !fnType->isAsync() || FRI->getFunction()->isAsync()
91+
// CHECK: Verifying instruction:
92+
// CHECK: -> // function_ref asynccallee1
93+
// CHECK: %0 = function_ref @asynccallee1 : $@convention(thin) @async () -> () // user: %1
94+
// CHECK: %1 = partial_apply %0() : $@convention(thin) @async () -> () // user: %2
95+
// CHECK-LABEL: End Error in function nonasync_partial_apply_caller
96+
sil [ossa] @nonasync_partial_apply_caller : $@convention(thin) () -> @owned @async @callee_owned () -> () {
97+
bb0:
98+
%1 = function_ref @asynccallee1 : $@async @convention(thin) () -> ()
99+
%2 = partial_apply %1() : $@async @convention(thin) () -> ()
100+
return %2 : $@async @callee_owned () -> ()
101+
}
102+
103+
protocol P {
104+
func printMe() async -> Int64
105+
}
106+
107+
// CHECK-NOT: Function: 'async_witness_method_caller'
108+
sil hidden @async_witness_method_caller : $@async @convention(thin) <T where T : P> (@in_guaranteed T) -> Int64 {
109+
bb0(%t : $*T):
110+
%method = witness_method $T, #P.printMe : <Self where Self : P> (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
111+
%result = apply %method<T>(%t) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
112+
return %result : $Int64
113+
}
114+
115+
// CHECK-LABEL: Begin Error in function nonasync_witness_method_caller
116+
// CHECK: SIL verification failed: cannot call an async function from a non-async function: !methodType->isAsync() || AMI->getFunction()->isAsync()
117+
// CHECK: Verifying instruction:
118+
// CHECK: -> %1 = witness_method $T, #P.printMe : <Self where Self : P> (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // user: %2
119+
// CHECK: %2 = apply %1<T>(%0) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // user: %3
120+
// CHECK-LABEL: End Error in function nonasync_witness_method_caller
121+
sil hidden @nonasync_witness_method_caller : $@convention(thin) <T where T : P> (@in_guaranteed T) -> Int64 {
122+
bb0(%t : $*T):
123+
%method = witness_method $T, #P.printMe : <Self where Self : P> (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
124+
%result = apply %method<T>(%t) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
125+
return %result : $Int64
126+
}
127+
128+
class S {
129+
func method(_ int: Int64) async
130+
deinit
131+
init()
132+
}
133+
134+
// CHECK-NOT: Function: 'async_class_method_caller'
135+
sil hidden @async_class_method_caller : $@async @convention(thin) (Int64, S) -> () {
136+
bb0(%int : $Int64, %instance : $S):
137+
%func = class_method %instance : $S, #S.method : (S) -> (Int64) async -> (), $@convention(method) @async (Int64, @guaranteed S) -> ()
138+
%result = apply %func(%int, %instance) : $@convention(method) @async (Int64, @guaranteed S) -> ()
139+
%res = tuple ()
140+
return %res : $()
141+
}
142+
143+
// CHECK-LABEL: Begin Error in function nonasync_class_method_caller
144+
// CHECK: SIL verification failed: cannot call an async function from a non-async function: !methodType->isAsync() || CMI->getFunction()->isAsync()
145+
// CHECK: Verifying instruction:
146+
// CHECK: %1 = argument of bb0 : $S // users: %3, %2
147+
// CHECK: -> %2 = class_method %1 : $S, #S.method : (S) -> (Int64) async -> (), $@convention(method) @async (Int64, @guaranteed S) -> () // user: %3
148+
// CHECK: %3 = apply %2(%0, %1) : $@convention(method) @async (Int64, @guaranteed S) -> ()
149+
// CHECK-LABEL: End Error in function nonasync_class_method_caller
150+
sil hidden @nonasync_class_method_caller : $@convention(thin) (Int64, S) -> () {
151+
bb0(%int : $Int64, %instance : $S):
152+
%func = class_method %instance : $S, #S.method : (S) -> (Int64) async -> (), $@convention(method) @async (Int64, @guaranteed S) -> ()
153+
%result = apply %func(%int, %instance) : $@convention(method) @async (Int64, @guaranteed S) -> ()
154+
%res = tuple ()
155+
return %res : $()
156+
}

test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ sil_witness_table ObserverImpl : Observer module main {
5656
}
5757

5858
// CHECK-LL: define internal swiftcc void @"$sTA"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} {
59-
sil hidden @witness_method : $@convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> () {
59+
sil hidden @witness_method : $@async @convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> () {
6060
bb0(%0 : $*S):
6161
%1 = witness_method $S, #Observable.subscribe : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
6262
%2 = partial_apply %1<S, O>(%0) : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
@@ -71,8 +71,8 @@ bb0(%argc : $Int32, %argv : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<
7171
strong_retain %observableImpl : $ObservableImpl
7272
%observableImpl_addr = alloc_stack $ObservableImpl
7373
store %observableImpl to %observableImpl_addr : $*ObservableImpl
74-
%witness_method = function_ref @witness_method : $@convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> ()
75-
%partiallyApplied = apply %witness_method<ObservableImpl, ObserverImpl>(%observableImpl_addr) : $@convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> ()
74+
%witness_method = function_ref @witness_method : $@async @convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> ()
75+
%partiallyApplied = apply %witness_method<ObservableImpl, ObserverImpl>(%observableImpl_addr) : $@async @convention(thin) <S where S : Observable><O where O : Observer, S.Result == O.Result> (@in S) -> @owned @async @callee_owned (@in O) -> ()
7676
%observerImpl = alloc_ref $ObserverImpl
7777
%observerImpl_addr = alloc_stack $ObserverImpl
7878
store %observerImpl to %observerImpl_addr : $*ObserverImpl

test/IRGen/async/run-partialapply-capture-int64-to-generic.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import _Concurrency
1919
sil public_external @printGeneric : $@convention(thin) <T> (@in_guaranteed T) -> ()
2020
sil public_external @printInt32 : $@convention(thin) (Int32) -> ()
2121

22-
sil @partial_apply_dynamic_with_out_param : $@convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T {
22+
sil @partial_apply_dynamic_with_out_param : $@async @convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T {
2323
bb0(%x : $Int32, %f : $@async @callee_owned (Int32) -> @out T):
2424
%p = partial_apply %f(%x) : $@async @callee_owned (Int32) -> @out T
2525
return %p : $@async @callee_owned () -> @out T
@@ -45,10 +45,10 @@ bb0(%argc : $Int32, %argv : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<
4545
%first_addr = alloc_stack $Int32
4646
store %first to %first_addr : $*Int32
4747
%callee1 = partial_apply %callee<Int32>(%first_addr) : $@async @convention(thin) <T> (Int32, @in_guaranteed T) -> @out T
48-
%partialApplier = function_ref @partial_apply_dynamic_with_out_param : $@convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T
48+
%partialApplier = function_ref @partial_apply_dynamic_with_out_param : $@async @convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T
4949
%second_literal = integer_literal $Builtin.Int32, 6789
5050
%second = struct $Int32 (%second_literal : $Builtin.Int32)
51-
%callee2 = apply %partialApplier<Int32>(%second, %callee1) : $@convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T
51+
%callee2 = apply %partialApplier<Int32>(%second, %callee1) : $@async @convention(thin) <T> (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T
5252

5353
%result_addr = alloc_stack $Int32
5454
%result = apply %callee2(%result_addr) : $@async @callee_owned () -> @out Int32

test/SIL/Serialization/async.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Builtin
88
import Swift
99
import _Concurrency
1010

11-
sil [serialized] @aaa : $@convention(thin) () -> () {
11+
sil [serialized] @aaa : $@async @convention(thin) () -> () {
1212
entry:
1313
%a = function_ref @async_continuation : $@convention(thin) @async () -> ()
1414
%b = function_ref @async_continuation_throws : $@convention(thin) @async () -> ()

0 commit comments

Comments
 (0)