Invariants that involve types (stack type of a variable against its IType,
the element type of an array access, the operand types of a comparison)
need a type system to resolve them against, and the only correct one is the
type system the instruction tree was decoded with. Until now CheckInvariant
took only the phase, so such a check had no compilation to use:
DeconstructInstruction.CheckInvariant called IsAssignment with a null type
system, which only held up because the targets it sees are ldloc, whose
InferType never touches the compilation; a ldflda-wrapped or pointer target
would have failed inside the invariant instead of reporting a violation.
Every call site already has that type system in scope: the ILReader's
compilation, the ILTransformContext of the running transform, or the
decompiler's own IDecompilerTypeSystem. It is now passed explicitly and the
base implementation asserts it is present, so a future invariant can rely
on it without re-plumbing the callers.
Assisted-by: Claude:claude-fable-5:Claude Code
The loop-shape decision can use ILVariable use lists before AST lowering, so avoid creating a for-loop when its iterator updates a byref local that must remain usable after the loop.
Assisted-by: OpenAI:openai/gpt-5.5:OpenCode
This avoids recursively un-registering e.g. all LdLocs from their ILVariable.LoadInstructions, etc. (all the ILInstruction.Disconnected logic). This speeds up the example from #1193 by another factor 2.
This allows reverting the changes to HighLevelLoopTransform+ReduceNestingTransform from the previous commit, which fixes a bug in loop detection (the previous commit did not handle loops where the loop BlockContainer didn't have a Block as parent).