Skip to content

Reenable β-reduction of immediately-invoked F#-defined generic delegates #18401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
* Fix duplicate parse error reporting for GetBackgroundCheckResultsForFileInProject ([Issue #18379](https://github.com/dotnet/fsharp/issues/18379) [PR #18380](https://github.com/dotnet/fsharp/pull/18380))
* Fix MethodDefNotFound when compiling code invoking delegate with option parameter ([Issue #5171](https://github.com/dotnet/fsharp/issues/5171), [PR #18385](https://github.com/dotnet/fsharp/pull/18385))
* Fix #r nuget ..." downloads unneeded packages ([Issue #18231](https://github.com/dotnet/fsharp/issues/18231), [PR #18393](https://github.com/dotnet/fsharp/pull/18393))
* Reenable β-reduction and subsequent reoptimization of immediately-invoked F#-defined generic delegates. ([PR #18401](https://github.com/dotnet/fsharp/pull/18401))

### Added
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))
14 changes: 7 additions & 7 deletions src/Compiler/Optimize/Optimizer.fs
Original file line number Diff line number Diff line change
@@ -1734,9 +1734,9 @@ let TryEliminateBinding cenv _env bind e2 _m =

// Immediate consumption of delegate via an application in a sequential, e.g. 'let part1 = e in part1.Invoke(args); rest'
// See https://github.com/fsharp/fslang-design/blob/master/tooling/FST-1034-lambda-optimizations.md
| Expr.Sequential(DebugPoints(DelegateInvokeExpr g (delInvokeRef, delInvokeTy, DebugPoints (Expr.Val (VRefLocal vspec2, _, _), recreate2), delInvokeArg, _), recreate1), rest, NormalSeq, m)
| Expr.Sequential(DebugPoints(DelegateInvokeExpr g (delInvokeRef, delInvokeTy, tyargs, DebugPoints (Expr.Val (VRefLocal vspec2, _, _), recreate2), delInvokeArg, _), recreate1), rest, NormalSeq, m)
when IsUniqueUse vspec2 [rest;delInvokeArg] ->
let invoke = MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, recreate2 e1, delInvokeTy, delInvokeArg, m)
let invoke = MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, recreate2 e1, delInvokeTy, tyargs, delInvokeArg, m)
Some (Expr.Sequential(recreate1 invoke, rest, NormalSeq, m) |> recreate0)

// Immediate consumption of value by a pattern match 'let x = e in match x with ...'
@@ -2397,8 +2397,8 @@ let rec OptimizeExpr cenv (env: IncrementalOptimizationEnv) expr =

| Expr.App (f, fty, tyargs, argsl, m) ->
match expr with
| DelegateInvokeExpr g (delInvokeRef, delInvokeTy, delExpr, delInvokeArg, m) ->
OptimizeFSharpDelegateInvoke cenv env (delInvokeRef, delExpr, delInvokeTy, delInvokeArg, m)
| DelegateInvokeExpr g (delInvokeRef, delInvokeTy, tyargs, delExpr, delInvokeArg, m) ->
OptimizeFSharpDelegateInvoke cenv env (delInvokeRef, delExpr, delInvokeTy, tyargs, delInvokeArg, m)
| _ ->
let attempt =
if IsDebugPipeRightExpr cenv expr then
@@ -3799,18 +3799,18 @@ and OptimizeDebugPipeRights cenv env expr =
pipesExprR
expr, { pipesInfo with HasEffect=true}

and OptimizeFSharpDelegateInvoke cenv env (delInvokeRef, delExpr, delInvokeTy, delInvokeArg, m) =
and OptimizeFSharpDelegateInvoke cenv env (delInvokeRef, delExpr, delInvokeTy, tyargs, delInvokeArg, m) =
let g = cenv.g
let optf0, finfo = OptimizeExpr cenv env delExpr

match StripPreComputationsFromComputedFunction g optf0 [delInvokeArg] (fun f delInvokeArgsR -> MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, f, delInvokeTy, List.head delInvokeArgsR, m)) with
match StripPreComputationsFromComputedFunction g optf0 [delInvokeArg] (fun f delInvokeArgsR -> MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, f, delInvokeTy, tyargs, List.head delInvokeArgsR, m)) with
| Choice1Of2 remade ->
OptimizeExpr cenv env remade
| Choice2Of2 (newf0, remake) ->

let newDelInvokeArgs, arginfos = OptimizeExprsThenConsiderSplits cenv env [delInvokeArg]
let newDelInvokeArg = List.head newDelInvokeArgs
let reducedExpr = MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, newf0, delInvokeTy, newDelInvokeArg, m)
let reducedExpr = MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, newf0, delInvokeTy, tyargs, newDelInvokeArg, m)
let newExpr = reducedExpr |> remake
match newf0, reducedExpr with
| Expr.Obj _, Expr.Let _ ->
12 changes: 6 additions & 6 deletions src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
@@ -8476,9 +8476,9 @@ let (|NewDelegateExpr|_|) g expr =
[<return: Struct>]
let (|DelegateInvokeExpr|_|) g expr =
match expr with
| Expr.App ((Expr.Val (invokeRef, _, _)) as delInvokeRef, delInvokeTy, [], [delExpr;delInvokeArg], m)
| Expr.App ((Expr.Val (invokeRef, _, _)) as delInvokeRef, delInvokeTy, tyargs, [delExpr;delInvokeArg], m)
when invokeRef.LogicalName = "Invoke" && isFSharpDelegateTy g (tyOfExpr g delExpr) ->
ValueSome(delInvokeRef, delInvokeTy, delExpr, delInvokeArg, m)
ValueSome(delInvokeRef, delInvokeTy, tyargs, delExpr, delInvokeArg, m)
| _ -> ValueNone

[<return: Struct>]
@@ -8505,17 +8505,17 @@ let (|OpPipeRight3|_|) g expr =
ValueSome(resType, arg1, arg2, arg3, fExpr, m)
| _ -> ValueNone

let rec MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, delExpr, delInvokeTy, delInvokeArg, m) =
let rec MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, delExpr, delInvokeTy, tyargs, delInvokeArg, m) =
match delExpr with
| Expr.Let (bind, body, mLet, _) ->
mkLetBind mLet bind (MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, body, delInvokeTy, delInvokeArg, m))
| NewDelegateExpr g (_, argvs, body, m, _) when argvs.Length > 0 ->
mkLetBind mLet bind (MakeFSharpDelegateInvokeAndTryBetaReduce g (delInvokeRef, body, delInvokeTy, tyargs, delInvokeArg, m))
| NewDelegateExpr g (_, argvs & _ :: _, body, m, _) ->
let pairs, body = MultiLambdaToTupledLambdaIfNeeded g (argvs, delInvokeArg) body
let argvs2, args2 = List.unzip pairs
mkLetsBind m (mkCompGenBinds argvs2 args2) body
| _ ->
// Remake the delegate invoke
Expr.App (delInvokeRef, delInvokeTy, [], [delExpr; delInvokeArg], m)
Expr.App (delInvokeRef, delInvokeTy, tyargs, [delExpr; delInvokeArg], m)

//---------------------------------------------------------------------------
// Adjust for expected usage
6 changes: 4 additions & 2 deletions src/Compiler/TypedTree/TypedTreeOps.fsi
Original file line number Diff line number Diff line change
@@ -1438,7 +1438,9 @@ val MakeApplicationAndBetaReduce: TcGlobals -> Expr * TType * TypeInst list * Ex
/// Make a delegate invoke expression for an F# delegate type, doing beta reduction by introducing let-bindings
/// if the delegate expression is a construction of a delegate.
val MakeFSharpDelegateInvokeAndTryBetaReduce:
TcGlobals -> delInvokeRef: Expr * delExpr: Expr * delInvokeTy: TType * delInvokeArg: Expr * m: range -> Expr
TcGlobals ->
delInvokeRef: Expr * delExpr: Expr * delInvokeTy: TType * tyargs: TypeInst * delInvokeArg: Expr * m: range ->
Expr

/// Combine two static-resolution requirements on a type parameter
val JoinTyparStaticReq: TyparStaticReq -> TyparStaticReq -> TyparStaticReq
@@ -2751,7 +2753,7 @@ val (|NewDelegateExpr|_|): TcGlobals -> Expr -> (Unique * Val list * Expr * rang

/// Match a .Invoke on a delegate
[<return: Struct>]
val (|DelegateInvokeExpr|_|): TcGlobals -> Expr -> (Expr * TType * Expr * Expr * range) voption
val (|DelegateInvokeExpr|_|): TcGlobals -> Expr -> (Expr * TType * TypeInst * Expr * Expr * range) voption

/// Match 'if __useResumableCode then ... else ...' expressions
[<return: Struct>]
Original file line number Diff line number Diff line change
@@ -73,3 +73,16 @@ module ComputationExpressions =
compilation
|> getCompilation
|> verifyCompilation

[<Theory; FileInlineData("CustomCollectionBuilderComputationExpr.fs", Optimize=BooleanOptions.Both)>]
let ``CustomCollectionBuilderComputationExpr_fs_OptimizeOff`` compilation =
compilation
|> getCompilation
|> asExe
|> withReferences [
FsFromPath (Path.Combine (__SOURCE_DIRECTORY__, "CustomCollectionBuilderComputationExprLibrary.fs"))
|> withName "CustomCollectionBuilderComputationExprLibrary"
]
|> withEmbeddedPdb
|> withEmbedAllSource
|> verifyILBaseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ComputationExpressions.Program

let f0 () =
let xs = ResizeArray ()
xs.Add 1
xs.Add 2
xs.Add 3
xs

let xs = f0 ()

let f1 () = resizeArray { 1; 2; 3 }
let f2 () = resizeArray { yield! xs }
let f3 () = resizeArray { for x in xs -> x * x }
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@





.assembly extern runtime { }
.assembly extern FSharp.Core { }
.assembly extern assemblyLibrary
{
.ver 0:0:0:0
}
.assembly assembly
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32,
int32,
int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 )




.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module assembly.exe

.imagebase {value}
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001





.class public abstract auto ansi sealed ComputationExpressions.Program
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.field static assembly class [runtime]System.Collections.Generic.List`1<int32> xs@10
.custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )
.method public static class [runtime]System.Collections.Generic.List`1<int32> f0() cil managed
{

.maxstack 4
.locals init (class [runtime]System.Collections.Generic.List`1<int32> V_0)
IL_0000: newobj instance void class [runtime]System.Collections.Generic.List`1<int32>::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.1
IL_0008: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_000d: ldloc.0
IL_000e: ldc.i4.2
IL_000f: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_0014: ldloc.0
IL_0015: ldc.i4.3
IL_0016: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_001b: ldloc.0
IL_001c: ret
}

.method public specialname static class [runtime]System.Collections.Generic.List`1<int32> get_xs() cil managed
{

.maxstack 8
IL_0000: ldsfld class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::xs@10
IL_0005: ret
}

.method public static class [runtime]System.Collections.Generic.List`1<int32> f1() cil managed
{

.maxstack 4
.locals init (class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32> V_0,
class [runtime]System.Collections.Generic.List`1<int32> V_1,
class [runtime]System.Collections.Generic.List`1<int32>& V_2)
IL_0000: call class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<!0> class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32>::get_Instance()
IL_0005: stloc.0
IL_0006: newobj instance void class [runtime]System.Collections.Generic.List`1<int32>::.ctor()
IL_000b: stloc.1
IL_000c: ldloca.s V_1
IL_000e: stloc.2
IL_000f: ldloc.2
IL_0010: ldobj class [runtime]System.Collections.Generic.List`1<int32>
IL_0015: ldc.i4.1
IL_0016: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_001b: ldloca.s V_1
IL_001d: stloc.2
IL_001e: ldloc.2
IL_001f: ldobj class [runtime]System.Collections.Generic.List`1<int32>
IL_0024: ldc.i4.2
IL_0025: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_002a: ldloca.s V_1
IL_002c: stloc.2
IL_002d: ldloc.2
IL_002e: ldobj class [runtime]System.Collections.Generic.List`1<int32>
IL_0033: ldc.i4.3
IL_0034: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_0039: ldloc.1
IL_003a: ret
}

.method public static class [runtime]System.Collections.Generic.List`1<int32> f2() cil managed
{

.maxstack 4
.locals init (class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32> V_0,
class [runtime]System.Collections.Generic.List`1<int32> V_1,
class [runtime]System.Collections.Generic.List`1<int32>& V_2)
IL_0000: call class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<!0> class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32>::get_Instance()
IL_0005: stloc.0
IL_0006: newobj instance void class [runtime]System.Collections.Generic.List`1<int32>::.ctor()
IL_000b: stloc.1
IL_000c: ldloca.s V_1
IL_000e: stloc.2
IL_000f: ldloc.2
IL_0010: ldobj class [runtime]System.Collections.Generic.List`1<int32>
IL_0015: call class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::get_xs()
IL_001a: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::AddRange(class [runtime]System.Collections.Generic.IEnumerable`1<!0>)
IL_001f: ldloc.1
IL_0020: ret
}

.method public static class [runtime]System.Collections.Generic.List`1<int32> f3() cil managed
{

.maxstack 5
.locals init (class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32> V_0,
class [runtime]System.Collections.Generic.List`1<int32> V_1,
int32 V_2,
int32 V_3,
int32 V_4,
int32 V_5,
class [runtime]System.Collections.Generic.List`1<int32>& V_6)
IL_0000: call class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<!0> class [assemblyLibrary]ComputationExpressions.Library/ResizeArrayBuilder`1<int32>::get_Instance()
IL_0005: stloc.0
IL_0006: newobj instance void class [runtime]System.Collections.Generic.List`1<int32>::.ctor()
IL_000b: stloc.1
IL_000c: ldc.i4.0
IL_000d: stloc.3
IL_000e: call class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::get_xs()
IL_0013: callvirt instance int32 class [runtime]System.Collections.Generic.List`1<int32>::get_Count()
IL_0018: ldc.i4.1
IL_0019: sub
IL_001a: stloc.2
IL_001b: ldloc.2
IL_001c: ldloc.3
IL_001d: blt.s IL_004f

IL_001f: call class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::get_xs()
IL_0024: ldloc.3
IL_0025: callvirt instance !0 class [runtime]System.Collections.Generic.List`1<int32>::get_Item(int32)
IL_002a: stloc.s V_4
IL_002c: ldloc.s V_4
IL_002e: ldloc.s V_4
IL_0030: mul
IL_0031: stloc.s V_5
IL_0033: ldloca.s V_1
IL_0035: stloc.s V_6
IL_0037: ldloc.s V_6
IL_0039: ldobj class [runtime]System.Collections.Generic.List`1<int32>
IL_003e: ldloc.s V_5
IL_0040: callvirt instance void class [runtime]System.Collections.Generic.List`1<int32>::Add(!0)
IL_0045: ldloc.3
IL_0046: ldc.i4.1
IL_0047: add
IL_0048: stloc.3
IL_0049: ldloc.3
IL_004a: ldloc.2
IL_004b: ldc.i4.1
IL_004c: add
IL_004d: bne.un.s IL_001f

IL_004f: ldloc.1
IL_0050: ret
}

.method private specialname rtspecialname static void .cctor() cil managed
{

.maxstack 8
IL_0000: ldc.i4.0
IL_0001: stsfld int32 '<StartupCode$assembly>.$ComputationExpressions'.Program::init@
IL_0006: ldsfld int32 '<StartupCode$assembly>.$ComputationExpressions'.Program::init@
IL_000b: pop
IL_000c: ret
}

.method assembly specialname static void staticInitialization@() cil managed
{

.maxstack 8
IL_0000: call class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::f0()
IL_0005: stsfld class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::xs@10
IL_000a: ret
}

.property class [runtime]System.Collections.Generic.List`1<int32>
xs()
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 09 00 00 00 00 00 )
.get class [runtime]System.Collections.Generic.List`1<int32> ComputationExpressions.Program::get_xs()
}
}

.class private abstract auto ansi sealed '<StartupCode$assembly>.$ComputationExpressions'.Program
extends [runtime]System.Object
{
.field static assembly int32 init@
.custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )
.method public static void main@() cil managed
{
.entrypoint

.maxstack 8
IL_0000: call void ComputationExpressions.Program::staticInitialization@()
IL_0005: ret
}

}






Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
namespace ComputationExpressions

#nowarn 77

open System

[<AutoOpen>]
module Library =
type CollectionBuilderCode<'T> = delegate of byref<'T> -> unit

type CollectionBuilder () =
member inline _.Combine ([<InlineIfLambda>] f1 : CollectionBuilderCode<_>, [<InlineIfLambda>] f2 : CollectionBuilderCode<_>) =
CollectionBuilderCode (fun sm -> f1.Invoke &sm; f2.Invoke &sm)

member inline _.Delay ([<InlineIfLambda>] f : unit -> CollectionBuilderCode<_>) =
CollectionBuilderCode (fun sm -> (f ()).Invoke &sm)

member inline _.Zero () = CollectionBuilderCode (fun _ -> ())

member inline _.While ([<InlineIfLambda>] condition : unit -> bool, [<InlineIfLambda>] body : CollectionBuilderCode<_>) =
CollectionBuilderCode (fun sm ->
while condition () do
body.Invoke &sm)

member inline _.TryWith ([<InlineIfLambda>] body : CollectionBuilderCode<_>, [<InlineIfLambda>] handle : exn -> CollectionBuilderCode<_>) =
CollectionBuilderCode (fun sm ->
try body.Invoke &sm
with e -> (handle e).Invoke &sm)

member inline _.TryFinally ([<InlineIfLambda>] body : CollectionBuilderCode<_>, compensation : unit -> unit) =
CollectionBuilderCode (fun sm ->
try body.Invoke &sm
with _ ->
compensation ()
reraise ()
compensation ())

member inline builder.Using (disposable : #IDisposable, [<InlineIfLambda>] body : #IDisposable -> CollectionBuilderCode<_>) =
builder.TryFinally ((fun sm -> (body disposable).Invoke &sm), (fun () -> if not (isNull (box disposable)) then disposable.Dispose ()))

member inline _.For (resizeArray : ResizeArray<_>, [<InlineIfLambda>] body : _ -> CollectionBuilderCode<_>) =
CollectionBuilderCode (fun sm ->
for i in 0 .. resizeArray.Count - 1 do
(body resizeArray[i]).Invoke &sm)

member inline _.Yield x = CollectionBuilderCode (fun sm -> ignore (^a : (member Add : ^b -> _) (sm, x)))

member inline builder.YieldFrom xs = CollectionBuilderCode (fun sm -> ignore (^a : (member AddRange : ^b -> _) (sm, xs)))

[<Sealed>]
type ResizeArrayBuilder<'T> () =
inherit CollectionBuilder ()
static member val Instance = ResizeArrayBuilder<'T> ()
member inline _.Run ([<InlineIfLambda>] f : CollectionBuilderCode<_>) =
let mutable sm = ResizeArray<'T> ()
f.Invoke &sm
sm

let resizeArray<'T> = ResizeArrayBuilder<'T>.Instance
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Program

module DelegateImmediateInvoke1 =
type Foo = delegate of unit -> unit

let f () =
let f1 = Foo ignore
f1.Invoke ()

module DelegateImmediateInvoke2 =
type Foo = delegate of unit -> unit

let f () = Foo(ignore).Invoke()

module DelegateImmediateInvoke3 =
type Foo<'T> = delegate of 'T -> unit

let f () =
let f1 = Foo<unit> ignore
f1.Invoke(())

module DelegateImmediateInvoke4 =
type Foo<'T> = delegate of 'T -> unit

let f () = Foo<unit>(ignore).Invoke(())
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@





.assembly extern runtime { }
.assembly extern FSharp.Core { }
.assembly assembly
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32,
int32,
int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 )




.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module assembly.exe

.imagebase {value}
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001





.class public abstract auto ansi sealed Program
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class abstract auto ansi sealed nested public DelegateImmediateInvoke4
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo`1<T>
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke(!T A_1) runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(!T A_1,
class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke3
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo`1<T>
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke(!T A_1) runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(!T A_1,
class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke2
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke() runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.class abstract auto autochar serializable sealed nested assembly beforefieldinit specialname 'f@13-1'
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 )
.method assembly static void Invoke() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ldnull
IL_0001: ldftn void Program/DelegateImmediateInvoke2/'f@13-1'::Invoke()
IL_0007: newobj instance void Program/DelegateImmediateInvoke2/Foo::.ctor(object,
native int)
IL_000c: tail.
IL_000e: callvirt instance void Program/DelegateImmediateInvoke2/Foo::Invoke()
IL_0013: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke1
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke() runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.class abstract auto autochar serializable sealed nested assembly beforefieldinit specialname f@7
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 )
.method assembly static void Invoke() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ldnull
IL_0001: ldftn void Program/DelegateImmediateInvoke1/f@7::Invoke()
IL_0007: newobj instance void Program/DelegateImmediateInvoke1/Foo::.ctor(object,
native int)
IL_000c: tail.
IL_000e: callvirt instance void Program/DelegateImmediateInvoke1/Foo::Invoke()
IL_0013: ret
}

}

}

.class private abstract auto ansi sealed '<StartupCode$assembly>'.$Program
extends [runtime]System.Object
{
.method public static void main@() cil managed
{
.entrypoint

.maxstack 8
IL_0000: ret
}

}






Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@





.assembly extern runtime { }
.assembly extern FSharp.Core { }
.assembly assembly
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32,
int32,
int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 )




.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module assembly.exe

.imagebase {value}
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001





.class public abstract auto ansi sealed Program
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class abstract auto ansi sealed nested public DelegateImmediateInvoke4
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo`1<T>
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke(!T A_1) runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(!T A_1,
class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ldnull
IL_0001: pop
IL_0002: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke3
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo`1<T>
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke(!T A_1) runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(!T A_1,
class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.class abstract auto autochar serializable sealed nested assembly beforefieldinit specialname 'f1@19-1'
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 )
.method assembly static void Invoke(class [FSharp.Core]Microsoft.FSharp.Core.Unit delegateArg0) cil managed
{

.maxstack 8
IL_0000: ldarg.0
IL_0001: pop
IL_0002: ret
}

}

.method public static void f() cil managed
{

.maxstack 4
.locals init (class Program/DelegateImmediateInvoke3/Foo`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> V_0)
IL_0000: ldnull
IL_0001: ldftn void Program/DelegateImmediateInvoke3/'f1@19-1'::Invoke(class [FSharp.Core]Microsoft.FSharp.Core.Unit)
IL_0007: newobj instance void class Program/DelegateImmediateInvoke3/Foo`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit>::.ctor(object,
native int)
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: ldnull
IL_000f: tail.
IL_0011: callvirt instance void class Program/DelegateImmediateInvoke3/Foo`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit>::Invoke(!0)
IL_0016: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke2
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke() runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.class abstract auto autochar serializable sealed nested assembly beforefieldinit specialname f@13
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 )
.method assembly static void Invoke() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.method public static void f() cil managed
{

.maxstack 8
IL_0000: ldnull
IL_0001: ldftn void Program/DelegateImmediateInvoke2/f@13::Invoke()
IL_0007: newobj instance void Program/DelegateImmediateInvoke2/Foo::.ctor(object,
native int)
IL_000c: tail.
IL_000e: callvirt instance void Program/DelegateImmediateInvoke2/Foo::Invoke()
IL_0013: ret
}

}

.class abstract auto ansi sealed nested public DelegateImmediateInvoke1
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
.class auto ansi serializable sealed nested public Foo
extends [runtime]System.MulticastDelegate
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig strict virtual instance void Invoke() runtime managed
{
}

.method public hidebysig strict virtual
instance class [runtime]System.IAsyncResult
BeginInvoke(class [runtime]System.AsyncCallback callback,
object objects) runtime managed
{
}

.method public hidebysig strict virtual instance void EndInvoke(class [runtime]System.IAsyncResult result) runtime managed
{
}

}

.class abstract auto autochar serializable sealed nested assembly beforefieldinit specialname f1@7
extends [runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 )
.method assembly static void Invoke() cil managed
{

.maxstack 8
IL_0000: ret
}

}

.method public static void f() cil managed
{

.maxstack 4
.locals init (class Program/DelegateImmediateInvoke1/Foo V_0)
IL_0000: ldnull
IL_0001: ldftn void Program/DelegateImmediateInvoke1/f1@7::Invoke()
IL_0007: newobj instance void Program/DelegateImmediateInvoke1/Foo::.ctor(object,
native int)
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: tail.
IL_0010: callvirt instance void Program/DelegateImmediateInvoke1/Foo::Invoke()
IL_0015: ret
}

}

}

.class private abstract auto ansi sealed '<StartupCode$assembly>'.$Program
extends [runtime]System.Object
{
.method public static void main@() cil managed
{
.entrypoint

.maxstack 8
IL_0000: ret
}

}






Original file line number Diff line number Diff line change
@@ -41,6 +41,17 @@ module Inlining =
|> getCompilation
|> verifyCompilation

[<Theory; FileInlineData("FSharpDelegateBetaReduction.fs", Realsig=BooleanOptions.True, Optimize=BooleanOptions.Both)>]
let ``FSharpDelegateBetaReduction_fs`` compilation =
compilation
|> getCompilation
|> withOptions [ "--test:EmitFeeFeeAs100001" ]
|> asExe
|> withEmbeddedPdb
|> withEmbedAllSource
|> ignoreWarnings
|> verifyILBaseline

[<Fact>]
let ``List contains inlining`` () =
Fsx """module Test
3 changes: 2 additions & 1 deletion tests/FSharp.Test.Utilities/ILChecker.fs
Original file line number Diff line number Diff line change
@@ -60,8 +60,9 @@ module ILChecker =

let unifyRuntimeAssemblyName ilCode =
List.fold replace ilCode [
"\[System\.Runtime\]|\[System\.Console\]|\[System\.Runtime\.Extensions\]|\[mscorlib\]|\[System\.Memory\]", "[runtime]"
"\[System\.Runtime\]|\[System\.Console\]|\[System\.Runtime\.Extensions\]|\[mscorlib\]|\[System\.Memory\]|\[System\.Collections\]", "[runtime]"
"(\.assembly extern (System\.Runtime|System\.Console|System\.Runtime\.Extensions|mscorlib|System\.Memory)){1}([^\}]*)\}", ".assembly extern runtime { }"
"(\.assembly extern (System\.Collections)){1}([^\}]*)\}\\s+", ""
"(\.assembly extern (FSharp.Core)){1}([^\}]*)\}", ".assembly extern FSharp.Core { }" ]

let unifyImageBase ilCode = replace ilCode ("\.imagebase\s*0x\d*", ".imagebase {value}")