From a06c22b88a7eb3d2802929e4c38f0efcd1f2bb7c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 22 Jun 2026 21:46:22 +0200 Subject: [PATCH] Decode float/double stackalloc initializer constants by their type A constant-size stackalloc initializer stores its constant elements through a data blob (cpblk from a field). ReadElement decoded every element by width, so a 4-byte float was read as Int32 and an 8-byte double as Int64. The resulting constant carried the raw bit pattern (1f decoded as 1.0653532E+09f) and its stack type no longer matched the store, tripping StObj.CheckInvariant. Dispatch on the element's type code so Single and Double are read as floating-point, matching the heap-array decoder. Found while exploring stackalloc-initializer coverage around the element-type hint fix; floating-point element types had no test case. Assisted-by: Claude:claude-opus-4-8:Claude Code --- .../Pretty/CS73_StackAllocInitializers.cs | 17 +++++++++++++++++ .../IL/Transforms/TransformArrayInitializers.cs | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs index 264be7e2a..d082d149a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs @@ -212,6 +212,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return UsePointer((byte*)ptr); } + // Mixed constant/non-constant floating-point initializers go through the constant data + // blob; reading a 'float'/'double' element as the integer of the same width produced the + // raw bit pattern as the literal (e.g. 1f decoded as 1.0653532E+09f). + public unsafe string SimpleStackAllocSingle(float a) + { + float* ptr = stackalloc float[4] { 1f, 2f, 3f, a }; + Console.WriteLine(*ptr); + return UsePointer((byte*)ptr); + } + + public unsafe string SimpleStackAllocDouble(double a) + { + double* ptr = stackalloc double[4] { 1.0, 2.0, 3.0, a }; + Console.WriteLine(*ptr); + return UsePointer((byte*)ptr); + } + public unsafe string SimpleStackAllocInt32NonConstant(int a, int b, int c) { int* ptr = stackalloc int[6] { 0, 1, 0, a, b, c }; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 7abf46d74..6245920c1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -325,6 +325,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms private ILInstruction ReadElement(ref BlobReader blob, IType elementType) { + // Floating-point elements must be read as their actual type: their stored bytes are + // not the integer of the same width, and the resulting constant has to match the + // element type's stack type (see StObj.CheckInvariant). + switch (ReflectionHelper.GetTypeCode(elementType)) + { + case TypeCode.Single: + return new LdcF4(blob.ReadSingle()); + case TypeCode.Double: + return new LdcF8(blob.ReadDouble()); + } switch (elementType.GetSize()) { case 1: