Skip to content

Fix #3821: keep ref-struct conditional as if/return, not ?. / ?? (CS8978)#3823

Merged
siegfriedpammer merged 1 commit into
icsharpcode:masterfrom
sailro:fix-refstruct-nullpropagation
Jun 29, 2026
Merged

Fix #3821: keep ref-struct conditional as if/return, not ?. / ?? (CS8978)#3823
siegfriedpammer merged 1 commit into
icsharpcode:masterfrom
sailro:fix-refstruct-nullpropagation

Conversation

@sailro

@sailro sailro commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #3821.

NullPropagationTransform rewrote testedVar != null ? testedVar.AccessChain : default
to testedVar?.AccessChain ?? default whenever the access-chain result was a
non-nullable value type. For a by-ref-like type (a ref struct such as Span<T>)
this does not compile: a ref struct cannot be wrapped in Nullable<T> (CS8978).

This excludes by-ref-like return types from the null-coalescing rewrite, leaving
the conditional as an if/return, which compiles.

Test

A Correctness/NullPropagation case returns a ref struct from a
holder != null ? holder.Get() : default conditional (guarded #if CS72).
Without the fix the decompiled holder?.Get() ?? default fails to recompile
(CS8978); with it the decompiled output recompiles and behaves identically.

Verified against a build with this change by decompiling a minimal assembly:
h != null ? h.GetSpan() : default now decompiles to an if/return instead of
h?.GetSpan() ?? default(Span<byte>).

…. / ?? (CS8978)

NullPropagationTransform rewrote `c != null ? c.AccessChain : default` to
`c?.AccessChain ?? default` whenever the access-chain result was a non-nullable
value type. For a by-ref-like type (a ref struct such as Span<T>) that form does
not compile: a ref struct cannot be wrapped in Nullable<T> (CS8978). Exclude
by-ref-like return types from the null-coalescing rewrite.

Assisted-by: Copilot:claude-opus-4.8:GitHub Copilot CLI

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

This PR fixes an invalid null-propagation rewrite in NullPropagationTransform that produced uncompilable C# when the access-chain returns a by-ref-like value type (e.g., a ref struct such as Span<T>), addressing #3821.

Changes:

  • Exclude by-ref-like return types from the ?. / ?? null-coalescing rewrite that relies on nullable-lifting.
  • Add a correctness test case (guarded by #if CS72) that returns a ref struct from holder != null ? holder.Get() : default to ensure the decompiled output remains recompilable.

Reviewed changes

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

File Description
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs Adds a guard (!returnType.IsByRefLike) to prevent emitting ?./?? for by-ref-like return types that cannot be made nullable.
ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs Adds a C# 7.2+ test case covering ref struct return values to prevent regressions.

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

@siegfriedpammer
siegfriedpammer merged commit f5d156b into icsharpcode:master Jun 29, 2026
7 checks passed
@siegfriedpammer

Copy link
Copy Markdown
Member

Thank you for your contribution!

@sailro
sailro deleted the fix-refstruct-nullpropagation branch June 29, 2026 05:49
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.

Decompiler emits ?. / ?? over a ref-struct-returning member, producing CS8978 (cannot be made nullable)

3 participants