Skip to content

Fix #3804: decompile foreach over inline array#3837

Open
siegfriedpammer wants to merge 1 commit into
masterfrom
fix-3804-foreach-inline-array
Open

Fix #3804: decompile foreach over inline array#3837
siegfriedpammer wants to merge 1 commit into
masterfrom
fix-3804-foreach-inline-array

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

Fixes #3804.

Problem

Decompiling a foreach over an inline-array value emitted code referencing the compiler-internal <PrivateImplementationDetails>.InlineArrayElementRef helper, whose type name contains angle brackets and cannot be written in C# — so the output did not compile.

Root cause

The C# compiler lowers foreach over an inline array into a for loop whose body calls <PrivateImplementationDetails>.InlineArrayElementRef(ref b, i) with the loop variable as the index. InlineArrayTransform.MatchInlineArrayElementRef only recognized a constant index (LdcI4), so the call survived into the output verbatim.

Fix

Accept a non-constant index in the matcher. The buffer-length range check only applies to a compile-time-constant index; the compiler emits this helper only where the index is provably in range. Everything downstream (LdElemaInlineArray, indexer rendering) is already index-agnostic, so the body recovers to the inline-array indexer b[i], producing a valid for loop. The decompiler does not reconstruct the original foreach, but the output now compiles.

Test

Adds a Sum(Byte16 b) method to the InlineArrayTests Pretty fixture. Because the fixture is both input and expected output, it uses the EXPECTED_OUTPUT idiom: the foreach is compiled (to trigger the lowering), and the for loop over b[i] is the expected decompilation. Verified against all roslyn4OrNewerOptions configs.

🤖 Generated with Claude Code

The C# compiler lowers a foreach over an inline array into a for loop whose
body calls <PrivateImplementationDetails>.InlineArrayElementRef(ref b, i) with
the loop variable as index. MatchInlineArrayElementRef only recognized a
constant index, so this call survived into the output verbatim; because
<PrivateImplementationDetails> cannot be named in C#, the result did not
compile.

Accept a non-constant index in the matcher. The buffer-length range check only
applies to a compile-time-constant index; the compiler emits this helper only
where the index is provably in range. Everything downstream (LdElemaInlineArray,
the indexer rendering) is already index-agnostic, so the body recovers to the
inline-array indexer b[i], producing a valid for loop. The decompiler does not
reconstruct the original foreach, but the output now compiles.

Assisted-by: Claude:claude-opus-4-8:Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a C# decompilation bug where foreach over an inline-array value could decompile into a reference to the compiler-internal <PrivateImplementationDetails>.InlineArrayElementRef(...), producing non-compilable C# due to the angle brackets in the type name. The change updates the inline-array helper matcher so the call is rewritten into the inline-array indexer form (e.g. b[i]) even when the index is not a compile-time constant, and adds a regression test.

Changes:

  • Relax InlineArrayElementRef pattern matching to accept non-constant index expressions while keeping bounds validation for constant indices.
  • Add a Pretty test case that compiles a foreach over an inline array and expects decompilation into a compilable for loop using b[i].

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs Allows matching InlineArrayElementRef with non-constant indices (e.g. loop variable from foreach lowering), enabling rewrite to ldelema.inlinearray and ultimately b[i].
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs Adds a regression fixture using the EXPECTED_OUTPUT idiom to validate decompilation of foreach over an inline array into a compilable loop.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dgrunwald

dgrunwald commented Jun 29, 2026

Copy link
Copy Markdown
Member

This changes the behavior with an out-of-bounds non-constant index.

This transform is only valid if the decompiler can prove the index is in-bounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decompiling foreach over an inline array emits invalid '<PrivateImplementationDetails>.InlineArrayElementRef'

3 participants