Browse Source

Document two CSharpConversions bugs found by differential fuzzing csc

Dumping the implicit/explicit classification of all pairs from a 120-type
universe and diffing it against what csc actually compiles surfaced two
divergences (16 affected pairs), both proven by a compiling-and-running
snippet and captured here as ignored known-bug tests:

1. Nullable conversions derived from tuple conversions are missing:
   csc accepts "(long, object)? a = t;" for t of type (int, string) as
   well as the lifted and explicit forms, CSharpConversions returns None.
   The ECMA spec's 10.6.1 does not list tuple conversions as liftable,
   so this is a case of Roslyn exceeding the spec.
2. An explicit user-defined conversion to a nullable target is rejected
   when the operator result additionally needs an explicit numeric
   conversion: csc accepts "(int?)new ImplicitToLong()" (operator to
   long, then explicit long -> int?), CSharpConversions returns None.

The same sweep confirmed the SByte..Decimal TypeCode range in
ImplicitEnumerationConversion is correct: csc accepts zero constants of
any numeric type (0.0, 0f, 0m) for enum conversion and rejects '\0',
matching the implementation exactly.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3916/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
6932586827
  1. 12
      ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
  2. 23
      ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs

12
ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs

@ -1592,6 +1592,18 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -1592,6 +1592,18 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Is.EqualTo(C.None));
}
[Test, Ignore("Known bug: CSharpConversions does not support nullable conversions derived from tuple conversions, but csc accepts them")]
public void LiftedTupleConversions()
{
// csc accepts the nullable conversions derived from tuple conversions:
// (int, string) t = (1, "one");
// (long, object)? a = t;
// (int, string)? tn = t;
// (long, object)? b = tn;
Assert.That(ImplicitConversion(typeof((int, string)), typeof((long, object)?)).IsValid, "(int, string) -> (long, object)?");
Assert.That(ImplicitConversion(typeof((int, string)?), typeof((long, object)?)).IsValid, "(int, string)? -> (long, object)?");
}
[Test]
public void UserDefinedImplicitConversion_OperatorDeclaredInBaseClassOfSource()
{

23
ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs

@ -731,6 +731,29 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -731,6 +731,29 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Assert.That(ExplicitConversion(typeof(IEnumerable<int>), typeof(StructImplementingIEnumerableOfString)), Is.EqualTo(C.None));
}
[Test, Ignore("Known bug: CSharpConversions does not support nullable conversions derived from tuple conversions, but csc accepts them")]
public void ExplicitLiftedTupleConversions()
{
// csc accepts the explicit nullable conversions derived from tuple conversions:
// (int, string)? tn = ...;
// (long, object) c = ((long, object))tn;
// (int, string)? d = ((int, string)?)lo; with lo of type (long, object)?
Assert.That(ExplicitConversion(typeof((int, string)?), typeof((long, object))).IsValid, "(int, string)? -> (long, object)");
Assert.That(ExplicitConversion(typeof((long, object)?), typeof((int, string)?)).IsValid, "(long, object)? -> (int, string)?");
}
[Test, Ignore("Known bug: CSharpConversions rejects explicit user-defined conversions to a nullable target when the operator result needs an explicit numeric conversion, but csc accepts them")]
public void UserDefinedExplicitConversion_ExplicitNumericConversionToNullableTarget()
{
// csc accepts:
// int? d = (int?)new ImplicitToLong(); -- UD op to long, then explicit long -> int?
// int? e = (int?)new ExplicitToLongOrUInt(); -- UD ops to long/uint, then explicit -> int?
var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ImplicitToLong), typeof(int?));
Assert.That(c.IsValid, "(int?)ImplicitToLong");
var c2 = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToLongOrUInt), typeof(int?));
Assert.That(c2.IsValid, "(int?)ExplicitToLongOrUInt");
}
[Test]
public void ExplicitTypeParameterConversionFromEffectiveBaseClass()
{

Loading…
Cancel
Save