@@ -84,13 +84,18 @@ type LoweredSeqFirstPhaseResult =
8484{ /// The code to run in the second phase, to rebuild the expressions, once all code labels and their mapping to program counters have been determined
8585/// 'nextVar' is the argument variable for the GenerateNext method that represents the byref argument that holds the "goto" destination for a tailcalling sequence expression
8686 phase2: ( (* pc:*) ValRef * (* current:*) ValRef * (* nextVar:*) ValRef * Map < ILCodeLabel , int > -> Expr * Expr * Expr )
87+
8788/// The labels allocated for one portion of the sequence expression
8889 labels: int list
90+
8991/// any actual work done in Close
9092 significantClose: bool
9193
9294/// The state variables allocated for one portion of the sequence expression (i.e. the local let-bound variables which become state variables)
93- stateVars: ValRef list }
95+ stateVars: ValRef list
96+
97+ /// The vars captured by the non-synchronous path
98+ capturedVars: FreeVars }
9499
95100let isVarFreeInExpr v e = Zset.contains v( freeInExpr CollectTyparsAndLocals e) .FreeLocals
96101
@@ -174,6 +179,17 @@ let LowerSeqExpr g amap overallExpr =
174179| Expr.App( Expr.Val( vref,_,_),_ f0ty,[ elemTy],[ e],_ m) when valRefEq g vref g.seq_ vref-> Some( e, elemTy)
175180| _ -> None
176181
182+ let RepresentBindingAsLocal ( bind : Binding ) res2 m =
183+ // printfn "found letrec state variable %s" bind.Var.DisplayName
184+ { res2with
185+ phase2= ( fun ctxt ->
186+ let generate2 , dispose2 , checkDispose2 = res2.phase2 ctxt
187+ let generate = mkLetBind m bind generate2
188+ let dispose = dispose2
189+ let checkDispose = checkDispose2
190+ generate, dispose, checkDispose)
191+ stateVars= res2.stateVars}
192+
177193let RepresentBindingAsStateMachineLocal ( bind : Binding ) res2 m =
178194// printfn "found letrec state variable %s" bind.Var.DisplayName
179195let ( TBind ( v , e , sp )) = bind
@@ -243,17 +259,28 @@ let LowerSeqExpr g amap overallExpr =
243259 labels=[ label]
244260 stateVars=[]
245261 significantClose= false
262+ capturedVars= emptyFreeVars
246263}
247264
248- | SeqDelay( e ,_ elemTy) ->
265+ | SeqDelay( delayedExpr ,_ elemTy) ->
249266// printfn "found Seq.delay"
250- Lower isWholeExpr isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel e// note, using 'isWholeExpr' here prevents 'seq { yield! e }' and 'seq { 0 .. 1000 }' from being compiled
267+ // note, using 'isWholeExpr' here prevents 'seq { yield! e }' and 'seq { 0 .. 1000 }' from being compiled
268+ Lower isWholeExpr isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel delayedExpr
251269
252270| SeqAppend( e1, e2, m) ->
253271// printfn "found Seq.append"
254- match Lowerfalse false noDisposeContinuationLabel currentDisposeContinuationLabel e1,
255- Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel e2with
272+ let res1 = Lowerfalse false noDisposeContinuationLabel currentDisposeContinuationLabel e1
273+ let res2 = Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel e2
274+ match res1, res2with
256275| Some res1, Some res2->
276+
277+ let capturedVars =
278+ if res1.labels.IsEmptythen
279+ res2.capturedVars
280+ else
281+ // All of 'e2' is needed after resuming at any of the labels
282+ unionFreeVars res1.capturedVars( freeInExpr CollectLocals e2)
283+
257284 Some{ phase2= ( fun ctxt ->
258285let generate1 , dispose1 , checkDispose1 = res1.phase2 ctxt
259286let generate2 , dispose2 , checkDispose2 = res2.phase2 ctxt
@@ -265,29 +292,43 @@ let LowerSeqExpr g amap overallExpr =
265292 generate, dispose, checkDispose)
266293 labels= res1.labels@ res2.labels
267294 stateVars= res1.stateVars@ res2.stateVars
268- significantClose= res1.significantClose|| res2.significantClose}
295+ significantClose= res1.significantClose|| res2.significantClose
296+ capturedVars= capturedVars}
269297| _ ->
270298 None
271299
272- | SeqWhile( e1 , e2 , m) ->
300+ | SeqWhile( guardExpr , bodyExpr , m) ->
273301// printfn "found Seq.while"
274- match Lowerfalse false noDisposeContinuationLabel currentDisposeContinuationLabel e2with
302+ let resBody = Lowerfalse false noDisposeContinuationLabel currentDisposeContinuationLabel bodyExpr
303+ match resBodywith
275304| Some res2->
305+ let capturedVars =
306+ if res2.labels.IsEmptythen
307+ res2.capturedVars// the whole loopis synchronous, no labels
308+ else
309+ freeInExpr CollectLocals expr// everything is needed on subsequent iterations
310+
276311 Some{ phase2= ( fun ctxt ->
277312let generate2 , dispose2 , checkDispose2 = res2.phase2 ctxt
278- let generate = mkWhile g( SequencePointAtWhileLoope1 .Range, NoSpecialWhileLoopMarker, e1 , generate2, m)
313+ let generate = mkWhile g( SequencePointAtWhileLoopguardExpr .Range, NoSpecialWhileLoopMarker, guardExpr , generate2, m)
279314let dispose = dispose2
280315let checkDispose = checkDispose2
281316 generate, dispose, checkDispose)
282317 labels= res2.labels
283318 stateVars= res2.stateVars
284- significantClose= res2.significantClose}
319+ significantClose= res2.significantClose
320+ capturedVars= capturedVars}
285321| _ ->
286322 None
287323
288324| SeqUsing( resource, v, body, elemTy, m) ->
289325// printfn "found Seq.using"
290- Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel( mkLet( SequencePointAtBinding body.Range) m v resource( mkCallSeqFinally g m elemTy body( mkUnitDelayLambda g m( mkCallDispose g m v.Type( exprForVal m v)))))
326+ let reduction =
327+ mkLet( SequencePointAtBinding body.Range) m v resource
328+ ( mkCallSeqFinally g m elemTy body
329+ ( mkUnitDelayLambda g m
330+ ( mkCallDispose g m v.Type( exprForVal m v))))
331+ Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel reduction
291332
292333| SeqFor( inp, v, body, genElemTy, m) ->
293334// printfn "found Seq.for"
@@ -298,18 +339,21 @@ let LowerSeqExpr g amap overallExpr =
298339// while enum.MoveNext() do
299340// let v = enum.Current
300341// body ]]
301- Lower false isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel
302- ( mkCallSeqUsing g m inpEnumTy genElemTy( callNonOverloadedMethod g amap m" GetEnumerator" ( mkSeqTy g inpElemTy) [ inp])
303- ( mkLambdaNoType g m enumv
342+ let reduction =
343+ mkCallSeqUsing g m inpEnumTy genElemTy( callNonOverloadedMethod g amap m" GetEnumerator" ( mkSeqTy g inpElemTy) [ inp])
344+ ( mkLambdaNoType g m enumv
304345( mkCallSeqGenerated g m genElemTy( mkUnitDelayLambda g m( callNonOverloadedMethod g amap m" MoveNext" inpEnumTy[ enume]))
305346( mkInvisibleLet m v( callNonOverloadedMethod g amap m" get_Current" inpEnumTy[ enume])
306- ( mkCoerceIfNeeded g( mkSeqTy g genElemTy) ( tyOfExpr g body) body)))))
347+ ( mkCoerceIfNeeded g( mkSeqTy g genElemTy) ( tyOfExpr g body) body))))
348+ Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel reduction
307349
308350| SeqTryFinally( e1, compensation, m) ->
309351// printfn "found Seq.try/finally"
310352let innerDisposeContinuationLabel = IL.generateCodeLabel()
311- match Lowerfalse false noDisposeContinuationLabel innerDisposeContinuationLabel e1with
353+ let resBody = Lowerfalse false noDisposeContinuationLabel innerDisposeContinuationLabel e1
354+ match resBodywith
312355| Some res1->
356+ let capturedVars = unionFreeVars res1.capturedVars( freeInExpr CollectLocals compensation)
313357 Some{ phase2= ( fun (( pcv , _currv , _ , pcMap ) as ctxt ) ->
314358let generate1 , dispose1 , checkDispose1 = res1.phase2 ctxt
315359let generate =
@@ -348,7 +392,8 @@ let LowerSeqExpr g amap overallExpr =
348392 generate, dispose, checkDispose)
349393 labels= innerDisposeContinuationLabel:: res1.labels
350394 stateVars= res1.stateVars
351- significantClose= true }
395+ significantClose= true
396+ capturedVars= capturedVars}
352397| _ ->
353398 None
354399
@@ -361,7 +406,8 @@ let LowerSeqExpr g amap overallExpr =
361406 generate, dispose, checkDispose)
362407 labels= []
363408 stateVars= []
364- significantClose= false }
409+ significantClose= false
410+ capturedVars= emptyFreeVars}
365411
366412| Expr.Sequential( x1, x2, NormalSeq, ty, m) ->
367413match Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel x2with
@@ -376,13 +422,18 @@ let LowerSeqExpr g amap overallExpr =
376422 generate, dispose, checkDispose) }
377423| None-> None
378424
379- | Expr.Let( bind, e2 , m,_)
425+ | Expr.Let( bind, bodyExpr , m,_)
380426// Restriction: compilation of sequence expressions containing non-toplevel constrained generic functions is not supported
381427when bind.Var.IsCompiledAsTopLevel|| not ( IsGenericValWithGenericContraints g bind.Var) ->
382- match Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel e2with
428+
429+ let resBody = Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel bodyExpr
430+ match resBodywith
383431| Some res2->
384432if bind.Var.IsCompiledAsTopLevelthen
385433 Some( RepresentBindingsAsLifted( mkLetBind m bind) res2)
434+ elif not ( res2.capturedVars.FreeLocals.Contains( bind.Var)) then
435+ // printfn "found state variable %s" bind.Var.DisplayName
436+ Some( RepresentBindingAsLocal bind res2 m)
386437else
387438// printfn "found state variable %s" bind.Var.DisplayName
388439 Some( RepresentBindingAsStateMachineLocal bind res2 m)
@@ -421,12 +472,18 @@ let LowerSeqExpr g amap overallExpr =
421472*)
422473| Expr.Match( spBind, exprm, pt, targets, m, ty) when targets|> Array.forall( fun ( TTarget ( vs , _e , _spTarget )) -> isNil vs) ->
423474// lower all the targets. abandon if any fail to lower
424- let tgl = targets|> Array.map( fun ( TTarget ( _vs , e , _spTarget )) -> Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabele ) |> Array.toList
475+ let tglArray = targets|> Array.map( fun ( TTarget ( _vs , targetExpr , _spTarget )) -> Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabeltargetExpr )
425476// LIMITATION: non-trivial pattern matches involving or-patterns or active patterns where bindings can't be
426477// transferred to the r.h.s. are not yet compiled.
427- if tgl|> List.forall Option.isSomethen
428- let tgl = List.map Option.get tgl
478+ if tglArray|> Array.forall Option.isSomethen
479+ let tglArray = Array.map Option.get tglArray
480+ let tgl = Array.toList tglArray
429481let labs = tgl|> List.collect( fun res -> res.labels)
482+ let ( capturedVars , _ ) =
483+ (( emptyFreeVars, false ), Array.zip targets tglArray)
484+ ||> Array.fold( fun ( fvs , seenLabel ) (( TTarget ( _vs , e , _spTarget )), res ) ->
485+ if seenLabelthen unionFreeVars fvs( freeInExpr CollectLocals e), true
486+ else res.capturedVars, not res.labels.IsEmpty)
430487let stateVars = tgl|> List.collect( fun res -> res.stateVars)
431488let significantClose = tgl|> List.exists( fun res -> res.significantClose)
432489 Some{ phase2= ( fun ctxt ->
@@ -443,7 +500,8 @@ let LowerSeqExpr g amap overallExpr =
443500 generate, dispose, checkDispose)
444501 labels= labs
445502 stateVars= stateVars
446- significantClose= significantClose}
503+ significantClose= significantClose
504+ capturedVars= capturedVars}
447505else
448506 None
449507
@@ -502,7 +560,8 @@ let LowerSeqExpr g amap overallExpr =
502560 generate, dispose, checkDispose)
503561 labels=[ label]
504562 stateVars=[]
505- significantClose= false }
563+ significantClose= false
564+ capturedVars= emptyFreeVars}
506565else
507566let v , ve = mkCompGenLocal m" v" inpElemTy
508567 Lowerfalse isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel( mkCallSeqCollect g m inpElemTy inpElemTy( mkLambdaNoType g m v( mkCallSeqSingleton g m inpElemTy ve)) arbitrarySeqExpr)