From fb9ff796da30156c693f76a49f2b34f7f44aa108 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 6 Sep 2026 20:13:54 +0200 Subject: [PATCH] Fix `stackalloc char[4]` turning to `stackalloc short[4]`. --- .gitignore | 1 + .../TestCases/Pretty/CS73_StackAllocInitializers.cs | 13 +++++++------ ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 8 +++++--- .../IL/Transforms/ExpressionTransforms.cs | 2 ++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index d79b59fe0..5f8042922 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ ILSpy.Installer/wix/ **/.vscode/ DecompilerTests.config.json *.trx +report/ # Claude Code local session/skills directory /.claude/ /.understand-anything \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs index aaa641053..387e9b4fa 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs @@ -332,6 +332,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return UseSpan(span); } + public string GetSpan5() + { + Span span = stackalloc char[4] { '1', '2', '3', '4' }; + return UseSpan(span); + } + public void Issue2103a() { Span span = stackalloc byte[3] { 1, 2, 3 }; @@ -354,12 +360,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine((stackalloc byte[3])[1]); } - public string UseSpan(Span span) - { - throw new NotImplementedException(); - } - - public string UseSpan(Span span) + public string UseSpan(Span span) { throw new NotImplementedException(); } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index d9b1ab982..aba6278ac 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -586,11 +586,11 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLocAllocSpan(LocAllocSpan inst, TranslationContext context) { - return TranslateLocAllocSpan(inst, context.TypeHint, out _) + return TranslateLocAllocSpan(inst, out _) .WithILInstruction(inst).WithRR(new ResolveResult(inst.Type)); } - StackAllocExpression TranslateLocAllocSpan(LocAllocSpan inst, IType typeHint, out IType elementType) + StackAllocExpression TranslateLocAllocSpan(LocAllocSpan inst, out IType elementType) { elementType = inst.Type.TypeArguments[0]; TranslatedExpression countExpression = Translate(inst.Argument) @@ -3985,6 +3985,8 @@ namespace ICSharpCode.Decompiler.CSharp { final = spanCtor.Arguments[0] as LdLoc; resultType = spanCtor.Method.DeclaringType; + // The following function expects typeHint to be a pointer type. + typeHint = new PointerType(spanCtor.Method.DeclaringType.TypeArguments[0]); } if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) throw new ArgumentException("given Block is invalid!"); @@ -4008,7 +4010,7 @@ namespace ICSharpCode.Decompiler.CSharp stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out elementType); break; case LocAllocSpan locAllocSpan: - stackAllocExpression = TranslateLocAllocSpan(locAllocSpan, typeHint, out elementType); + stackAllocExpression = TranslateLocAllocSpan(locAllocSpan, out elementType); break; default: throw new ArgumentException("given Block is invalid!"); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 51d1cfd79..c563248c7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -414,6 +414,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!initializer.Instructions[0].MatchStLoc(out var initializerVariable, out var value)) return false; + if (!TypeUtils.IsCompatiblePointerTypeForMemoryAccess(initializerVariable.Type, elementType)) + return false; if (!(value.MatchLocAlloc(out sizeInBytes) && MatchesElementCount(sizeInBytes, elementType, newObj.Arguments[1]))) return false; // The block addresses the allocation through the localloc pointer and only its