dotnet / dotnet/fsharp

Optimisation failure with structs in referenced projects

Open
#3,923 2 comments 0 reactions 0 assignees View on GitHub
Area-Compiler-Optimization Bug Impact-Medium
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 11h
Merged PRs (30d)
131

Description

Structs referenced from other projects have a significant optimisation failure whereby an instance of the struct cannot be passed to functions - it is always re-instantiated to empty just before the function call.

#### Repro steps

Imagine you have the following C# project with a single file:

```c#
namespace ClassLibrary1
{
public struct SomeStruct
{
private string _value;
public string Value => _value;

public void Set(string value)
{
_value = value;
}
}
}
```

The above project is referenced from F#:

```f#
open ClassLibrary1

let someFunc (s : SomeStruct) =
printfn "From inner: %A" s.Value

[]
let main argv =

let s = SomeStruct()
s.Set("there")
printfn "Pre call: %A" s.Value
someFunc s
printfn "Post call: %A" s.Value

0
```

#### Expected behavior

Output of the program is:

```
Pre call: "there"
From inner: "there"
Post call: "there"
```

#### Actual behavior

**With optimisations enabled (e.g. Release mode)**, the output is:

```
Pre call: "there"
From inner:
Post call: "there"
```

#### Known workarounds

No known workaround.

#### Related information

Severe bug, stopping me from using a C# library that uses structs.

The IL for the main function looks like:

```msil
.method public static int32 main(string[] argv) cil managed
{
.entrypoint
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor() = ( 01 00 00 00 )
// Code size 40 (0x28)
.maxstack 4
.locals init ([0] valuetype [ClassLibrary1]ClassLibrary1.SomeStruct s,
[1] valuetype [ClassLibrary1]ClassLibrary1.SomeStruct V_1)
IL_0000: nop
IL_0001: ldloca.s V_1
IL_0003: initobj [ClassLibrary1]ClassLibrary1.SomeStruct
IL_0009: ldloc.1
IL_000a: stloc.0
IL_000b: ldloca.s s
IL_000d: ldstr "there"
IL_0012: call instance void [ClassLibrary1]ClassLibrary1.SomeStruct::Set(string)
IL_0017: ldloca.s V_1 // WHY IS THIS HERE?
IL_0019: initobj [ClassLibrary1]ClassLibrary1.SomeStruct // WHY IS THIS HERE?
IL_001f: ldloc.1
IL_0020: call void Program::someFunc(valuetype [ClassLibrary1]ClassLibrary1.SomeStruct)
IL_0025: nop
IL_0026: ldc.i4.0
IL_0027: ret
} // end of method Program::main
```

IL_0017 and IL_0019 have been added by the optimiser - in Debug mode these instructions are not here, and the bad behaviour is not present.

Thanks

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.