From 9aa3911f3aac8a71864e5e8be0d1444e6c55a151 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 30 Aug 2026 22:55:07 +0200 Subject: [PATCH] Remove definition-chasing pointer recovery in DeconstructInstruction.IsAssignment GetPointerElementType existed because a pointer passing through a stack slot could be typed IntPtr: ILReader replaced the slot type with FindType(StackType) whenever the inferred type did not match the stack type. With InferType() implemented on every ILInstruction that fallback is gone (FlushExpressionStack now asserts the inferred type is stack-accurate), so the target's inferred type is precise and the definition chain no longer needs to be walked. Merged stack slots were never recovered by the helper anyway (it required a single store). Disabling the PointerType arm makes the uint*/byte* deconstruction fixtures fail, so the sign-agnostic stobj.Type fallback remains guarded. Assisted-by: Claude:claude-fable-5:Claude Code --- .../IL/Instructions/DeconstructInstruction.cs | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs index 5382d07db..b7c9b3855 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs @@ -256,17 +256,14 @@ namespace ICSharpCode.Decompiler.IL { return false; } - if (stobj.Target.InferType(typeSystem) is ByReferenceType brt) - expectedType = brt.ElementType; - else - { - // Pointer targets do not infer a ByReferenceType. stobj.Type cannot stand - // in for the element type: it comes from the store opcode, which is - // sign-agnostic, so a uint* and an int* both report int32 and a byte* - // reports sbyte. Recover the declared type instead, and only fall back to - // the store where the target is not a pointer at all. - expectedType = GetPointerElementType(stobj.Target, typeSystem) ?? stobj.Type; - } + // stobj.Type comes from the store opcode and is sign-agnostic (an int* and a + // uint* target both store int32), so for pointer targets the element type of + // the target's inferred type must be used instead. + expectedType = stobj.Target.InferType(typeSystem) switch { + ByReferenceType brt => brt.ElementType, + PointerType ptr => ptr.ElementType, + _ => stobj.Type + }; value = stobj.Value; return true; default: @@ -274,28 +271,6 @@ namespace ICSharpCode.Decompiler.IL } } - /// - /// The element type of a pointer-typed target. A pointer passing through a stack slot - /// is typed IntPtr there, so the declared type has to be taken from the definition the - /// slot was filled from. Returns null if the target is not a pointer. - /// - static IType GetPointerElementType(ILInstruction target, ICompilation typeSystem) - { - // Bounded because a definition chain could be cyclic in invalid IL. - for (int step = 0; step < 4; step++) - { - if (target.InferType(typeSystem) is PointerType pointerType) - return pointerType.ElementType; - if (!target.MatchLdLoc(out var v) || !v.IsSingleDefinition - || v.StoreInstructions.Count != 1 || !(v.StoreInstructions[0] is StLoc store)) - { - return null; - } - target = store.Value; - } - return null; - } - internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation);