dotnet / dotnet/fsharp

Incorrect codegen for Debug build with srtp and mutable struct

Open
#16,292 1 comment 0 reactions 0 assignees View on GitHub
Area-Compiler-CodeGen Bug Impact-Low
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 11h
Merged PRs (30d)
131

Description

The showIt function in the following has incorrect code in Debug builds (the bug exists in both F# 7 and 8):
```
open System
open System.Buffers
open System.Text

let inline forEach<'C, 'E, 'I
when 'C: (member GetEnumerator: unit -> 'I)
and 'I: struct
and 'I: (member MoveNext: unit -> bool)
and 'I: (member Current : 'E) >
([] f: 'E -> unit) (container: 'C) =
let mutable iter = container.GetEnumerator()
while iter.MoveNext() do
f iter.Current

let showIt (buffer: ReadOnlySequence) =
buffer |> forEach (fun segment ->
let s = Encoding.ASCII.GetString(segment.Span)
Console.Write(s)
)
```

Using ILSpy, here's a (correct) Release build:
```
public static void showIt(ReadOnlySequence buffer)
{
ReadOnlySequence.Enumerator enumerator = buffer.GetEnumerator();
while (enumerator.MoveNext())
{
ReadOnlyMemory current = enumerator.Current;
Console.Write(Encoding.ASCII.GetString(current.Span));
}
}
```

Below is the incorrect Debug build. The problem is that `enumerator2` is always reinitialized in the loop (using the value of `enumerator`). `enumerator2.MoveNext()` will then mutate `enumerator2`, but this mutation will be lost when `enumerator2` is reinitialized:
```
public static void showIt(ReadOnlySequence buffer)
{
ReadOnlySequence readOnlySequence = buffer;
ReadOnlySequence.Enumerator enumerator = readOnlySequence.GetEnumerator();
while (true)
{
ReadOnlySequence.Enumerator enumerator2 = enumerator;
if (enumerator2.MoveNext())
{
ReadOnlyMemory segment = enumerator.Current;
string s = Encoding.ASCII.GetString(segment.Span);
Console.Write(s);
continue;
}
break;
}
}
```

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.