Skip to content

Raise value-type using statements (constrained dispose)#712

Merged
richlander merged 1 commit into
mainfrom
feature/raise-using
Jun 20, 2026
Merged

Raise value-type using statements (constrained dispose)#712
richlander merged 1 commit into
mainfrom
feature/raise-using

Conversation

@richlander

@richlander richlander commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #711, which raised the reference-type using lowering and explicitly deferred "value-type/constrained dispose ... for a later slice." This PR is that slice.

For a struct IDisposable resource (e.g. List<T>.Enumerator), csc emits no null guard — the finally is a bare constrained V_0.Dispose(); disposing through the local's address. #711's reference-type-only match left it as a hand-rolled try/finally; now it raises to using (...).

What changed

  • UsingStatementPass.TryMatch recognizes both finally shapes:

    • reference type: if (V_0) ((IDisposable)V_0).Dispose(); (null guard)
    • value type: V_0.Dispose(); (no guard)

    A shared IsDisposeOf helper accepts a LoadLocal (reference) or LoadLocalAddress (value-type constrained) receiver.

  • Fixture StructUsing (List<int>.Enumerator) in CfgSampleClass; tests assert the raise, the Enumerator resource type, and the rendered using header with no finally/Dispose left.

  • Ledger annotation broadened: reference-type IDisposable null-guard and value-type constrained dispose now covered; ref-struct pattern dispose and await using remain the open slices.

Tests

357 decompiler + 1157 product tests green.

🤖 Generated with Copilot

Extend UsingStatementPass to the value-type slice #711 deferred: csc emits
no null guard for a struct IDisposable resource — the finally is a bare
constrained `V_0.Dispose();` disposing through the local's address — so the
reference-type-only match left it as a hand-rolled try/finally.

- TryMatch now recognizes both finally shapes: the reference-type
  `if (V_0) ((IDisposable)V_0).Dispose();` null guard and the value-type
  unguarded `V_0.Dispose();`. A shared IsDisposeOf helper accepts a
  LoadLocal (reference) or LoadLocalAddress (value-type constrained) receiver.
- Fixture StructUsing (List<int>.Enumerator) in CfgSampleClass; tests assert
  the raise, the Enumerator resource type, and the rendered `using` header
  with no finally/Dispose left.
- Ledger annotation broadened: reference-type IDisposable null-guard and
  value-type constrained dispose now covered; ref-struct pattern dispose and
  await using remain the open slices.

357 decompiler + 1157 product tests green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@richlander richlander force-pushed the feature/raise-using branch from c329158 to 89a1dd1 Compare June 20, 2026 08:22
@richlander richlander changed the title Raise try/finally-Dispose into using statements Raise value-type using statements (constrained dispose) Jun 20, 2026
@richlander richlander merged commit 7ad68b1 into main Jun 20, 2026
5 checks passed
@richlander richlander deleted the feature/raise-using branch June 20, 2026 08:29
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