From 2a3279d6279911d4b3f394784bb992f4e67592a5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 5 Jul 2026 08:51:46 +0200 Subject: [PATCH] Keep ref iterator loops as while when live after loop 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 --- .../IL/Transforms/HighLevelLoopTransform.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 78e413826..83000fe10 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -395,6 +395,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // - increment block if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3) return false; + if (StoresRefLocalUsedAfterLoop(loop, incrementBlock)) + return false; context.Step("Transform to for loop: " + loop.EntryPoint.Label, loop); // move the block to the end of the loop: loop.Blocks.MoveElementToEnd(incrementBlock); @@ -467,6 +469,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + static bool StoresRefLocalUsedAfterLoop(BlockContainer loop, Block incrementBlock) + { + foreach (var store in incrementBlock.Instructions.SkipLast(1).SelectMany(inst => inst.Descendants).OfType()) + { + var variable = store.Variable; + if (variable.Type.IsByRefLike && IsVariableUsedAfterLoop(loop, variable)) + return true; + } + return false; + } + + static bool IsVariableUsedAfterLoop(BlockContainer loop, ILVariable variable) + { + foreach (var use in variable.LoadInstructions.Concat(variable.AddressInstructions).Concat(variable.StoreInstructions.Cast())) + { + if (loop.GetCommonParent(use) is Block { Kind: BlockKind.ControlFlow } && loop.IsBefore(use)) + return true; + } + return false; + } + bool IsAssignment(ILInstruction inst) { if (inst is StLoc)