[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
{
...
}
[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 adefaultfallback 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 + theSystem.Memorypackage, or net8.0):Decompile the resulting assembly.
Actual output
Recompiling this output fails with:
Expected output
Something a ref struct can express, e.g.:
Root cause
NullPropagationTransform.TryNullPropagation(ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs) takes theNullableType.IsNonNullableValueType(returnType)branch forSpan<byte>— a ref struct hasIsReferenceType == falseand is notNullable<T>, soIsNonNullableValueTypereturnstrue— and emits the?. ?? fallbackshape. There is no guard for by-ref-like (ref struct) types.Suggested fix
Guard the value-type branch against ref structs: