< Summary - FSharp.Interop.Dlr

Information
Class: FSharp.Interop.Dlr.TranslateBlock
Assembly: FSharp.Interop.Dlr
File(s): /home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/TranslateBlock.fs
Line coverage
92%
Covered lines: 167
Uncovered lines: 14
Coverable lines: 181
Total lines: 417
Line coverage: 92.2%
Branch coverage
84%
Covered branches: 429
Total branches: 510
Branch coverage: 84.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
NameFor(...)100%44100%
get_Self()100%22100%
Convert(...)100%11100%
isCode(...)75%44100%
codeType(...)50%22100%
defaultOf(...)100%44100%
asUnit(...)100%22100%
isUnitCall(...)100%66100%
capturing(...)50%22100%
structPath(...)100%1010100%
Invoke(...)100%44100%
rooted@109(...)100%44100%
Invoke(...)100%22100%
apply@113(...)100%22100%
Invoke(...)100%22100%
asValue@119(...)100%22100%
inPlace(...)100%1212100%
fields(...)100%66100%
Invoke(...)83.33%66100%
effectFree(...)69.76%1008687.5%
Invoke(...)0%2040%
primitive@185(...)25%44100%
elementField(...)83.33%66100%
elements(...)83.33%1212100%
elementNameClashes(...)100%1212100%
Invoke(...)0%4260%
go@239(...)100%66100%
isAlias(...)100%1010100%
reached(...)100%11100%
Invoke(...)81.25%161691.66%
go@268-1(...)94.44%181891.66%
ambiguous(...)100%11100%
isRefCell(...)50%66100%
read(...)90.9%372268.75%
Invoke(...)0%620%
recovered@317(...)100%22100%
element(...)83.33%44100%
isCell(...)50%5466.66%
assign(...)66.66%9657.14%
Invoke(...)91.66%1212100%
go@370-2(...)91.66%1212100%
rethrowing(...)100%11100%
func(...)100%44100%
call(...)100%3232100%

File(s)

/home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/TranslateBlock.fs

#LineLine coverage
 1namespace FSharp.Interop.Dlr
 2
 3open System
 4open System.Linq.Expressions
 5open FSharp.Quotations
 6open FSharp.Quotations.Patterns
 7open FSharp.Quotations.DerivedPatterns
 8open FSharp.Quotations.ExprShape
 9open FSharp.Reflection
 10open Microsoft.FSharp.Linq.RuntimeHelpers
 11open System.Runtime.CompilerServices
 12
 13/// One block's translation context (`Block`) and what every rewrite of it shares: where its
 14/// free variables come from (`Captures`), the builder's own members (`Plumbing`), and the
 15/// helpers they use.
 16module internal TranslateBlock =
 17    open TranslatePatterns
 18    open SiteHoisting
 19
 20    /// What every part of the translation of one block needs: where it is (the builder and the
 21    /// member it sits in) and where its values come from (the state machine struct or the Delay
 22    /// closure — the same contract: the captured variables are its fields, by name).
 23    type Block =
 24        { BuilderType: Type
 25          /// Type declaring the enclosing member: the binder's accessibility context, as in C#.
 26          Context: Type
 27          /// The enclosing member's whole reflected body, for values the optimizer inlined.
 28          MemberBody: Expr
 29          /// The compiler-generated container: the state machine struct, or the Delay closure class.
 30          ClosureType: Type
 31          /// The compiled delegate's one parameter: the struct itself (a copy of the machine the
 32          /// reader is handed by reference), or the closure as `obj`.
 33          Closure: Var
 34          /// The container's fields, named after the captured variables.
 35          Fields: Collections.Generic.IDictionary<string, Reflection.FieldInfo>
 36          /// The compiled block's name, `dlr__Program_fs@7`: its stack frame's name.
 37          Name: string
 38          /// How many nested lambdas of each kind (`for`, `try`, `fun`, …) have been named so far.
 39          Names: Collections.Generic.Dictionary<string, int>
 40          /// Names two of the variables the block reaches share (#196): its free variables, and
 41          /// those of the definitions it recovers for what the optimizer inlined (`Captures.reached`).
 42          Ambiguous: Set<string>
 43          /// Aliases (`let x = y` of an immutable `y`, `let x = 1`) the block reaches only through
 44          /// a recovered definition: substituted, never a field, whatever field shares the name.
 45          Substituted: Set<Var> }
 46        /// The name for this block's next compiled part of `kind`, for its stack frame: F#'s
 47        /// closure style, `dlr__Program_fs@7-for`, then `-for-2`, `-for-3` in order.
 48        member this.NameFor (kind: string) =
 3591949            let n = (match this.Names.TryGetValue kind with | true, n -> n | _ -> 0) + 1
 1197350            this.Names.[kind] <- n
 2394651            if n = 1 then sprintf "%s-%s" this.Name kind else sprintf "%s-%s-%d" this.Name kind n
 52        member this.Self =
 1268153            if this.ClosureType.IsValueType then Expr.Var this.Closure
 663954            else Expr.Coerce(Expr.Var this.Closure, this.ClosureType)
 718355        member this.Convert (resultType: Type) (e: Expr) = Binders.convert this.Context resultType e
 56
 57    /// The builder's members type as `ResumableCode<'D, 'R>` — the shape the compiler's state
 58    /// machine needs — where the translation produces the plain `'R` value. `codeType` is what
 59    /// such an expression's type becomes; an expression typed that way that is not a builder call
 60    /// (the compiler's default value in an unmatched `try … with` arm) is retyped to it so the
 61    /// rebuilt tree is consistent.
 62    let isCode (t: Type) =
 613563        t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Microsoft.FSharp.Core.CompilerServices.ResumableCode
 242464    let codeType (t: Type) = if isCode t then t.GetGenericArguments().[1] else t
 65    let defaultOf (t: Type) =
 9666        if t = typeof<unit> then Expr.Value(())
 3667        elif t.IsValueType then Expr.DefaultValue t
 1268        else Expr.Value(null, t)
 69
 70    /// The generic rewriter, threaded through the sections so each can rewrite a subexpression:
 71    /// the variables bound inside the expression so far, and the expression.
 72    type Rewrite = Set<Var> -> Expr -> Expr
 73
 74    /// A `unit` expression may compile to a void call, which cannot be the value of a lambda or
 75    /// of the block; end it with the unit constant so the tree has a `Unit` value.
 76    let asUnit (e: Expr) =
 8151477        if e.Type = typeof<unit> then Expr.Sequential(e, Expr.Value(())) else e
 78
 79    /// A `unit`-typed call or application — possibly a void method, which the converter cannot
 80    /// use as a value — as opposed to `()` or a unit-typed variable.
 81    let isUnitCall (e: Expr) =
 5469782        e.Type = typeof<unit> && (match e with Call _ | Application _ -> true | _ -> false)
 83
 84    /// On Mono's browser-wasm runtime a nested lambda that captures nothing loses its arguments
 85    /// when invoked (see DlrRuntime); one that captures is fine. So there, a nested delegate
 86    /// body is made to capture the block's closure parameter, which costs nothing elsewhere.
 87    let capturing (block: Block) (body: Expr) =
 5527288        if onWasm then Expr.Let(Var("captured", block.Closure.Type), Expr.Var block.Closure, body) else body
 89
 90    /// The struct variable a receiver is rooted at, with the struct fields down from it:
 91    /// `v`, `v.Inner`, `v.Inner.Point`.
 92    let rec structPath (e: Expr) : (Var * Reflection.FieldInfo list) option =
 1562393        match e with
 1636794        | Var v when v.Type.IsValueType -> Some(v, [])
 111395        | FieldGet(Some inner, f) when f.FieldType.IsValueType -> structPath inner |> Option.map (fun (v, fs) -> v, fs @
 1197596        | _ -> None
 97
 98    /// A struct variable that lives in a cell — a `let mutable` of the block, or a mutable captured
 99    /// from outside it, which the compiler stores as an FSharpRef — reads back as a copy, so an
 100    /// operation that mutates it in place (a field set, a property setter or a method, on the
 101    /// variable or a struct field of it) would be lost (#162). It runs on a temporary copy that is
 102    /// written back, in a finally. The arguments are evaluated first, left to right, into temporaries: the
 103    /// receiver is a variable, so this is F#'s order, and an argument that mutates the variable
 104    /// itself is not overwritten by a copy read before it ran. The copy is immutable to F# — so it
 105    /// is not celled again — but a LINQ variable, which a field set or call mutates in place.
 106    /// `isTarget` picks the cell variables; `read` and `write` are the cell's; `recurse` rewrites
 107    /// the arguments. None when `e` is not such an operation.
 108    let inPlace (isTarget: Var -> bool) (read: Var -> Expr) (write: Var -> Expr -> Expr) (recurse: Expr -> Expr) (e: Exp
 109        let rooted (receiver: Expr) =
 15305110            match structPath receiver with
 4812111            | Some(v, fields) when isTarget v -> Some(v, fields)
 13823112            | _ -> None
 113        let apply (v: Var) (fields: Reflection.FieldInfo list) (args: Expr list) (op: Expr -> Expr list -> Expr) =
 3249114            let temps = args |> List.mapi (fun i a -> Var(sprintf "arg%d" i, a.Type), recurse a)
 1482115            let copy = Var(v.Name + "Copy", v.Type)
 2373116            let receiver = fields |> List.fold (fun r f -> Expr.FieldGet(r, f)) (Expr.Var copy)
 5472117            let result = op receiver [ for t, _ in temps -> Expr.Var t ]
 118            // Unit-typed both, as values: a void call or setter cannot be an argument.
 5928119            let asValue (e: Expr) = if e.Type = typeof<unit> then Expr.Sequential(e, Expr.Value(())) else e
 120            // The write-back in a finally (`Binders.InPlace`, made a TryFinally after conversion),
 121            // so a member that mutates then throws keeps its mutation, as in plain F#; the
 122            // arguments ran before, outside it, so a throwing argument writes nothing back.
 1482123            let body =
 1482124                Expr.Call(typeof<Binders.InPlace>.GetMethod("Then", Reflection.BindingFlags.Static ||| Reflection.Bindin
 1482125                          [ asValue result; asValue (write v (Expr.Var copy)) ])
 2508126            List.foldBack (fun (t, a) inner -> Expr.Let(t, a, inner)) temps (Expr.Let(copy, read v, body))
 275852127        match e with
 128        | FieldSet(Some receiver, f, x) ->
 3066129            rooted receiver |> Option.map (fun (v, fields) -> apply v fields [ x ] (fun r a -> Expr.FieldSet(r, f, List.
 130        | PropertySet(Some receiver, p, indexes, x) ->
 6207131            rooted receiver |> Option.map (fun (v, fields) ->
 4726132                apply v fields (indexes @ [ x ]) (fun r a -> Expr.PropertySet(r, p, List.last a, List.take indexes.Lengt
 133        | Call(Some receiver, mi, args) ->
 16011134            rooted receiver |> Option.map (fun (v, fields) -> apply v fields args (fun r a -> Expr.Call(r, mi, a)))
 260547135        | _ -> None
 136
 137    /// Where a free variable of the body comes from: the Delay closure, or failing that the
 138    /// enclosing member's body.
 139    module Captures =
 140
 141        /// The container's fields by name. A state machine also has fields of its own, `Data`
 142        /// (a `DlrData<_>`) and `ResumptionPoint` (an int), declared first, and they are skipped
 143        /// rather than found by name: a captured `Data` gets a second field of that name after
 144        /// them (IL allows it, the types differ); a captured `ResumptionPoint` is an `FSharpRef`
 145        /// when mutable, a compiler error (FS2014, as in `task { }`) when an `int` the optimizer
 146        /// kept, and when it inlined the value there is no field, and the body's variable of that
 147        /// name must resolve from the enclosing member, not to the machine's own counter.
 148        let fields (containerType: Type) : Collections.Generic.IDictionary<string, Reflection.FieldInfo> =
 13634149            let all = containerType.GetFields(Reflection.BindingFlags.Instance ||| Reflection.BindingFlags.Public ||| Re
 150            let isData (f: Reflection.FieldInfo) =
 20978151                f.Name = "Data" && f.FieldType.IsGenericType && f.FieldType.GetGenericTypeDefinition() = typedefof<DlrDa
 152            let own =
 13634153                if containerType.IsValueType then
 4470154                    [ yield! all |> Array.filter isData
 20972155                      yield! all |> Array.filter (fun f -> f.Name = "ResumptionPoint" && f.FieldType = typeof<int>) |> A
 9164156                else []
 13634157            let fields = Collections.Generic.Dictionary<string, Reflection.FieldInfo>()
 78324158            for f in all do
 42116159                if not (List.contains f own) then fields.[f.Name] <- f
 13634160            fields :> _
 161
 162        /// Whether recovering `e` runs nothing twice: the optimizer moves no effect into the
 163        /// block's closure, so an effect in a recovered definition has run already, outside it
 164        /// (#203). Literals, immutable variables and module values, lambdas (built, not run), the
 165        /// F# operators on primitives, and tuples, unions, records, lets of those.
 166        let rec effectFree (e: Expr) =
 496167            match e with
 354168            | Value _ | Lambda _ | DefaultValue _ -> true
 169            // Not a mutable: read again, it gives its current value, not the one bound (#211 review).
 82170            | Var v -> not v.IsMutable
 171            // A module's immutable value (`let answer = 42`): reading it runs nothing. Not a class's
 172            // static property, whose getter can run code.
 173            | PropertyGet(None, p, []) ->
 0174                not p.CanWrite
 6175                && (p.DeclaringType.GetCustomAttributes(typeof<CompilationMappingAttribute>, false)
 12176                    |> Seq.exists (fun a -> (a :?> CompilationMappingAttribute).SourceConstructFlags = SourceConstructFl
 6177            | NewTuple es | NewUnionCase(_, es) | NewRecord(_, es) -> List.forall effectFree es
 0178            | Coerce(x, _) | TupleGet(x, _) -> effectFree x
 3179            | Let(_, d, b) -> effectFree d && effectFree b
 24180            | LetRecursive(bs, b) -> List.forall (snd >> effectFree) bs && effectFree b
 33181            | Call(None, mi, args) when mi.DeclaringType.FullName = "Microsoft.FSharp.Core.Operators" ->
 182                // `fst`/`snd`, and the arithmetic, comparison, bitwise and `not` operators on
 183                // primitives and strings. Not every operator: one on a user's type calls the user's
 184                // code (`+`, `=` through `Equals`), and `|>` calls a function.
 12185                let primitive (t: Type) = t.IsPrimitive || t = typeof<string> || t = typeof<decimal>
 186                let plain =
 24187                    match mi.Name with
 18188                    | "Fst" | "Snd" -> true
 189                    | "op_Addition" | "op_Subtraction" | "op_Multiply" | "op_Division" | "op_Modulus"
 190                    | "op_UnaryNegation" | "op_UnaryPlus"
 191                    | "op_Equality" | "op_Inequality" | "op_LessThan" | "op_GreaterThan" | "op_LessThanOrEqual" | "op_Gr
 192                    | "op_BitwiseAnd" | "op_BitwiseOr" | "op_ExclusiveOr" | "op_LogicalNot" | "op_LeftShift" | "op_Right
 18193                        args |> List.forall (fun a -> primitive a.Type)
 0194                    | _ -> false
 48195                plain && List.forall effectFree args
 9196            | _ -> false
 197
 198        /// The field element `i` of a reference tuple the Release optimizer split is held in: `t_0`,
 199        /// `t_1`, … (`ExpandStructuralBinding`), with `$tupleElem` after each when its definition
 200        /// branched (`TryRewriteBranchingTupleBinding`); an element nothing reads has none (#203).
 201        let elementField (fields: Collections.Generic.IDictionary<string, Reflection.FieldInfo>) (v: Var) (i: int) : Ref
 29862202            if not (FSharpType.IsTuple v.Type) || v.Type.IsValueType || fields.ContainsKey v.Name then None
 203            else
 315204                let t = (FSharpType.GetTupleElements v.Type).[i]
 315205                [ sprintf "%s_%d" v.Name i; sprintf "%s_%d$tupleElem" v.Name i ]
 315206                |> List.tryPick (fun name ->
 525207                    match fields.TryGetValue name with
 234208                    | true, f when f.FieldType = t -> Some f
 723209                    | _ -> None)
 210
 211        /// The fields a reference tuple the Release optimizer split is held in, one per element:
 212        /// `t_0`, `t_1`, … (`ExpandStructuralBinding`), with `$tupleElem` after each when its
 213        /// definition branched (`TryRewriteBranchingTupleBinding`). Its definition would run again
 214        /// (#203). An element with no field (a constant the optimizer inlined) is the one in the
 215        /// definition's tuple, when that is effect-free.
 216        let elements (fields: Collections.Generic.IDictionary<string, Reflection.FieldInfo>) (memberBody: Expr) (v: Var)
 28982217            if not (FSharpType.IsTuple v.Type) || v.Type.IsValueType then None
 218            else
 225219                let found = FSharpType.GetTupleElements v.Type |> Array.mapi (fun i _ -> elementField fields v i) |> Lis
 123220                if List.forall Option.isNone found then None
 221                else
 222                    // An element the optimizer inlined (a constant) has no field: the definition's.
 54223                    let literal = match letDefinition v memberBody with Some(NewTuple es) -> Some es | _ -> None
 224                    let parts =
 27225                        found |> List.mapi (fun i f ->
 54226                            match f, literal with
 27227                            | Some f, _ -> Some(Choice1Of2 f)
 51228                            | None, Some es when effectFree es.[i] -> Some(Choice2Of2 es.[i])
 30229                            | None, _ -> None)
 54230                    if List.forall Option.isSome parts then Some(List.choose id parts) else None
 231
 232        /// Names a split tuple's element field and a variable of the member could both have: a
 233        /// variable named `t_0` beside a tuple `t`. The optimizer's `t_0` then becomes `t_00` or the
 234        /// user's does, in an order of its own, so neither is told apart by name: both refused.
 235        /// `split`: whether the container may hold `t` split (a container with a field `t` holds it
 236        /// whole, as every Debug closure does).
 237        let elementNameClashes (split: string -> bool) (memberBody: Expr) : Set<string> =
 6656238            let vars = Collections.Generic.HashSet<Var>(HashIdentity.Reference)
 239            let rec go (e: Expr) =
 1124720240                match e with
 270513241                | ShapeVar v -> vars.Add v |> ignore
 463706242                | ShapeLambda(v, b) -> vars.Add v |> ignore; go b
 3903341243                | ShapeCombination(_, es) -> for x in es do go x
 6656244            go memberBody
 238509245            let names = vars |> Seq.map (fun v -> v.Name) |> Set.ofSeq
 251821246            set [ for v in vars do
 472128247                    if FSharpType.IsTuple v.Type && not v.Type.IsValueType && split v.Name then
 21361248                        for i in 0 .. (FSharpType.GetTupleElements v.Type).Length - 1 do
 9655249                            let element = sprintf "%s_%d" v.Name i
 19310250                            if names.Contains element then
 12251                                yield v.Name
 6668252                                yield element ]
 253
 254        /// Whether `reached` takes `v` for an alias the optimizer substituted: immutable, not one of
 255        /// the block's own free variables (`own`), and an immutable `let` of an immutable variable or
 256        /// of a literal. Such a variable is never read from a field, even one of its name.
 257        let isAlias (own: Set<Var>) (memberBody: Expr) (v: Var) =
 35755258            not v.IsMutable && not (own.Contains v)
 320259            && (match letDefinition v memberBody with Some(Var y) -> not y.IsMutable | Some(Value _) -> true | _ -> fals
 260
 261        /// The variables a block reaches: its free variables and, for each one with no field of
 262        /// its name, those of the definition `read` would recover for it. An alias reached that
 263        /// way (an immutable `let` of an immutable variable or of a literal) is the optimizer's to
 264        /// substitute, never a field, so it stands for what it names: it is returned apart, not
 265        /// counted with the rest. One of a mutable it keeps, holding the value at the time.
 266        let reached (fields: Collections.Generic.IDictionary<string, Reflection.FieldInfo>) (memberBody: Expr) (body: Ex
 20030267            let own = body.GetFreeVars() |> Set.ofSeq
 268            let rec go (seen: Set<Var>) counted aliases (pending: Var list) =
 36291269                match pending with
 13343270                | [] -> counted, aliases
 23017271                | v :: rest when seen.Contains v -> go seen counted aliases rest
 272                | v :: rest ->
 22879273                    let seen = seen.Add v
 22879274                    let def = letDefinition v memberBody
 22879275                    let alias = isAlias own memberBody v
 276                    let inner =
 91546277                        if (fields.ContainsKey v.Name || (elementField fields v 0).IsSome || (elements fields memberBody
 278                        else
 7071279                            match def |> Option.orElse (parameterArgument v memberBody) with
 7059280                            | Some d -> List.ofSeq (d.GetFreeVars())
 0281                            | None -> []
 22894282                    if alias then go seen counted (Set.add v aliases) (inner @ rest)
 22864283                    else go seen (v :: counted) aliases (inner @ rest)
 13343284            go Set.empty [] Set.empty (List.ofSeq own)
 285
 286        /// Two variables of one name, one reached through a local function or alias the optimizer
 287        /// inlined: the compiler names their fields `x`, `x0`, … in an order of its own, and a
 288        /// recovered definition would run again, so neither tells which is which (#196).
 289        let private ambiguous (v: Var) =
 69290            DlrTranslationException(
 69291                sprintf "dlr { } reaches two variables named '%s', one through a local function or alias the optimizer i
 292
 293        let private isRefCell (f: Reflection.FieldInfo) (t: Type) =
 555294            f.FieldType.IsGenericType
 555295            && f.FieldType.GetGenericTypeDefinition() = typedefof<Ref<_>>
 555296            && f.FieldType.GetGenericArguments().[0] = t
 297
 298        /// A free variable of the body becomes a read of the closure field of the same name.
 299        /// A captured `let mutable` is stored as an FSharpRef cell; read through it. When there is
 300        /// no such field the optimizer inlined the variable's definition (a literal, a local
 301        /// function, ...) instead of capturing it, so substitute that definition from the
 302        /// enclosing member's body; its own free variables resolve the same way. A tuple split
 303        /// into a field per element is rebuilt from those (`elements`).
 304        let read (block: Block) (resolve: Expr -> Expr) (v: Var) : Expr =
 9904305            if block.Ambiguous.Contains v.Name then raise (ambiguous v)
 29370306            match (if block.Substituted.Contains v then (false, null) else block.Fields.TryGetValue v.Name) with
 18489307            | true, f when f.FieldType = v.Type -> Expr.FieldGet(block.Self, f)
 279308            | true, f when isRefCell f v.Type ->
 279309                Expr.PropertyGet(Expr.FieldGet(block.Self, f), f.FieldType.GetProperty("Value"))
 310            | true, f ->
 0311                raise (DlrTranslationException(
 0312                        sprintf "dlr { } captured '%s' as %s but the body uses it as %s." v.Name f.FieldType.Name v.Type
 313            | _ ->
 1218314                match (if block.Substituted.Contains v then None else elements block.Fields block.MemberBody v) with
 105315                | Some parts -> Expr.NewTuple [ for p in parts -> match p with Choice1Of2 f -> Expr.FieldGet(block.Self,
 316                | None ->
 317                let recovered (def: Expr) =
 779318                    if effectFree def then resolve def
 319                    else
 9320                        raise (DlrTranslationException(
 9321                                sprintf "dlr { } would run the definition of '%s' again: the optimizer did not capture i
 394322                match letDefinition v block.MemberBody with
 367323                | Some def -> recovered def
 324                | None ->
 27325                    match parameterArgument v block.MemberBody with
 27326                    | Some arg -> recovered arg
 327                    | None ->
 0328                        raise (DlrTranslationException(
 0329                                sprintf "dlr { } could not find captured variable '%s' on closure %s (fields: %s), a let
 0330                                    v.Name block.ClosureType.Name (String.Join(", ", block.Fields.Keys))))
 331
 332        /// `fst v`, `snd v` or element `i` of `v` otherwise, read from its own field when the
 333        /// optimizer split `v` (`elementField`); None to read `v` itself.
 334        let element (block: Block) (v: Var) (i: int) : Expr option =
 240335            if block.Ambiguous.Contains v.Name then raise (ambiguous v)
 222336            if block.Substituted.Contains v then None
 375337            else elementField block.Fields v i |> Option.map (fun f -> Expr.FieldGet(block.Self, f))
 338
 339        /// Whether a captured variable is a mutable, stored in an FSharpRef cell.
 340        let isCell (block: Block) (v: Var) =
 42341            match block.Fields.TryGetValue v.Name with
 84342            | true, f when not (block.Ambiguous.Contains v.Name) -> isRefCell f v.Type
 0343            | _ -> false
 344
 345        /// `v <- value` on a captured `let mutable`: a write through its FSharpRef cell.
 346        let assign (block: Block) (v: Var) (value: Expr) : Expr =
 240347            if block.Ambiguous.Contains v.Name then raise (ambiguous v)
 234348            match block.Fields.TryGetValue v.Name with
 234349            | true, f when isRefCell f v.Type ->
 234350                Expr.PropertySet(Expr.FieldGet(block.Self, f), f.FieldType.GetProperty("Value"), value)
 351            | _ ->
 0352                raise (DlrTranslationException(
 0353                        sprintf "dlr { } assigns '%s', which is not a captured mutable on closure %s (fields: %s)."
 0354                            v.Name block.ClosureType.Name (String.Join(", ", block.Fields.Keys))))
 355
 356    /// The builder's own methods: `Return`/`Zero`/`Combine` fold away, and the control-flow
 357    /// members become `DlrRuntime` calls over delegates.
 358    module Plumbing =
 359
 360        /// A body as a `Func<..>` delegate over `vars` (see DlrRuntime for why not an F# function).
 361        /// The delegate's type is built from the variables' types and the body's; a `unit` body
 362        /// (which may compile to a void call) becomes a `Func<.., unit>`, not an `Action`.
 363        /// A raw `try … with`'s handler, run as a delegate (`DlrRuntime.tryWith`), is outside any
 364        /// catch block, so its `reraise ()` — F# puts one where no case matches — throws the
 365        /// caught exception `ex` again instead (`DlrRuntime.rethrow`, keeping its stack trace). A
 366        /// nested `try … with` keeps its own handler's. (The builder's handlers need none: there
 367        /// the compiler's unmatched case is already an ExceptionDispatchInfo rethrow, and an
 368        /// explicit `reraise ()` does not compile.)
 369        let rethrowing (ex: Var) (handler: Expr) : Expr =
 370            let rec go (e: Expr) =
 3228371                match e with
 612372                | Call(None, mi, []) when mi.IsGenericMethod && mi.GetGenericMethodDefinition() = reraiseMethod ->
 306373                    Expr.Call(rethrow.GetGenericMethodDefinition().MakeGenericMethod(e.Type), [ Expr.Var ex ])
 6374                | TryWith(body, fv, filter, cv, catch) -> Expr.TryWith(go body, fv, filter, cv, catch)
 2916375                | _ -> Quotation.rebuild go e
 768376            go handler
 377
 378        let func (block: Block) (rewriteIn: Rewrite) (bound: Set<Var>) (vars: Var list) (body: Expr) : Expr =
 12484379            let bound = vars |> List.fold (fun b v -> Set.add v b) bound
 6134380            let body = capturing block (asUnit (rewriteIn bound body))
 381            // A parameterless delegate whose body is itself a lambda (`try (fun x -> …) with …`)
 382            // reads back from FSharp.Core as a delegate of that lambda's parameter: hold the lambda
 383            // in a `let` so it stays the result.
 384            let body =
 6134385                match vars, body with
 24386                | [], Lambda _ -> let result = Var("result", body.Type) in Expr.Let(result, body, Expr.Var result)
 6122387                | _ -> body
 18616388            let delegateType = Expression.GetFuncType(Array.append (vars |> List.map (fun v -> v.Type) |> Array.ofList) 
 6134389            Expr.NewDelegate(delegateType, vars, body)
 390
 391        /// A call on the builder (Discover already unwrapped the outermost Delay).
 392        let call (block: Block) (rewriteIn: Rewrite) (bound: Set<Var>) (mi: Reflection.MethodInfo) (args: Expr list) (e:
 21207393            let rewrite = rewriteIn bound
 10624394            let func = func block rewriteIn bound
 21207395            match mi.Name, args with
 14549396            | "Return", [ value ] -> rewrite value
 2068397            | "Zero", [] -> Expr.Value(())
 1138398            | "Combine", [ first; Call(_, d, [ Lambda(_, rest); _; _ ]) ] when d.Name = "Delay" ->
 1138399                Expr.Sequential(rewrite first, rewrite rest)
 400            // `for i in a .. b` over `int`: a counted loop, as F# compiles it outside a builder (#240).
 73401            | "For", [ PrimitiveRange(_, [ low; Value(step, _); high ]); Lambda(x, body) ] when x.Type = typeof<int> && 
 29402                Expr.Call(forRange, [ rewrite low; rewrite high; func [ x ] body ])
 403            | "For", [ items; Lambda(x, body) ] ->
 589404                let items = rewrite items
 589405                Expr.Call(forEach.MakeGenericMethod(x.Type), [ items; func [ x ] body ])
 9406            | "While", [ Lambda(_, guard); Call(_, d, [ Lambda(_, body); _; _ ]) ] when d.Name = "Delay" ->
 9407                Expr.Call(whileLoop, [ func [] guard; func [] body ])
 555408            | "TryWith", [ Call(_, d, [ Lambda(_, body); _; _ ]); Lambda(ex, handler) ] when d.Name = "Delay" ->
 555409                Expr.Call(tryWith.MakeGenericMethod(codeType e.Type), [ func [] body; func [ ex ] handler ])
 12410            | "TryFinally", [ Call(_, d, [ Lambda(_, body); _; _ ]); Lambda(_, compensation) ] when d.Name = "Delay" ->
 12411                Expr.Call(tryFinally.MakeGenericMethod(codeType e.Type), [ func [] body; func [] compensation ])
 412            | "Using", [ resource; Lambda(r, body) ] ->
 9413                Expr.Call(using.MakeGenericMethod(r.Type, codeType e.Type), [ rewrite resource; func [ r ] body ])
 414            // A nested dlr { } is compiled as part of this one: at run time its closure would be
 415            // made by our compiled code, not the F# compiler, so it has no reflected body of its own.
 1044416            | "Run", [ Call(_, d, [ Lambda(_, inner); _; _ ]) ] when d.Name = "Delay" -> rewrite inner
 0417            | name, _ -> unsupported (sprintf "the '%s' construct" name) e

Methods/Properties

NameFor(System.String)
get_Self()
Convert(System.Type,Microsoft.FSharp.Quotations.FSharpExpr)
isCode(System.Type)
codeType(System.Type)
defaultOf(System.Type)
asUnit(Microsoft.FSharp.Quotations.FSharpExpr)
isUnitCall(Microsoft.FSharp.Quotations.FSharpExpr)
capturing(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Quotations.FSharpExpr)
structPath(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
rooted@109(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,System.Boolean>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Collections.FSharpList`1<System.Reflection.FieldInfo>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpExpr>>)
apply@113(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Collections.FSharpList`1<System.Reflection.FieldInfo>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpExpr>>)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
asValue@119(Microsoft.FSharp.Quotations.FSharpExpr)
inPlace(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,System.Boolean>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpExpr)
fields(System.Type)
Invoke(System.Reflection.FieldInfo)
effectFree(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(System.Type)
primitive@185(System.Type)
elementField(System.Collections.Generic.IDictionary`2<System.String,System.Reflection.FieldInfo>,Microsoft.FSharp.Quotations.FSharpVar,System.Int32)
elements(System.Collections.Generic.IDictionary`2<System.String,System.Reflection.FieldInfo>,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpVar)
elementNameClashes(Microsoft.FSharp.Core.FSharpFunc`2<System.String,System.Boolean>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
go@239(System.Collections.Generic.HashSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
isAlias(Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpVar)
reached(System.Collections.Generic.IDictionary`2<System.String,System.Reflection.FieldInfo>,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>)
go@268-1(System.Collections.Generic.IDictionary`2<System.String,System.Reflection.FieldInfo>,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>)
ambiguous(Microsoft.FSharp.Quotations.FSharpVar)
isRefCell(System.Reflection.FieldInfo,System.Type)
read(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpVar)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
recovered@317(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
element(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Quotations.FSharpVar,System.Int32)
isCell(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Quotations.FSharpVar)
assign(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
go@370-2(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
rethrowing(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
func(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
call(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,System.Reflection.MethodInfo,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Quotations.FSharpExpr)