Skip to content

Recover ref/out/in on generic-instance and ref-readonly call sites#609

Merged
richlander merged 1 commit into
mainfrom
fix/generic-instance-refkinds-v2
Jun 18, 2026
Merged

Recover ref/out/in on generic-instance and ref-readonly call sites#609
richlander merged 1 commit into
mainfrom
fix/generic-instance-refkinds-v2

Conversation

@richlander

Copy link
Copy Markdown
Owner

What

Recovers call-site ref/out/in keywords that the pipeline was dropping, in two related cases the raising-pass review surfaced.

Generic-instance calls

A call on a constructed generic type resolves as a MemberReference with a TypeSpecification parent, which carries no parameter rows — so out/in were lost and rendered as ref (CS1620/CS1615). The classic case: every Dictionary<,>.TryGetValue(out v) call. This resolves the MemberRef back to its underlying generic MethodDef (signature-blob match within the declaring TypeDef) and reads its parameter rows.

Ref-kind classification / spelling

  • ref readonly parameters (RequiresLocationAttribute) now join in (IsReadOnlyAttribute) as readonly references — spelled without a mutable-ref keyword (valid for a readonly/rvalue argument). Previously a ref readonly ctor like ReadOnlySpan<T>(ref readonly T) was rendered ref this on a readonly receiver → CS1605.
  • out/ref arguments now require a genuine assignable lvalue; a cast (unbox) is not one, so out (T)x (CS0206) falls back to the default spelling. in still accepts a value.

Validation

Harness defect-diff (--diff-defects) vs main, over the methods checked in both runs:

REGRESSED (method gained the code):
  (none)
IMPROVED (method lost the code):
  CS1620: 2   (MemberInfoCache.PopulateProperties, Attribute.AddAttributesToList)
  CS1605: 1   (GuidResult.ToGuid)

A clean win. Decompiler tests 427 pass (adds a generic-instance out/in call-site test and a RefKindBox<T> fixture).

Dependency

Depends on #608 (compile-check: import internal members) for the clean scoreboard. Without it, four spurious CS1615 appear (Array.Fill/Sort/UnsafeArrayAsSpan, String.JoinCore): the decompiled ref is correct, but it calls the internal Span<T>(ref T, int) ctor that the external compile-check shell can't access, so Roslyn mis-binds and mis-reports. #608 makes those report CS0122 (filtered noise) instead. Merge #608 first.

🤖 Generated with Claude Code

A call on a constructed generic type resolves as a MemberReference with a
TypeSpecification parent, which carries no parameter rows — so out/in were lost
and rendered as ref (CS1620/CS1615), e.g. every `Dictionary<,>.TryGetValue(out
v)` call. Recover them by resolving the MemberRef back to the underlying generic
MethodDef (signature-blob match within the declaring TypeDef) and reading its
parameter rows.

Also fixes how by-ref kinds are classified and spelled:

- `ref readonly` parameters (RequiresLocationAttribute) join `in`
  (IsReadOnlyAttribute) as readonly references — rendered without a mutable-ref
  keyword, valid for a readonly/rvalue argument. Previously a `ref readonly` ctor
  like `ReadOnlySpan<T>(ref readonly T)` was spelled `ref this` on a readonly
  receiver (CS1605).
- `out`/`ref` arguments now require a genuine assignable lvalue; a cast (unbox)
  is not one, so `out (T)x` (CS0206) falls back to the default spelling. `in`
  still accepts a value form.

Validated with the harness defect-diff: vs main, REGRESSED (none), IMPROVED
CS1620 x2 and CS1605 x1. (The four spurious CS1615 from internal-overload calls
are a compile-check shell artifact addressed separately by importing internals.)
Tests: ILInspector.Decompiler.Tests 427 pass (a generic-instance out/in call site
and the RefKindBox<T> fixture added).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@richlander richlander merged commit 424f767 into main Jun 18, 2026
10 checks passed
@richlander richlander deleted the fix/generic-instance-refkinds-v2 branch June 18, 2026 12:10
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.

1 participant