Description
Description
I've got a scenario where the debugger consistently reports wrong values for the loop variable, accessing the foreach loop value of the next iteration. The runtime seems to lag behind one iteration, while the debugger shows value of the variable it would have in the next iteration. This makes debugging confusing and very hard if you happen to trigger it.
The problem happens every run, I've reduced the code triggering the issue below (some code comparing assemblies tripped into this bug, originally using Mono.Cecil, but moving to plain reflection observes the same behavior given the same structure of code)
Reproduction Steps
using System.Diagnostics;
using System.Reflection;
await BugRepro(typeof(System.Collections.Immutable.ImmutableDictionary<,>.Enumerator), null);
// method must be async for the bug to trigger
static async Task BugRepro(Type type, Type? comparingType)
{
const BindingFlags f = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
foreach (var field in type.GetFields(f))
{
// repeating the assert from below is necessary to trigger the bug
if (comparingType?.GetFields(f).SingleOrDefault(x => x.Name == field.Name) is null)
continue;
// original code did more stuff but could remove it
}
foreach (var field in type.GetFields(f))
{
// inspecting debugger variables, this should be false (can even set a breakpoint and step through)
var fieldContainsGenericParameter = field.FieldType.ContainsGenericParameters;
if (fieldContainsGenericParameter)
{
// inspecting debugger variables, this assert should have triggered
Debug.Assert(field.Name != "_enumeratingBuilderVersion");
// inspect debugger variables mentioned above
Debugger.Break();
// repeating the assert is necessary to trigger the bug
Debug.Assert(comparingType?.GetFields(f).SingleOrDefault(x => x.Name == field.Name) is null);
}
}
}
Expected behavior
the foreach loop variable shown in the debugger should match its actual value the runtime is using
Actual behavior
the debugger shows the foreach loop variable of the next iteration and bases all its displayed information off of that
in particular the debugger shows us being in the if-block for the field _enumeratingBuilderVersion
which has type Int32 for which ContainsGenericParameters
is false, yet the runtime assigns fieldContainsGenericParameter
true (which it is for the previous field in the iteration)
Regression?
no, repros in Desktop Framework and VS 2019 as well as .NET 7 and 8 under VS 2022
Known Workarounds
none known, once the code is structured to trigger the issue, the debugger seems to consistently report wrong values
Configuration
x64 Windows 10 22H2 19045.3448
.NET Framework 4.8.9181.0
.NET 7.0.11
.NET 8.0.100 rc.1.23455.8
VS 2019 16.11.30
VS 2022 17.8.0 Preview 2.0
Other information
No response