Skip to content

StateMachineHoistedLocalScopes rows are not aligned with the hoisted slots #4121

Description

@siegfriedpammer

Summary

StateMachineHoistedLocalScopes is indexed by slot number: a debugger parses N out of a state machine field name (<name>5__N, <>8__N) and reads row N-1 of the blob. PortablePdbWriter.BuildStateMachineHoistedLocalScopes (line 330) instead emits one row per surviving ILVariable that has a StateMachineField, ordered by field token, and gives every row the extent (0, CodeSize). That sequence has no relation to the slot numbering, so rows are misaligned, and where it is shorter than the highest slot the hoisted locals above the row count are dropped by the expression evaluator.

Reproduction

public static async Task<int> MixedAsync(IEnumerable<int> source)
{
	List<int> chunks = new List<int>();
	foreach (int value in source)
		chunks.Add(value);
	await Task.Yield();
	int total = 0;
	try
	{
		foreach (int chunk in chunks)
		{
			total += chunk;
			await Task.Yield();
		}
	}
	catch (InvalidOperationException e)
	{
		Console.WriteLine(e.Message);
	}
	return total;
}

State machine fields of <MixedAsync>d__0 (slots 1-7):

<>1__state, <>t__builder, source, <chunks>5__1, <total>5__2, <>s__3,
<value>5__4, <>s__5, <chunk>5__6, <e>5__7, <>u__1

Blob rows for <MixedAsync>d__0.MoveNext, start+length:

compiler:  0+24e, 0+24e, 0+0, 3c+25, 0+0, 124+8c, 1e8+1f
ILSpy:     0+24e, 0+24e, 0+24e, 0+24e, 0+24e, 0+24e, 0+24e

The compiler emits exactly one row per slot, in slot order, with an empty 0+0 placeholder for the synthesized <>s__N slots and the real live range for each user local. ILSpy's rows happen to number seven here, but they enumerate a different set (the hoisted parameter source is included, the <>s__N slots are not), so row N-1 does not describe slot N.

The counts diverge on real code: checking the PDB ILSpy generates against the highest user-visible slot in the binary reports a shortfall in 227 state machines of Newtonsoft.Json 13.0.4 and 2 of Microsoft.Extensions.Http 10.0.11. A shortfall means the hoisted locals whose slot exceeds the row count disappear from the Locals window entirely.

Note

#1230 lists StateMachineHoistedLocalScopes as unimplemented. It is emitted (PortablePdbWriter.cs lines 186-192), just not in the shape a consumer reads.

Posted by an AI agent (Claude) on Siegfried's behalf.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions