Browse Source

Fix #3821: keep ref-struct conditional as if/return, not ?. / ?? (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
pull/3823/head
Sebastien Lebreton 2 weeks ago
parent
commit
bf4bb1287e
  1. 34
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs
  2. 6
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

34
ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs

@ -7,6 +7,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -7,6 +7,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static void Main()
{
new NullPropagation().TestNotCoalescing();
#if CS72
new NullPropagation().TestRefStructNotCoalescing();
#endif
}
class MyClass
@ -25,5 +28,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -25,5 +28,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
return c != null ? c.Text : "Hello";
}
#if CS72
ref struct ByRefLikeStruct
{
public int Value;
}
class RefStructHolder
{
public ByRefLikeStruct Get()
{
ByRefLikeStruct result = default(ByRefLikeStruct);
result.Value = 42;
return result;
}
}
void TestRefStructNotCoalescing()
{
Console.WriteLine("TestRefStructNotCoalescing:");
Console.WriteLine(RefStructNotCoalescing(null).Value);
Console.WriteLine(RefStructNotCoalescing(new RefStructHolder()).Value);
}
// A by-ref-like return type cannot be turned into a ?. / ?? null-propagation: a ref struct
// cannot be wrapped in Nullable<T>, so that form does not compile.
ByRefLikeStruct RefStructNotCoalescing(RefStructHolder holder)
{
return holder != null ? holder.Get() : default(ByRefLikeStruct);
}
#endif
}
}

6
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -140,12 +140,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -140,12 +140,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
IntroduceUnwrap(testedVar, varLoad, mode);
return new NullableRewrap(nonNullInst);
}
else if (!removedRewrapOrNullableCtor && NullableType.IsNonNullableValueType(returnType))
else if (!removedRewrapOrNullableCtor && NullableType.IsNonNullableValueType(returnType)
&& !returnType.IsByRefLike)
{
context.Step($"Null propagation (mode={mode}, output=null coalescing)", nonNullInst);
// testedVar != null ? testedVar.AccessChain : nullInst
// => testedVar?.AccessChain ?? nullInst
// (only valid if AccessChain returns a non-nullable value)
// (only valid if AccessChain returns a non-nullable value; a by-ref-like type such as
// Span<T> is excluded because it cannot be wrapped in Nullable<T> for the ?. / ?? form)
IntroduceUnwrap(testedVar, varLoad, mode);
return new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback,

Loading…
Cancel
Save