Skip to content

Commit fa4444c

Browse files
authored
Remove top-level restriction for state machines compilation (#18817)
1 parent c8dc95c commit fa4444c

File tree

3 files changed

+96
-5
lines changed

3 files changed

+96
-5
lines changed

src/Compiler/Optimize/LowerStateMachines.fs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ let RepresentBindingAsStateVar g (bind: Binding) (resBody: StateMachineConversio
103103
stateVars = vref :: resBody.stateVars }
104104

105105
let isExpandVar g (v: Val) =
106-
isReturnsResumableCodeTy g v.TauType &&
107-
not v.IsCompiledAsTopLevel
106+
isReturnsResumableCodeTy g v.TauType
108107

109108
// We allow a prefix of bindings prior to the state machine, e.g.
110109
// task { .. }
@@ -114,8 +113,7 @@ let isExpandVar g (v: Val) =
114113
let isStateMachineBindingVar g (v: Val) =
115114
isExpandVar g v ||
116115
(let nm = v.LogicalName
117-
(nm.StartsWithOrdinal("builder@") || v.IsMemberThisVal) &&
118-
not v.IsCompiledAsTopLevel)
116+
(nm.StartsWithOrdinal("builder@") || v.IsMemberThisVal))
119117

120118
type env =
121119
{

tests/FSharp.Compiler.ComponentTests/Language/StateMachineTests.fs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace Language
44

55
open Xunit
66
open FSharp.Test.Compiler
7+
open FSharp.Test
78

89
module StateMachineTests =
910

@@ -160,4 +161,83 @@ let main _ =
160161
|> ignoreWarnings
161162
|> withOptimize
162163
|> compileExeAndRun
163-
|> shouldSucceed
164+
|> shouldSucceed
165+
166+
[<Fact>]
167+
let ``State machine defined as top level value is statically compiled`` () =
168+
Fsx """
169+
let test = task { return 42 }
170+
if test.Result <> 42 then failwith "expected 42"
171+
172+
task { printfn "Hello, World!"; return 42 }
173+
"""
174+
|> runFsi
175+
|> shouldSucceed
176+
177+
[<Fact>]
178+
let ``State machine defined as top level has a generated MoveNext method`` () =
179+
FSharp """
180+
module TestStateMachine
181+
let test = task { return 42 }
182+
"""
183+
|> compile
184+
|> verifyIL [ """
185+
.method public strict virtual instance void MoveNext() cil managed
186+
{
187+
.override [runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
188+
189+
.maxstack 4
190+
.locals init (int32 V_0,
191+
class [runtime]System.Exception V_1,
192+
bool V_2,
193+
class [runtime]System.Exception V_3)
194+
IL_0000: ldarg.0
195+
IL_0001: ldfld int32 TestStateMachine/test@3::ResumptionPoint
196+
IL_0006: stloc.0
197+
.try
198+
{
199+
IL_0007: ldarg.0
200+
IL_0008: ldflda valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32> TestStateMachine/test@3::Data
201+
IL_000d: ldc.i4.s 42
202+
IL_000f: stfld !0 valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32>::Result
203+
IL_0014: ldc.i4.1
204+
IL_0015: stloc.2
205+
IL_0016: ldloc.2
206+
IL_0017: brfalse.s IL_0036
207+
208+
IL_0019: ldarg.0
209+
IL_001a: ldflda valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32> TestStateMachine/test@3::Data
210+
IL_001f: ldflda valuetype [runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32>::MethodBuilder
211+
IL_0024: ldarg.0
212+
IL_0025: ldflda valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32> TestStateMachine/test@3::Data
213+
IL_002a: ldfld !0 valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32>::Result
214+
IL_002f: call instance void valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
215+
IL_0034: leave.s IL_0042
216+
217+
IL_0036: leave.s IL_0042
218+
219+
}
220+
catch [runtime]System.Object
221+
{
222+
IL_0038: castclass [runtime]System.Exception
223+
IL_003d: stloc.3
224+
IL_003e: ldloc.3
225+
IL_003f: stloc.1
226+
IL_0040: leave.s IL_0042
227+
228+
}
229+
IL_0042: ldloc.1
230+
IL_0043: stloc.3
231+
IL_0044: ldloc.3
232+
IL_0045: brtrue.s IL_0048
233+
234+
IL_0047: ret
235+
236+
IL_0048: ldarg.0
237+
IL_0049: ldflda valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32> TestStateMachine/test@3::Data
238+
IL_004e: ldflda valuetype [runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1<int32>::MethodBuilder
239+
IL_0053: ldloc.3
240+
IL_0054: call instance void valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [netstandard]System.Exception)
241+
IL_0059: ret
242+
}
243+
""" ]

tests/fsharp/core/state-machines/test.fsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,19 @@ module ``Check after code may include closures`` =
656656
f 3))
657657
check "vwelvewl" (makeStateMachine 3) -2
658658

659+
module ``Check simple state machine as top level value`` =
660+
let stateMachine =
661+
__stateMachine<int, int>
662+
(MoveNextMethodImpl<_>(fun sm ->
663+
if __useResumableCode then
664+
sm.Data <- 42 // we expect this result for successful resumable code compilation
665+
else
666+
sm.Data <- 0xdeadbeef // if we get this result it means we've failed to compile as resumable code
667+
))
668+
(SetStateMachineMethodImpl<_>(fun sm state -> ()))
669+
(AfterCode<_,_>(fun sm -> MoveOnce(&sm)))
670+
check "top_level_value" stateMachine 42
671+
659672
#if TESTS_AS_APP
660673
let RUN() = !failures
661674
#else

0 commit comments

Comments
 (0)