< Summary - FSharp.Interop.Dlr

Information
Class: FSharp.Interop.Dlr.TranslatePatterns
Assembly: FSharp.Interop.Dlr
File(s): /home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/TranslatePatterns.fs
Line coverage
97%
Covered lines: 215
Uncovered lines: 5
Coverable lines: 220
Total lines: 527
Line coverage: 97.7%
Branch coverage
94%
Covered branches: 577
Total branches: 610
Branch coverage: 94.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
unsupported(...)100%11100%
genericDef(...)100%22100%
opMethod(...)100%11100%
|New|_|(...)100%1414100%
Invoke(...)100%22100%
Invoke(...)100%11100%
|PrimitiveRange|_|(...)100%88100%
Invoke(...)100%44100%
one@126(...)100%44100%
|TypeOfList|_|(...)92.85%2828100%
|TypeArgs|_|(...)97.22%3636100%
|StaticTarget|_|(...)92.85%1414100%
|Op|_|(...)100%66100%
|NamedOf|_|(...)100%66100%
|ArgsOf|_|(...)100%66100%
isSplat(...)100%44100%
|OutMarker|_|(...)100%2020100%
|RefMarker|_|(...)100%1212100%
isByRefMarker(...)100%44100%
|UnaryOp|_|(...)100%1010100%
|BinaryOp|_|(...)100%1212100%
|Literal|_|(...)100%22100%
|Unboxed|(...)100%44100%
Invoke(...)100%1212100%
peel@259(...)100%1212100%
|NamedRecord|_|(...)100%22100%
|EtaReduced|(...)92.85%1414100%
Invoke(...)90%2020100%
peel@283-1(...)90%2020100%
retuples(...)100%11100%
|MemberOp|_|(...)100%4646100%
splitArgs(...)100%44100%
isPure(...)61.53%622662.5%
hoists(...)100%22100%
hoists(...)100%22100%
sequenced(...)100%11100%
Invoke(...)100%22100%
place@360(...)100%22100%
letDefinition(...)100%1212100%
Invoke(...)100%22100%
lambdaParams@413(...)100%22100%
Invoke(...)0%506220%
chain@420(...)100%2222100%
Invoke(...)0%7280%
applications@417(...)100%88100%
Invoke(...)81.81%2222100%
applied@436(...)100%2222100%
Invoke(...)66.66%6683.33%
appliedArgument@444(...)100%66100%
Invoke(...)52.38%2634250%
search@452(...)83.33%434291.66%
parameterArgument(...)100%11100%
Invoke(...)100%88100%
occurrences@478(...)100%1212100%
Invoke(...)100%22100%
spineOccurrences@486(...)100%22100%
Invoke(...)100%44100%
pure'@490(...)100%44100%
Invoke(...)90%101083.33%
apply@491(...)90%101083.33%
Invoke(...)100%44100%
applied@501-1(...)100%44100%
normalize(...)91.66%6060100%

File(s)

/home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/TranslatePatterns.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/// <summary>The state machine's data slot; the builder's <c>Return</c> writes it so the value stays live in the compile
 14/// <remarks>Not part of the supported API: public only because compiled blocks call it, and it may change in any releas
 15[<Struct; NoComparison; NoEquality; System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Nev
 16type DlrData<'T> =
 17    [<DefaultValue(false)>]
 18    val mutable Result: 'T
 19
 20/// <summary>Where a block is: its outermost <c>Delay</c>, run while this thread is probing, records
 21/// its caller's file and line here and returns without running the body. Thread-static rather
 22/// than in <c>DlrData</c>: a field there would grow every state machine, and the boxed copy of it
 23/// a compiled loop or <c>try</c> makes per call.</summary>
 24/// <remarks>Not part of the supported API: public only because compiled blocks call it, and it may change in any releas
 25[<Sealed; AbstractClass; System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
 26type DlrLocation =
 27    [<ThreadStatic; DefaultValue>]
 28    static val mutable private probing: bool
 29    [<ThreadStatic; DefaultValue>]
 30    static val mutable private file: string
 31    [<ThreadStatic; DefaultValue>]
 32    static val mutable private line: int
 33
 34    /// Whether this thread is probing for a block's location.
 35    static member Probing = DlrLocation.probing
 36
 37    /// The probed block's answer.
 38    static member Answer(file: string, line: int) =
 39        DlrLocation.file <- file
 40        DlrLocation.line <- line
 41
 42    /// Runs `run` (the block's code) as a probe and returns what its Delay answered.
 43    static member internal Probe(run: unit -> unit) : struct (string * int) =
 44        DlrLocation.probing <- true
 45        DlrLocation.file <- null
 46        DlrLocation.line <- -1
 47        try
 48            run ()
 49            struct (DlrLocation.file, DlrLocation.line)
 50        finally
 51            DlrLocation.probing <- false
 52            DlrLocation.file <- null
 53
 54/// <summary>The compiled block over its state machine, by reference: no copy of the struct at the call,
 55/// none of the delegate-with-a-struct-argument cost a <c>Func&lt;'SM, 'T&gt;</c> has (measured 4x slower).
 56/// Public, and not for direct use: an F# <c>internal</c> delegate's <c>Invoke</c> is internal too, and
 57/// .NET Framework's <c>Expression.Lambda</c> finds <c>Invoke</c> by public lookup only (#125).</summary>
 58/// <remarks>Not part of the supported API: public only because compiled blocks call it, and it may change in any releas
 59[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
 60type DlrReader<'SM, 'T> = delegate of inref<'SM> -> 'T
 61
 62/// The translator's view of a reflected body: the markers (`opMethod`), the member-operation
 63/// patterns, evaluation order (`sequenced`) and `normalize`, which applies a piped or curried
 64/// marker to its arguments before any other rewrite.
 65module internal TranslatePatterns =
 66    let unsupported (what: string) (e: Expr) =
 9067        raise (DlrTranslationException(sprintf "dlr { } does not support %s: %A" what e))
 68
 69    let genericDef (mi: Reflection.MethodInfo) =
 371970870        if mi.IsGenericMethod then mi.GetGenericMethodDefinition() else mi
 71
 72    /// The generic definition of the method a marker quotation calls. Curried static members
 73    /// quote as applications of an inner lambda, so this looks for the first call anywhere.
 12974    let opMethod (e: Expr<_>) = genericDef (Quotation.methodOf e)
 75
 76    let opDynamic = opMethod <@ fun (t: obj) (n: string) -> ((?) t n) : obj @>
 77    let opDynamicAssign = opMethod <@ fun (t: obj) (n: string) (v: obj) -> (?<-) t n v @>
 78    let opCall = opMethod <@ fun (t: obj) -> (Dlr.call t) : obj @>
 79    let opApply = opMethod <@ fun (a: obj) (t: obj) -> (Dlr.apply a t) : obj @>
 80    let opNamed = opMethod <@ fun (r: obj) -> Dlr.named r @>
 81    let opNamedOf = opMethod <@ fun (l: (string * obj) list) -> Dlr.namedOf l @>
 82    let opArgsOf = opMethod <@ fun (l: obj list) -> Dlr.argsOf l @>
 83    /// `Dlr.new'<T>(a, b, …)`: the type and the arguments (each unboxed to its static type).
 84    let (|New|_|) (e: Expr) =
 60715285        match e with
 9354086        | Call(None, mi, args) when mi.DeclaringType = typeof<Dlr> && mi.Name = "new'" ->
 324087            Some(mi.GetGenericArguments().[0], [ for a in args -> match a with Coerce(inner, t) when t = typeof<obj> -> 
 60669088        | _ -> None
 89    let opCast = opMethod <@ fun (v: obj) -> Dlr.cast<obj> v @>
 90    let opImplicit = opMethod <@ fun (v: obj) -> (Dlr.implicit v) : obj @>
 91    let opGet = opMethod <@ fun (n: string) (t: obj) -> (Dlr.get n t) : obj @>
 92    let opSet = opMethod <@ fun (n: string) (v: obj) (t: obj) -> Dlr.set n v t @>
 93    let opAddAssign = opMethod <@ fun (n: string) (v: obj) (t: obj) -> Dlr.addAssign n v t @>
 94    let opSubtractAssign = opMethod <@ fun (n: string) (v: obj) (t: obj) -> Dlr.subtractAssign n v t @>
 95    let opInvoke = opMethod <@ fun (n: string) (a: obj) (t: obj) -> (Dlr.invoke n a t) : obj @>
 96    let unaryOps =
 97        dict [
 98            opMethod <@ fun (v: obj) -> (Dlr.neg v) : obj @>, ExpressionType.Negate
 99            opMethod <@ fun (v: obj) -> (Dlr.not v) : obj @>, ExpressionType.Not
 100            opMethod <@ fun (v: obj) -> (Dlr.complement v) : obj @>, ExpressionType.OnesComplement
 101        ]
 102    let forEach = opMethod <@ fun (items: seq<obj>) (body: Func<obj, unit>) -> DlrRuntime.forEach items body @>
 103    let whileLoop = opMethod <@ fun (guard: Func<bool>) (body: Func<unit>) -> DlrRuntime.whileLoop guard body @>
 104    let forRange = opMethod <@ fun (low: int) (high: int) (body: Func<int, unit>) -> DlrRuntime.forRange low high body @
 105    let tryWith = opMethod <@ fun (body: Func<obj>) (handler: Func<exn, obj>) -> DlrRuntime.tryWith body handler @>
 106    let tryFinally = opMethod <@ fun (body: Func<obj>) (fin: Func<unit>) -> DlrRuntime.tryFinally body fin @>
 107    let rethrow = opMethod <@ fun (e: exn) -> (DlrRuntime.rethrow e : obj) @>
 108    let reraiseMethod = typeof<unit>.Assembly.GetType("Microsoft.FSharp.Core.Operators").GetMethod("Reraise")
 109    let using = opMethod <@ fun (r: IDisposable) (body: Func<IDisposable, obj>) -> DlrRuntime.using r body @>
 110
 111    /// FSharp.Core's typed ranges (`RangeInt32`, …, `RangeChar`) by element type.
 112    let private typedRanges =
 113        typeof<unit>.Assembly.GetType("Microsoft.FSharp.Core.Operators+OperatorIntrinsics").GetMethods()
 735114        |> Array.filter (fun m -> m.Name.StartsWith "Range" && not m.IsGenericMethodDefinition)
 42115        |> Array.map (fun m -> m.ReturnType.GetGenericArguments().[0], m)
 116        |> dict
 117
 118    /// A range on a primitive, `a .. b` or `a .. step .. b`, as the typed range the compiler inlines
 119    /// it to. Compiled from a quotation, the generic `op_Range`/`op_RangeStep` runs FSharp.Core's
 120    /// non-inlined form instead, whose every step is a dynamic addition and comparison, a few
 121    /// hundred bytes per element (#240).
 122    let (|PrimitiveRange|_|) (e: Expr) =
 117704123        match e with
 57560124        | Call(None, mi, args) when mi.IsGenericMethod && mi.DeclaringType.FullName = "Microsoft.FSharp.Core.Operators" 
 5963125            let t = mi.GetGenericArguments().[0]
 126            let one () =
 302127                if t = typeof<nativeint> then box 1n
 296128                elif t = typeof<unativeint> then box 1un
 284129                else Convert.ChangeType(1, t)
 5963130            match mi.Name, args, typedRanges.TryGetValue t with
 158131            | "op_Range", [ low; high ], (true, m) when m.GetParameters().Length = 2 -> Some(m, [ low; high ])   // Rang
 146132            | "op_Range", [ low; high ], (true, m) -> Some(m, [ low; Expr.Value(one (), t); high ])
 72133            | "op_RangeStep", [ low; step; high ], (true, m) when step.Type = t && m.GetParameters().Length = 3 -> Some(
 5787134            | _ -> None
 105810135        | _ -> None
 136    let opItem = opMethod <@ fun (i: obj) (t: obj) -> (Dlr.item i t) : obj @>
 137    let opSetItem = opMethod <@ fun (i: obj) (v: obj) (t: obj) -> Dlr.setItem i v t @>
 138
 139    let binaryOps =
 140        dict [
 141            opMethod <@ fun (l: obj) (r: obj) -> (l ?%? r) : obj @>, ExpressionType.Modulo
 142            opMethod <@ fun (l: obj) (r: obj) -> (l ?*? r) : obj @>, ExpressionType.Multiply
 143            opMethod <@ fun (l: obj) (r: obj) -> (l ?+? r) : obj @>, ExpressionType.Add
 144            opMethod <@ fun (l: obj) (r: obj) -> (l ?-? r) : obj @>, ExpressionType.Subtract
 145            opMethod <@ fun (l: obj) (r: obj) -> (l ?/? r) : obj @>, ExpressionType.Divide
 146            opMethod <@ fun (l: obj) (r: obj) -> (l ?&&&? r) : obj @>, ExpressionType.And
 147            opMethod <@ fun (l: obj) (r: obj) -> (l ?|||? r) : obj @>, ExpressionType.Or
 148            opMethod <@ fun (l: obj) (r: obj) -> (l ?^^^? r) : obj @>, ExpressionType.ExclusiveOr
 149            opMethod <@ fun (l: obj) (r: obj) -> (l ?<<<? r) : obj @>, ExpressionType.LeftShift
 150            opMethod <@ fun (l: obj) (r: obj) -> (l ?>>>? r) : obj @>, ExpressionType.RightShift
 151            opMethod <@ fun (l: obj) (r: obj) -> l ?<=? r @>, ExpressionType.LessThanOrEqual
 152            opMethod <@ fun (l: obj) (r: obj) -> l ?<>? r @>, ExpressionType.NotEqual
 153            opMethod <@ fun (l: obj) (r: obj) -> l ?<? r @>, ExpressionType.LessThan
 154            opMethod <@ fun (l: obj) (r: obj) -> l ?=? r @>, ExpressionType.Equal
 155            opMethod <@ fun (l: obj) (r: obj) -> l ?>? r @>, ExpressionType.GreaterThan
 156            opMethod <@ fun (l: obj) (r: obj) -> l ?>=? r @>, ExpressionType.GreaterThanOrEqual
 157        ]
 158
 159    /// A literal `[ typeof<A>; typeof<B>; … ]`.
 160    let rec (|TypeOfList|_|) (e: Expr) =
 27569161        match e with
 84162        | NewUnionCase(empty, []) when empty.Name = "Empty" -> Some []
 144163        | NewUnionCase(cons, [ Call(None, mi, []); TypeOfList rest ]) when cons.Name = "Cons" && mi.Name = "TypeOf" && m
 48164            Some(mi.GetGenericArguments().[0] :: rest)
 27485165        | _ -> None
 166
 167    /// The explicit type arguments of a call: known at translation time (`Dlr.typeArgs<A, B>()`,
 168    /// or `Dlr.typeArgsOf` with a literal list of `typeof`s, which folds to the same), or an
 169    /// expression evaluated per call (`Dlr.typeArgsOf ts`), which keys a SiteCache like a
 170    /// computed name does.
 171    type TypeArgsSpec =
 172        | StaticTypes of Type list
 173        | RuntimeTypes of Expr
 174
 175    let (|TypeArgs|_|) (e: Expr) =
 212044176        match e with
 2730177        | Call(None, mi, []) when mi.DeclaringType = typeof<Dlr> && mi.Name = "typeArgs" -> Some(StaticTypes(List.ofArra
 96178        | Call(None, mi, [ TypeOfList ts ]) when mi.DeclaringType = typeof<Dlr> && mi.Name = "typeArgsOf" -> Some(Static
 9313179        | Call(None, mi, [ types ]) when mi.DeclaringType = typeof<Dlr> && mi.Name = "typeArgsOf" -> Some(RuntimeTypes t
 211906180        | _ -> None
 181
 182    /// `Static<T>.Overloads` as a target: the type.
 183    let (|StaticTarget|_|) (e: Expr) =
 218090184        match e with
 20400185        | PropertyGet(None, pi, []) when pi.Name = "Overloads" && pi.DeclaringType.IsGenericType && pi.DeclaringType.Get
 216186            Some(pi.DeclaringType.GetGenericArguments().[0])
 217874187        | _ -> None
 188
 189    let (|Op|_|) (def: Reflection.MethodInfo) (e: Expr) =
 11817531190        match e with
 1946654191        | Call(None, mi, args) when genericDef mi = def -> Some args
 11703523192        | _ -> None
 193
 194    /// `Dlr.namedOf pairs`: the list expression.
 195    let (|NamedOf|_|) (e: Expr) =
 19330196        match e with
 20304197        | Op opNamedOf [ pairs ] -> Some pairs
 18356198        | _ -> None
 199
 200    /// `Dlr.argsOf values`: the list expression.
 201    let (|ArgsOf|_|) (e: Expr) =
 18084202        match e with
 19122203        | Op opArgsOf [ values ] -> Some values
 17046204        | _ -> None
 205
 206    /// Either splat marker.
 10786207    let isSplat (e: Expr) = match e with NamedOf _ | ArgsOf _ -> true | _ -> false
 208
 209    /// `Dlr.out` / `Dlr.outAs<'T> ()`: an out argument, its value returned in the result tuple; with
 210    /// the type the out states (`outAs`), or None for the result's shape to infer.
 211    let (|OutMarker|_|) (e: Expr) =
 227525212        match e with
 35226213        | PropertyGet(None, p, []) when p.DeclaringType = typeof<Dlr> && p.Name = "out" -> Some None
 3180214        | Call(None, mi, []) when mi.DeclaringType = typeof<Dlr> && mi.Name = "outAs" -> Some(Some(mi.GetGenericArgument
 219327215        | _ -> None
 216
 217    /// `Dlr.ref v`: a ref argument over the variable `v`.
 218    let (|RefMarker|_|) (e: Expr) =
 220829219        match e with
 10975220        | Call(None, mi, [ v ]) when mi.DeclaringType = typeof<Dlr> && mi.Name = "ref" -> Some v
 220523221        | _ -> None
 222
 18054223    let isByRefMarker (e: Expr) = match e with OutMarker _ | RefMarker _ -> true | _ -> false
 224    let unboxTo = opMethod <@ fun (o: obj) -> unbox<int> o @>
 225
 226    let (|UnaryOp|_|) (e: Expr) =
 201190227        match e with
 228        | Call(None, mi, [ v ]) ->
 8048229            match unaryOps.TryGetValue(genericDef mi) with
 30230            | true, op -> Some(op, v)
 8018231            | _ -> None
 193142232        | _ -> None
 233
 234    let (|BinaryOp|_|) (e: Expr) =
 201550235        match e with
 236        | Call(None, mi, [ l; r ]) ->
 18902237            match binaryOps.TryGetValue(genericDef mi) with
 360238            | true, op -> Some(op, l, r)
 18542239            | _ -> None
 182648240        | _ -> None
 241
 242    let (|Literal|_|) (e: Expr) =
 26298243        match e with
 22354244        | Value(o, _) -> Some o
 3944245        | _ -> None
 246
 247
 248    /// Strips the boxing F# inserts on the way to an `obj` parameter, so the binder can see the
 249    /// real static type.
 250    let (|Unboxed|) (e: Expr) =
 61764251        match e with
 696252        | Coerce(inner, t) when t = typeof<obj> -> inner
 61416253        | _ -> e
 254
 255    /// `Dlr.named {| a = x; b = y |}`: the field names and values. F# evaluates the fields in
 256    /// source order through `let` temporaries and then builds the record in its own (sorted)
 257    /// field order, so the temporaries come back as bindings to wrap around the call.
 258    let (|NamedRecord|_|) (e: Expr) =
 259        let rec peel (bindings: (Var * Expr) list) (e: Expr) =
 762260            match e with
 54261            | Let(v, value, body) -> peel ((v, value) :: bindings) body
 1062262            | Op opNamed [ inner ] -> peel bindings inner
 263            | NewRecord(recordType, values) ->
 342264                let fields = FSharpType.GetRecordFields recordType
 2014265                Some(List.rev bindings, List.zip [ for f in fields -> f.Name ] values)
 12266            | other -> unsupported "Dlr.named applied to anything but an anonymous record literal" other
 21225267        match e with
 21579268        | Op opNamed _ -> peel [] e
 20871269        | _ -> None
 270
 271    /// The compiler eta-expands a dynamic member used as a statement:
 272    /// `let clo = x?Foo in fun a -> clo a` applied to `()`. Fold it back to `x?Foo`.
 273    let rec (|EtaReduced|) (e: Expr) =
 29159274        match e with
 18275        | Let(v, value, Lambda(x, Application(Var v', Var x'))) when v = v' && x = x' -> value
 29153276        | _ -> e
 277
 278    /// `let a0 = x.0 in … let an = x.n in v (a0, …, an)`: the lambda body of the tupled
 279    /// eta-expansion the compiler makes of a call with a tuple of arguments on a target whose
 280    /// static type is not `obj` — a struct or class expression, a typed variable — (`let clo =
 281    /// (f ())?M in fun tupledArg -> …` applied to the tuple).
 282    let retuples (v: Var) (x: Var) (body: Expr) =
 283        let rec peel (elements: Var list) (e: Expr) =
 96284            match e with
 144285            | Let(a, TupleGet(Var x', i), rest) when x' = x && i = elements.Length -> peel (elements @ [ a ]) rest
 18286            | Application(Var v', NewTuple items) when v' = v ->
 72287                items.Length = elements.Length && List.forall2 (fun (item: Expr) (a: Var) -> item = Expr.Var a) items el
 30288            | _ -> false
 48289        peel [] body
 290
 291    /// One of the member operations, whichever way it was spelled: `x?Name` / `(?) x name` /
 292    /// `Dlr.get name x`, those applied to arguments (`x?Name(a)`, `(Dlr.get name x)(a)`) or
 293    /// `Dlr.invoke name a x`, and `x?Name <- v` / `Dlr.set name v x`. Target, name
 294    /// expression, and the argument expression (for an invocation) or value (for a set).
 295    type MemberOp =
 296        | GetMember of target: Expr * name: Expr
 297        | InvokeMember of target: Expr * name: Expr * args: Expr
 298        | SetMember of target: Expr * name: Expr * value: Expr
 299
 300    let (|MemberOp|_|) (e: Expr) =
 1345792301        match e with
 28330302        | Application(EtaReduced(Op opDynamic [ Unboxed target; name ]), args) -> Some(InvokeMember(target, name, args))
 1383112303        | Op opDynamic [ Unboxed target; name ] -> Some(GetMember(target, name))
 1290534304        | Op opDynamicAssign [ Unboxed target; name; value ] -> Some(SetMember(target, name, value))
 8574305        | Application(EtaReduced(Op opGet [ name; Unboxed target ]), args) -> Some(InvokeMember(target, name, args))
 1287246306        | Op opGet [ name; Unboxed target ] -> Some(GetMember(target, name))
 604443307        | Op opInvoke [ name; args; Unboxed target ] -> Some(InvokeMember(target, name, args))
 604317308        | Op opSet [ name; value; Unboxed target ] -> Some(SetMember(target, name, value))
 604281309        | _ -> None
 310
 311    /// `x?Foo()` applies unit; `x?Foo(a, b)` applies a tuple; `x?Foo(a)` applies one value. As in
 312    /// F#'s own method calls (`w.Add args` with `args: int * int`), an expression whose static
 313    /// type is a reference tuple is several arguments too: it is bound once and split with
 314    /// `TupleGet`, each element keeping its static type. A struct tuple is one value, as in F#;
 315    /// `box t` passes a tuple as one dynamic argument. Returns the binding, if any, and the items.
 316    let splitArgs (e: Expr) : (Var * Expr) list * Expr list =
 10818317        match e with
 318        // `()`, or a `unit`-typed variable (a generic parameter instantiated to unit, `let args = ()`):
 319        // no arguments, and nothing to evaluate.
 2610320        | Value(_, t) when t = typeof<unit> -> [], []
 531321        | Var v when v.Type = typeof<unit> -> [], []
 322        // A unit-valued expression: evaluated for its effect, and no arguments. A void call
 323        // cannot be let-bound (the converter has no value for it), so `()` follows it.
 5185324        | e when e.Type = typeof<unit> -> [ Var("effect", typeof<unit>), Expr.Sequential(e, Expr.Value(())) ], []
 3103325        | NewTuple items -> [], items
 2046326        | _ when FSharpType.IsTuple e.Type && not e.Type.IsValueType ->
 42327            let v = Var("args", e.Type)
 252328            [ v, e ], [ for i in 0 .. FSharpType.GetTupleElements(e.Type).Length - 1 -> Expr.TupleGet(Expr.Var v, i) ]
 1956329        | single -> [], [ single ]
 330
 331    /// Whether evaluating `e` has no effect and no order to keep: an immutable variable, a
 332    /// literal, a lambda, a read-only field, a tuple or coercion of such. A mutable (a captured
 333    /// `let mutable`, a mutable field) is not: an argument may assign it. Anything else is bound
 334    /// in source order by `sequenced`.
 335    let rec isPure (e: Expr) =
 5222336        match e with
 1194337        | Var v -> not v.IsMutable
 2066338        | Value _ | Lambda _ | StaticTarget _ -> true
 0339        | FieldGet(None, f) -> f.IsInitOnly || f.IsLiteral
 0340        | FieldGet(Some inner, f) -> f.IsInitOnly && isPure inner
 96341        | Coerce(inner, _) | TupleGet(inner, _) -> isPure inner
 0342        | NewTuple items -> List.forall isPure items
 1866343        | _ -> false
 344
 345    /// Whether a form's arguments make it hoist something ahead of its site call: a `Dlr.named`
 346    /// record's field temporaries, a splat list, a tuple bound by `splitArgs`.
 347    let hoists (tupleBindings: (Var * Expr) list) (argExprs: Expr list) =
 1766348        not tupleBindings.IsEmpty
 15073349        || argExprs |> List.exists (fun a -> match a with NamedOf _ | ArgsOf _ -> true | NamedRecord(lets, _) -> not let
 350
 351    /// C#'s evaluation order — the target, then the arguments left to right — kept where a form
 352    /// hoists something ahead of its site call (`hoists`), which would otherwise run first:
 353    /// every impure expression is bound to a variable in source order, so the hoisted ones take
 354    /// their own place. The tuple of `splitArgs` goes after the target, where the arguments are;
 355    /// a computed name or type list (`keys`) between the target and the arguments, where
 356    /// `(?) x name args` writes them. Returns the bindings, the variables to add to the bound
 357    /// set, and the target, keys and arguments rewritten over the variables.
 358    let sequenced (target: Expr option) (keys: Expr list) (tupleBindings: (Var * Expr) list) (argExprs: Expr list) =
 2384359        let bindings = ResizeArray<Var * Expr>()
 360        let place (name: string) (e: Expr) =
 8178361            if isPure e then e
 362            else
 1842363                let v = Var(name, e.Type)
 1842364                bindings.Add((v, e))
 1842365                Expr.Var v
 3574366        let target' = target |> Option.map (place "target")
 3692367        let keys' = keys |> List.mapi (fun i k -> place (sprintf "key%d" i) k)
 2384368        bindings.AddRange tupleBindings
 369        let args' =
 3574370            argExprs |> List.mapi (fun i a ->
 4838371                match a with
 2384372                // The record's temporaries at the record's place; the record itself then holds
 2384373                // only variables and pure values.
 66374                | NamedRecord(lets, _) when not lets.IsEmpty ->
 2384375                    let rec strip (e: Expr) =
 90376                        match e with
 30377                        | Let(_, _, body) -> strip body
 30378                        | Call(None, mi, [ inner ]) -> Expr.Call(mi, [ strip inner ])
 30379                        | e -> e
 30380                    bindings.AddRange lets
 30381                    strip a
 36382                | NamedRecord _ -> a
 782383                | Call(None, mi, [ list ]) when isSplat a -> Expr.Call(mi, [ place (sprintf "list%d" i) list ])
 2384384                // A byref marker stays where it is: the translator reads a ref's variable at the
 2384385                // call, after every other argument (a C# ref is a reference: the callee sees each
 2384386                // argument's write to it).
 2222387                | OutMarker _ | RefMarker _ -> a
 4594388                | a -> place (sprintf "arg%d" i) a)
 4764389        let bound = bindings |> Seq.map fst |> Set.ofSeq
 2384390        List.ofSeq bindings, bound, target', keys', args'
 391
 392    /// The definition of a let-bound variable somewhere in `e` (quotation Vars are identity-based,
 393    /// so shadowing is not a concern). A `let rec` one is its whole group, `let rec … in v`: its
 394    /// own references stay bound to it rather than recovered again (#198).
 395    let rec letDefinition (v: Var) (e: Expr) : Expr option =
 4242801396        match e with
 321018397        | Let(v', def, _) when v' = v -> Some def
 1533398        | LetRecursive(bindings, _) when bindings |> List.exists (fun (v', _) -> v' = v) -> Some(Expr.LetRecursive(bindi
 1006611399        | ShapeVar _ -> None
 881571400        | ShapeLambda(_, body) -> letDefinition v body
 3083379401        | ShapeCombination(_, args) -> args |> List.tryPick (letDefinition v)
 402
 403    // `|>` is inlined inside a quotation literal, so these come from reflection instead.
 404    let fsharpOperators = typeof<obj list>.Assembly.GetType("Microsoft.FSharp.Core.Operators")
 405    let pipeRight = fsharpOperators.GetMethod("op_PipeRight")
 406    let pipeLeft = fsharpOperators.GetMethod("op_PipeLeft")
 407
 408    /// The argument a parameter of a let-bound local function takes, when the member body
 409    /// applies that function exactly once — or of a lambda applied on the spot,
 410    /// `(fun k -> …) 1`: the optimizer inlines such a function at its call site, so its
 411    /// parameters become that call's arguments and are never captured.
 412    let parameterArgument (v: Var) (memberBody: Expr) : Expr option =
 413        let rec lambdaParams (e: Expr) =
 156987414            match e with
 125628415            | Lambda(p, body) -> let ps, inner = lambdaParams body in p :: ps, inner
 94173416            | _ -> [], e
 417        let rec applications (f: Var) (e: Expr) : Expr list list =
 418            // Full application chains f a1 a2 … of `f`, innermost first; a pipe is one more
 419            // application, `a |> f` or `f <| a` (#252).
 420            let rec chain (e: Expr) (acc: Expr list) =
 28147421                match e with
 1724422                | Application(inner, arg) -> chain inner (arg :: acc)
 27462423                | Op pipeRight [ arg; inner ] -> chain inner (arg :: acc)
 25390424                | Op pipeLeft [ inner; arg ] -> chain inner (arg :: acc)
 7014425                | Var f' when f' = f -> Some acc
 24940426                | _ -> None
 25378427            match chain e [] with
 876428            | Some args when not args.IsEmpty -> [ args ]
 429            | _ ->
 24940430                match e with
 5750431                | ShapeVar _ -> []
 5106432                | ShapeLambda(_, body) -> applications f body
 14084433                | ShapeCombination(_, args) -> args |> List.collect (applications f)
 434        /// `(fun p1 p2 -> body) a1 a2`: the lambda chain and its arguments, in order. A pipe is
 435        /// the same application, `a |> (fun p -> …)` or `(fun p -> …) <| a` (#252).
 436        let rec applied (e: Expr) (acc: Expr list) =
 147619437            match e with
 53457438            | Application(inner, arg) -> applied inner (arg :: acc)
 117643439            | Op pipeRight [ arg; f ] -> applied f (arg :: acc)
 70711440            | Op pipeLeft [ f; arg ] -> applied f (arg :: acc)
 74776441            | Lambda _ when not acc.IsEmpty -> Some(e, acc)
 33263442            | _ -> None
 443        /// The argument `v` takes in a lambda applied on the spot, if `e` is one binding `v`.
 444        let appliedArgument (e: Expr) : Expr option =
 70651445            match applied e [] with
 446            | Some(f, args) ->
 37388447                let ps, _ = lambdaParams f
 37388448                match List.tryFindIndex ((=) v) ps with
 26756449                | Some i when i < args.Length -> Some args.[i]
 24010450                | _ -> None
 33263451            | None -> None
 452        let rec search (e: Expr) : Expr option =
 798583453            match e with
 113048454            | Let(f, def, rest) when (let ps, _ = lambdaParams def in List.contains v ps) ->
 261455                let ps, _ = lambdaParams def
 261456                let index = List.findIndex ((=) v) ps
 261457                match applications f rest with
 282458                | [ args ] when args.Length > index -> Some args.[index]
 0459                | [] -> None
 120460                | _ -> None
 1653941461            | (Application _ | Op pipeRight _ | Op pipeLeft _) when (appliedArgument e).IsSome -> appliedArgument e
 184674462            | ShapeVar _ -> None
 163139463            | ShapeLambda(_, body) -> search body
 444090464            | ShapeCombination(_, args) -> args |> List.tryPick search
 9103465        search memberBody
 466
 467
 468    /// `x |> f` in a reflected body is `op_PipeRight(x, let name = "A" in fun target -> …)`, and a
 469    /// curried marker applied to its arguments is `Application(Lambda(name, Lambda(target, …)), …)`.
 470    /// Apply such functions to their arguments and inline `let`s of literals and variables, so
 471    /// `w |> Dlr.get "A"` becomes the plain `Dlr.get "A" w` call the member-op patterns recognise,
 472    /// with the name a literal and a tuple argument still a tuple. An argument is substituted for
 473    /// its parameter only when that cannot change what runs: it is a variable or a literal, or it
 474    /// is used exactly once and not under a lambda (where it would run per invocation). Otherwise
 475    /// it is let-bound, so it is evaluated once — including when the parameter is never used.
 476    let rec normalize (e: Expr) : Expr =
 477        /// Occurrences of `v`, with any occurrence under a lambda counted as many.
 478        let rec occurrences (v: Var) (e: Expr) =
 40052479            match e with
 17558480            | Var v' when v' = v -> 1
 7594481            | ShapeVar _ -> 0
 6492482            | ShapeLambda(_, body) -> occurrences v body * 2
 31476483            | ShapeCombination(_, args) -> args |> List.sumBy (occurrences v)
 484        /// The same, but lambdas on the spine of a curried function (`fun a -> fun b -> …`, each
 485        /// applied exactly once by the next argument) do not count as "under a lambda".
 486        let rec spineOccurrences (v: Var) (e: Expr) =
 3162487            match e with
 274488            | Lambda(_, body) -> spineOccurrences v body
 2888489            | _ -> occurrences v e
 12832490        let pure' (x: Expr) = match x with Var v -> not v.IsMutable | Value _ -> true | _ -> false
 491        let rec apply (f: Expr) (x: Expr) : Expr =
 6080492            match f with
 1194493            | Let(v, value, body) -> Expr.Let(v, value, apply body x)
 494            | Lambda(p, body) ->
 34072495                if pure' x || spineOccurrences p body = 1 then body.Substitute(fun v -> if v = p then Some x else None)
 496                // A `unit` argument may compile to a void site call, which cannot be let-bound:
 497                // run it, then the body with `()` for the parameter.
 1086498                elif x.Type = typeof<unit> then Expr.Sequential(x, body.Substitute(fun v -> if v = p then Some(Expr.Valu
 1062499                else Expr.Let(p, x, body)
 0500            | _ -> Expr.Application(f, x)
 501        let applied (f: Expr) (x: Expr) =
 11614502            match normalize f, normalize x with
 4886503            | (Lambda _ | Let _ as f), x -> normalize (apply f x)
 6728504            | f, x -> Expr.Application(f, x)
 412773505        match e with
 415133506        | Op pipeRight [ x; f ] -> applied f x
 410419507        | Op pipeLeft [ f; x ] -> applied f x
 508        // The tupled eta-expansion of a member call: back to the member applied to the tuple,
 509        // before the tuple would be let-bound under the lambda and its markers lost.
 66510        | Application(Let(v, value, Lambda(x, body)), arg) when retuples v x body -> applied value arg
 9200511        | Application(f, x) -> applied f x
 512        // A `let` of a literal or an immutable variable is inlined; a snapshot of a mutable
 513        // (`let y = n` with `n` mutable) is not, since `n` may change before `y` is used.
 21988514        | Let(v, (Value _ | Var _ as value), body) when not v.IsMutable && pure' value -> normalize (body.Substitute(fun
 515        // The eta-expanded statement form `let clo = x?Foo in clo ()` once its lambda is applied.
 60516        | Let(v, value, Application(Var v', arg)) when v = v' -> applied value arg
 517        // A parameterless delegate literal (`Func<int>(fun () -> 7)`, `Action(fun () -> …)`): F#
 518        // quotes it with no parameter and a bare body, a node FSharp.Core takes apart as the lambda
 519        // `fun () -> body` but rebuilds only from the bare body, so no rewrite can pass it through
 520        // (#156). It becomes `ParameterlessLiteral<D, R>.Of (fun () -> body)`, an ordinary call
 521        // that makes the delegate by its own type.
 522        | NewDelegate(t, [], body) ->
 204523            let body = normalize body
 204524            let thunk = Expr.Lambda(Var("unitVar", typeof<unit>), body)
 204525            let literal = typedefof<ParameterlessLiteral<_, _>>.MakeGenericType(t, body.Type)
 204526            Expr.Call(literal.GetMethod("Of"), [ thunk ])
 399437527        | _ -> Quotation.rebuild normalize e

Methods/Properties

unsupported(System.String,Microsoft.FSharp.Quotations.FSharpExpr)
genericDef(System.Reflection.MethodInfo)
opMethod(Microsoft.FSharp.Quotations.FSharpExpr`1<a>)
|New|_|(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(System.Reflection.MethodInfo)
Invoke(System.Reflection.MethodInfo)
|PrimitiveRange|_|(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Core.Unit)
one@126(System.Type,Microsoft.FSharp.Core.Unit)
|TypeOfList|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|TypeArgs|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|StaticTarget|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|Op|_|(System.Reflection.MethodInfo,Microsoft.FSharp.Quotations.FSharpExpr)
|NamedOf|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|ArgsOf|_|(Microsoft.FSharp.Quotations.FSharpExpr)
isSplat(Microsoft.FSharp.Quotations.FSharpExpr)
|OutMarker|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|RefMarker|_|(Microsoft.FSharp.Quotations.FSharpExpr)
isByRefMarker(Microsoft.FSharp.Quotations.FSharpExpr)
|UnaryOp|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|BinaryOp|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|Literal|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|Unboxed|(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Quotations.FSharpExpr)
peel@259(Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Quotations.FSharpExpr)
|NamedRecord|_|(Microsoft.FSharp.Quotations.FSharpExpr)
|EtaReduced|(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
peel@283-1(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
retuples(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
|MemberOp|_|(Microsoft.FSharp.Quotations.FSharpExpr)
splitArgs(Microsoft.FSharp.Quotations.FSharpExpr)
isPure(Microsoft.FSharp.Quotations.FSharpExpr)
hoists(Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
hoists(Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
sequenced(Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>,Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
Invoke(System.String,Microsoft.FSharp.Quotations.FSharpExpr)
place@360(System.Collections.Generic.List`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr>>,System.String,Microsoft.FSharp.Quotations.FSharpExpr)
letDefinition(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
lambdaParams@413(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
chain@420(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
Invoke(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
applications@417(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
applied@436(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpExpr>)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
appliedArgument@444(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
search@452(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
parameterArgument(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
occurrences@478(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
spineOccurrences@486(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
pure'@490(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
apply@491(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
applied@501-1(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
normalize(Microsoft.FSharp.Quotations.FSharpExpr)