Skip to content

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

Description

@sailro

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

Component: ICSharpCode.Decompiler
Version: 10.1.0.8386 (NuGet) — also reproduces in current master.

A conditional expression whose non-null branch yields a ref struct (e.g. Span<T> / ReadOnlySpan<T>) with a default fallback is decompiled using null-conditional access (?.) and the null-coalescing operator (??). A ref struct cannot be made nullable, so the decompiled code does not compile.

Steps to reproduce

Compile this to any TFM where Span<T> is available (e.g. net48 + the System.Memory package, or net8.0):

using System;

public class Holder
{
    public byte[] Data;
    public Span<byte> GetSpan() => Data;
}

public class C
{
    public static Span<byte> M(Holder h) => h != null ? h.GetSpan() : default;
}

Decompile the resulting assembly.

Actual output

public static Span<byte> M(Holder h)
{
    return h?.GetSpan() ?? default(Span<byte>);
}

Recompiling this output fails with:

error CS8978: 'Span<byte>' cannot be made nullable.

Expected output

Something a ref struct can express, e.g.:

public static Span<byte> M(Holder h)
{
    return (h != null) ? h.GetSpan() : default(Span<byte>);
}

Root cause

NullPropagationTransform.TryNullPropagation (ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs) takes the NullableType.IsNonNullableValueType(returnType) branch for Span<byte> — a ref struct has IsReferenceType == false and is not Nullable<T>, so IsNonNullableValueType returns true — and emits the ?. ?? fallback shape. There is no guard for by-ref-like (ref struct) types.

Suggested fix

Guard the value-type branch against ref structs:

else if (!removedRewrapOrNullableCtor
         && NullableType.IsNonNullableValueType(returnType)
         && !returnType.IsByRefLike)   // ref structs cannot be made nullable
{
    ...
}

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions