Optimization inlining applied inconsistently with piping
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 131
Description
**Repro steps**
I have a simple F# program. I expect in all cases the lambdas should be inlined but they don't seem to be for reasons that I can't make sense of.
```fsharp
// Simple PushStream
type 'T PushStream = ('T -> bool) -> bool
let inline ofArray (vs : _ array) : _ PushStream = fun ([] r) ->
let mutable i = 0
while i < vs.Length && r vs.[i] do
i <- i + 1
i = vs.Length
let inline fold ([] f) z ([] ps : _ PushStream) =
let mutable s = z
let _ = ps (fun v -> s <- f s v; true)
s
// It turns out that if we pipe using |> the F# compiler don't inlines
// the lambdas as we like it to
// So define a more restrictive version of |> that applies function f to a function v
// As both f and v are restricted to lambas we can apply InlineIfLambda
let inline (|>>) ([] v : _ -> _) ([] f : _ -> _) = f v
let values = [|0..10000|]
let thisIsInlined1 () = ofArray values |>> fold (+) 0
let thisIsInlined2 () =
let vs = [|0..10000|]
ofArray vs |>> fold (+) 0
let thisIsNotInlined1 () = ofArray [|0..10000|] |>> fold (+) 0
type Test() =
let _values = [|0..10000|]
static let _svalues = [|0..10000|]
let array vs =
ofArray vs |>> fold (+) 0
member x.thisIsInlined2 () = ofArray values |>> fold (+) 0
member x.thisIsInlined3 () = array _values
member x.thisIsNotInlined2 () = ofArray _values |>> fold (+) 0
member x.thisIsNotInlined3 () = ofArray _svalues |>> fold (+) 0
```
**Expected behavior**
All `thisIs*` methods should have similar inlined version of the data pipeline.
**Actual behavior**
All `thisIsInlined*` methods have similar inlined version of the data pipeline.
All `thisIsNotInlined*` methods have non-inlined code which is how it looks like if we didn't specify `InlineIfLambda`
Example decompiled:
```csharp
public static int thisIsInlined2()
{
int[] vs = SeqModule.ToArray(Operators.CreateSequence(Operators.OperatorIntrinsics.RangeInt32(0, 1, 10000)));
int num = 0;
int num2 = 0;
for (;;)
{
bool flag;
if (num2 < vs.Length)
{
int num3 = vs[num2];
num += num3;
flag = true;
}
else
{
flag = false;
}
if (!flag)
{
break;
}
num2++;
}
bool flag2 = num2 == vs.Length;
return num;
}
public static int thisIsNotInlined1()
{
int[] vs = SeqModule.ToArray(Operators.CreateSequence(Operators.OperatorIntrinsics.RangeInt32(0, 1, 10000)));
FSharpFunc, bool> fsharpFunc = new Program.thisIsNotInlined1@27(vs);
FSharpRef fsharpRef = new FSharpRef(0);
fsharpFunc.Invoke(new Program.thisIsNotInlined1@27-1(fsharpRef));
return fsharpRef.contents;
}
```
But the only difference is storing the generated source array in a variable or not:
```fsharp
let thisIsInlined2 () =
let vs = [|0..10000|]
ofArray vs |>> fold (+) 0
let thisIsNotInlined1 () = ofArray [|0..10000|] |>> fold (+) 0
```
Perhaps there are good reasons for this but I don't understand them.
**Known workarounds**
Before invoking functions that uses `InlineIfLambda` make sure the input args to them (except the lambdas) are stored in intermediate bindings.
**Related information**
* Operating system : Windows 10
* .NET Runtime kind : .NET 6 : 6.0.100
* Editing Tools: Visual Studio 2022
Contributor guide
Assessment
This issue has not been assessed yet.