@@ -662,7 +662,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
662
662
663
663
// Used for dominance checking within a basic block.
664
664
llvm::DenseMap<const SILInstruction *, unsigned > InstNumbers;
665
-
665
+
666
666
DeadEndBlocks DEBlocks;
667
667
LoadBorrowNeverInvalidatedAnalysis loadBorrowNeverInvalidatedAnalysis;
668
668
bool SingleFunction = true ;
@@ -4748,6 +4748,72 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4748
4748
" Type of witness instruction does not match actual type of "
4749
4749
" witnessed function" );
4750
4750
}
4751
+
4752
+ void checkGetAsyncContinuationInstBase (GetAsyncContinuationInstBase *GACI) {
4753
+ auto resultTy = GACI->getType ();
4754
+ auto &C = resultTy.getASTContext ();
4755
+ auto resultBGT = resultTy.getAs <BoundGenericType>();
4756
+ require (resultBGT, " Instruction type must be a continuation type" );
4757
+ auto resultDecl = resultBGT->getDecl ();
4758
+ require (resultDecl == C.getUnsafeContinuationDecl ()
4759
+ || resultDecl == C.getUnsafeThrowingContinuationDecl (),
4760
+ " Instruction type must be a continuation type" );
4761
+ }
4762
+
4763
+ void checkGetAsyncContinuationInst (GetAsyncContinuationInst *GACI) {
4764
+ checkGetAsyncContinuationInstBase (GACI);
4765
+ }
4766
+
4767
+ void checkGetAsyncContinuationAddrInst (GetAsyncContinuationAddrInst *GACI) {
4768
+ checkGetAsyncContinuationInstBase (GACI);
4769
+
4770
+ requireSameType (GACI->getOperand ()->getType (),
4771
+ GACI->getLoweredResumeType ().getAddressType (),
4772
+ " Operand type must match continuation resume type" );
4773
+ }
4774
+
4775
+ void checkAwaitAsyncContinuationInst (AwaitAsyncContinuationInst *AACI) {
4776
+ // The operand must be a GetAsyncContinuation* instruction.
4777
+ auto cont = dyn_cast<GetAsyncContinuationInstBase>(AACI->getOperand ());
4778
+ require (cont, " can only await the result of a get_async_continuation instruction" );
4779
+ bool isAddressForm = isa<GetAsyncContinuationAddrInst>(cont);
4780
+
4781
+ auto &C = cont->getType ().getASTContext ();
4782
+
4783
+ // The shape of the successors depends on the continuation instruction being
4784
+ // awaited.
4785
+ require ((bool )AACI->getErrorBB () == cont->throws (),
4786
+ " must have an error successor if and only if the continuation is throwing" );
4787
+ if (cont->throws ()) {
4788
+ require (AACI->getErrorBB ()->getNumArguments () == 1 ,
4789
+ " error successor must take one argument" );
4790
+ auto arg = AACI->getErrorBB ()->getArgument (0 );
4791
+ auto errorType = C.getErrorDecl ()->getDeclaredType ()->getCanonicalType ();
4792
+ requireSameType (arg->getType (),
4793
+ SILType::getPrimitiveObjectType (errorType),
4794
+ " error successor argument must have Error type" );
4795
+
4796
+ if (AACI->getFunction ()->hasOwnership ()) {
4797
+ require (arg->getOwnershipKind () == ValueOwnershipKind::Owned,
4798
+ " error successor argument must be owned" );
4799
+ }
4800
+ }
4801
+ if (isAddressForm) {
4802
+ require (AACI->getResumeBB ()->getNumArguments () == 0 ,
4803
+ " resume successor must take no arguments for get_async_continuation_addr" );
4804
+ } else {
4805
+ require (AACI->getResumeBB ()->getNumArguments () == 1 ,
4806
+ " resume successor must take one argument for get_async_continuation" );
4807
+ auto arg = AACI->getResumeBB ()->getArgument (0 );
4808
+
4809
+ requireSameType (arg->getType (), cont->getLoweredResumeType (),
4810
+ " resume successor must take an argument of the continuation resume type" );
4811
+ if (AACI->getFunction ()->hasOwnership ()) {
4812
+ require (arg->getOwnershipKind () == ValueOwnershipKind::Owned,
4813
+ " resume successor argument must be owned" );
4814
+ }
4815
+ }
4816
+ }
4751
4817
4752
4818
// This verifies that the entry block of a SIL function doesn't have
4753
4819
// any predecessors and also verifies the entry point arguments.
@@ -4902,13 +4968,17 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4902
4968
std::set<SILInstruction*> ActiveOps;
4903
4969
4904
4970
CFGState CFG = Normal;
4971
+
4972
+ GetAsyncContinuationInstBase *GotAsyncContinuation = nullptr ;
4905
4973
};
4906
4974
};
4907
4975
4908
4976
// / Verify the various control-flow-sensitive rules of SIL:
4909
4977
// /
4910
4978
// / - stack allocations and deallocations must obey a stack discipline
4911
4979
// / - accesses must be uniquely ended
4980
+ // / - async continuations must be awaited before getting the continuation again, suspending
4981
+ // / the task, or exiting the function
4912
4982
// / - flow-sensitive states must be equivalent on all paths into a block
4913
4983
void verifyFlowSensitiveRules (SILFunction *F) {
4914
4984
// Do a traversal of the basic blocks.
@@ -4924,6 +4994,20 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4924
4994
for (SILInstruction &i : *BB) {
4925
4995
CurInstruction = &i;
4926
4996
4997
+ if (i.maySuspend ()) {
4998
+ // Instructions that may suspend an async context must not happen
4999
+ // while the continuation is being accessed, with the exception of
5000
+ // the AwaitAsyncContinuationInst that completes suspending the task.
5001
+ if (auto aaci = dyn_cast<AwaitAsyncContinuationInst>(&i)) {
5002
+ require (state.GotAsyncContinuation == aaci->getOperand (),
5003
+ " encountered await_async_continuation that doesn't match active gotten continuation" );
5004
+ state.GotAsyncContinuation = nullptr ;
5005
+ } else {
5006
+ require (!state.GotAsyncContinuation ,
5007
+ " cannot suspend async task while unawaited continuation is active" );
5008
+ }
5009
+ }
5010
+
4927
5011
if (i.isAllocatingStack ()) {
4928
5012
state.Stack .push_back (cast<SingleValueInstruction>(&i));
4929
5013
@@ -4946,13 +5030,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4946
5030
bool present = state.ActiveOps .erase (beginOp);
4947
5031
require (present, " operation has already been ended" );
4948
5032
}
4949
-
5033
+ } else if (auto gaci = dyn_cast<GetAsyncContinuationInstBase>(&i)) {
5034
+ require (!state.GotAsyncContinuation ,
5035
+ " get_async_continuation while unawaited continuation is already active" );
5036
+ state.GotAsyncContinuation = gaci;
4950
5037
} else if (auto term = dyn_cast<TermInst>(&i)) {
4951
5038
if (term->isFunctionExiting ()) {
4952
5039
require (state.Stack .empty (),
4953
5040
" return with stack allocs that haven't been deallocated" );
4954
5041
require (state.ActiveOps .empty (),
4955
5042
" return with operations still active" );
5043
+ require (!state.GotAsyncContinuation ,
5044
+ " return with unawaited async continuation" );
4956
5045
4957
5046
if (isa<UnwindInst>(term)) {
4958
5047
require (state.CFG == VerifyFlowSensitiveRulesDetails::YieldUnwind,
@@ -4970,12 +5059,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4970
5059
}
4971
5060
}
4972
5061
}
4973
-
5062
+
4974
5063
if (isa<YieldInst>(term)) {
4975
5064
require (state.CFG != VerifyFlowSensitiveRulesDetails::YieldOnceResume,
4976
5065
" encountered multiple 'yield's along single path" );
4977
5066
require (state.CFG == VerifyFlowSensitiveRulesDetails::Normal,
4978
5067
" encountered 'yield' on abnormal CFG path" );
5068
+ require (!state.GotAsyncContinuation ,
5069
+ " encountered 'yield' while an unawaited continuation is active" );
4979
5070
}
4980
5071
4981
5072
auto successors = term->getSuccessors ();
@@ -5037,6 +5128,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
5037
5128
" inconsistent active-operations sets entering basic block" );
5038
5129
require (state.CFG == foundState.CFG ,
5039
5130
" inconsistent coroutine states entering basic block" );
5131
+ require (state.GotAsyncContinuation == foundState.GotAsyncContinuation ,
5132
+ " inconsistent active async continuations entering basic block" );
5040
5133
}
5041
5134
}
5042
5135
}
0 commit comments