-
Notifications
You must be signed in to change notification settings - Fork 825
Open
Labels
Milestone
Description
Please provide a succinct description of the issue.
When using a CE, if a yielded item was constructed as a DU case in place, it prevents inlining of subsequent yields in that CE.
Provide the steps required to reproduce the problem:
type IntOrString =
| I of int
| S of string
type IntOrStringBuilder() =
member inline _.Zero() = ignore
member inline _.Yield(x: int) = fun (xs: ResizeArray<IntOrString>) -> xs.Add(I x)
member inline _.Yield(x: string) = fun (xs: ResizeArray<IntOrString>) -> xs.Add(S x)
member inline _.Yield(x: IntOrString) = fun (xs: ResizeArray<IntOrString>) -> xs.Add(x)
member inline _.Run([<InlineIfLambda>] f: ResizeArray<IntOrString> -> unit) =
let xs = ResizeArray<IntOrString>()
f xs
xs
member inline _.Delay([<InlineIfLambda>] f: unit -> ResizeArray<IntOrString> -> unit) =
fun (xs: ResizeArray<IntOrString>) -> f () xs
member inline _.Combine
(
[<InlineIfLambda>] f1: ResizeArray<IntOrString> -> unit,
[<InlineIfLambda>] f2: ResizeArray<IntOrString> -> unit
) =
fun (xs: ResizeArray<IntOrString>) ->
f1 xs
f2 xs
let builder = IntOrStringBuilder()
let test1 () =
builder {
1
"two"
3
"four"
}
let test2 () =
builder {
I 1
"two"
3
"four"
}
let test3 () =
builder {
1
"two"
I 3
"four"
}
let test4 () =
let a = 1
let b = S "two"
builder {
a
b
3
"four"
}
let xs1 = test1()
let xs2 = test2()
let xs3 = test3()
let xs4 = test4()
printfn "Test 1: %A" xs1
printfn "Test 2: %A" xs2
printfn "Test 3: %A" xs3
printfn "Test 4: %A" xs4
Expected behavior
It is expected that each of these examples results in essentially the same codegen
Actual behavior
(decompiled using dotPeek). test1
and test4
are inlined as expected:
public static List<Program.IntOrString> test1()
{
return new List<Program.IntOrString>()
{
Program.IntOrString.NewI(1),
Program.IntOrString.NewS("two"),
Program.IntOrString.NewI(3),
Program.IntOrString.NewS("four")
};
}
public static List<Program.IntOrString> test4()
{
Program.IntOrString intOrString = Program.IntOrString.NewS("two");
return new List<Program.IntOrString>()
{
Program.IntOrString.NewI(1),
intOrString,
Program.IntOrString.NewI(3),
Program.IntOrString.NewS("four")
};
}
test2
The first yield I 1
fails to inline and subsequent yields of primitives are inlined into the generated lambdas
public static List<Program.IntOrString> test2()
{
List<Program.IntOrString> intOrStringList = new List<Program.IntOrString>();
FSharpFunc<Unit, List<Program.IntOrString>>.InvokeFast<Unit>((FSharpFunc<Unit, FSharpFunc<List<Program.IntOrString>, Unit>>) Program.test2\u004038.\u0040_instance, (Unit) null, intOrStringList);
return intOrStringList;
}
internal sealed class test2\u004038\u002D1 : FSharpFunc<List<Program.IntOrString>, Unit>
{
public Program.IntOrString x;
internal test2\u004038\u002D1(Program.IntOrString x) => this.x = x;
public override Unit Invoke(List<Program.IntOrString> xs)
{
xs.Add(this.x);
return (Unit) null;
}
}
internal sealed class test2\u004038\u002D2 : FSharpFunc<List<Program.IntOrString>, Unit>
{
public FSharpFunc<List<Program.IntOrString>, Unit> f1;
internal test2\u004038\u002D2(FSharpFunc<List<Program.IntOrString>, Unit> f1) => this.f1 = f1;
public override Unit Invoke(List<Program.IntOrString> xs)
{
this.f1.Invoke(xs);
xs.Add(Program.IntOrString.NewS("two"));
xs.Add(Program.IntOrString.NewI(3));
xs.Add(Program.IntOrString.NewS("four"));
return (Unit) null;
}
}
internal sealed class test2\u004038 : FSharpFunc<Unit, FSharpFunc<List<Program.IntOrString>, Unit>>
{
internal static readonly Program.test2\u004038 \u0040_instance = new Program.test2\u004038();
[CompilerGenerated]
[DebuggerNonUserCode]
internal test2\u004038()
{
}
public override FSharpFunc<List<Program.IntOrString>, Unit> Invoke(Unit unitVar)
{
return (FSharpFunc<List<Program.IntOrString>, Unit>) new Program.test2\u004038\u002D2((FSharpFunc<List<Program.IntOrString>, Unit>) new Program.test2\u004038\u002D1(Program.IntOrString.NewI(1)));
}
}
test3
the last yield fails to inline
public static List<Program.IntOrString> test3()
{
List<Program.IntOrString> intOrStringList = new List<Program.IntOrString>();
intOrStringList.Add(Program.IntOrString.NewI(1));
intOrStringList.Add(Program.IntOrString.NewS("two"));
intOrStringList.Add(Program.IntOrString.NewI(3));
FSharpFunc<Unit, List<Program.IntOrString>>.InvokeFast<Unit>((FSharpFunc<Unit, FSharpFunc<List<Program.IntOrString>, Unit>>) Program.test3\u004049.\u0040_instance, (Unit) null, intOrStringList);
return intOrStringList;
}
internal sealed class test3\u004049\u002D1 : FSharpFunc<List<Program.IntOrString>, Unit>
{
public Program.IntOrString x;
internal test3\u004049\u002D1(Program.IntOrString x) => this.x = x;
public override Unit Invoke(List<Program.IntOrString> xs)
{
xs.Add(this.x);
return (Unit) null;
}
}
internal sealed class test3\u004049 : FSharpFunc<Unit, FSharpFunc<List<Program.IntOrString>, Unit>>
{
internal static readonly Program.test3\u004049 \u0040_instance = new Program.test3\u004049();
internal test3\u004049()
{
}
public override FSharpFunc<List<Program.IntOrString>, Unit> Invoke(Unit unitVar)
{
return (FSharpFunc<List<Program.IntOrString>, Unit>) new Program.test3\u004049\u002D1(Program.IntOrString.NewS("four"));
}
}
Known workarounds
test4
shows that creating the DU value outside the CE allows for inlining but this is not intuitive
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
New