From ca307f5886024b4e1df2570c0f22aafd80ab5df4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 6 Jul 2026 20:15:29 +0200 Subject: [PATCH] Return an enum from the sequential store offset matcher The bool-plus-out-abortTransform contract encoded three outcomes in two flags, so false meant either 'sequence ended' or 'reject the transform' depending on the flag. A three-value enum names each outcome at the return site, and passing minExpectedOffset by ref makes visible that only the binary.add path advances the expected offset (the bare ldloc path previously echoed it back through an out parameter). Assisted-by: Claude:claude-fable-5:Claude Code --- .../Transforms/TransformArrayInitializers.cs | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 85fbe01c8..165141c41 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -462,13 +462,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms { break; } - if (!TryGetSequentialStoreOffset(target, store, elementType, minExpectedOffset, out var offset, out var abortTransform)) - { - if (abortTransform) - return false; + var match = GetSequentialStoreOffset(target, store, elementType, ref minExpectedOffset); + if (match == SequentialStoreMatch.Abort) + return false; + if (match != SequentialStoreMatch.Matched) break; - } - minExpectedOffset = offset; if (values == null) { var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true); @@ -529,34 +527,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - static bool TryGetSequentialStoreOffset(ILInstruction target, ILVariable store, IType elementType, long minExpectedOffset, out long offset, out bool abortTransform) + enum SequentialStoreMatch { - offset = 0; - abortTransform = false; + // The store belongs to the initializer sequence. + Matched, + // The store is not part of the sequence; it ends the scan, keeping what was matched. + SequenceEnd, + // The store's shape is unexpected; reject the whole transform. + Abort, + } + static SequentialStoreMatch GetSequentialStoreOffset(ILInstruction target, ILVariable store, IType elementType, ref long minExpectedOffset) + { // stobj T(ldloc store, value) writes at the current expected offset, initially 0. if (target.MatchLdLoc(store)) - { - offset = minExpectedOffset; - return true; - } + return SequentialStoreMatch.Matched; // stobj T(binary.add(ldloc store, offset), value) // The offset is either sizeof(T) or an element index multiplied by sizeof(T). if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) - { - abortTransform = true; - return false; - } + return SequentialStoreMatch.Abort; if (!left.MatchLdLoc(store)) - return false; + return SequentialStoreMatch.SequenceEnd; var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow); if (offsetInst == null) - { - abortTransform = true; - return false; - } - return offsetInst.MatchLdcI(out offset) && offset >= 0 && offset >= minExpectedOffset; + return SequentialStoreMatch.Abort; + if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset) + return SequentialStoreMatch.SequenceEnd; + minExpectedOffset = offset; + return SequentialStoreMatch.Matched; } ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type)