|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public partial class NullableConversionTests : CompilingTestBase
{
[Fact, WorkItem(544450, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544450")]
public void TestBug12780()
{
string source = @"
enum E : byte { A, B }
class Program
{
static void Main()
{
E? x = 0;
System.Console.Write(x);
x = (E?) E.B;
System.Console.Write(x);
}
}
";
var verifier = CompileAndVerify(source: source, expectedOutput: "AB");
}
[Fact]
public void TestNullableConversions()
{
// IntPtr and UIntPtr violate the rules of user-defined conversions for
// backwards-compatibility reasons. All of these should compile without error.
string source = @"
using System;
class P
{
public static void Main()
{
int? x = 123;
long? y = x;
V(x.HasValue);
V(x.Value == 123);
V((int)x == 123);
V(y.HasValue);
V(y.Value == 123);
V((long)y == 123);
V((int)y == 123);
x = null;
y = x;
V(x.HasValue);
V(y.HasValue);
bool caught = false;
try
{
y = (int) y;
}
catch
{
caught = true;
}
V(caught);
}
static void V(bool b)
{
Console.Write(b ? 't' : 'f');
}
}
";
string expectedOutput = @"tttttttfft";
var verifier = CompileAndVerify(source: source, expectedOutput: expectedOutput);
}
[Fact]
public void TestLiftedUserDefinedConversions()
{
string source = @"
struct Conv
{
public static implicit operator int(Conv c)
{
return 1;
}
// DELIBERATE SPEC VIOLATION: We allow 'lifting' even though the
// return type is not a non-nullable value type.
// UNDONE: Test pointer types
public static implicit operator string(Conv c)
{
return '2'.ToString();
}
public static implicit operator double?(Conv c)
{
return 123.0;
}
static void Main()
{
Conv? c = new Conv();
int? i = c;
string s = c;
double? d = c;
V(i.HasValue);
V(i == 1);
V(s != null);
V(s.Length == 1);
V(s[0] == '2');
V(d.HasValue);
V(d == 123.0);
c = null;
i = c;
s = c;
d = c;
V(!i.HasValue);
V(s == null);
V(!d.HasValue);
}
static void V(bool f)
{
System.Console.Write(f ? 't' : 'f');
}
}
";
string expectedOutput = @"tttttttttt";
var verifier = CompileAndVerify(source: source, expectedOutput: expectedOutput);
}
[Fact, WorkItem(529279, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529279")]
public void TestNullableWithGenericConstraints01()
{
string source = @"
class GenC<T, U> where T : struct, U where U : class
{
public void Test(T t)
{
T? nt = t;
U valueUn = nt;
System.Console.WriteLine(valueUn.ToString());
}
}
interface I1 { }
struct S1 : I1 { public override string ToString() { return ""Hola""; } }
static class Program
{
static void Main()
{
(new GenC<S1, I1>()).Test(default(S1));
}
}";
CompileAndVerify(source, expectedOutput: "Hola");
}
[Fact, WorkItem(543996, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543996")]
public void TestNonLiftedUDCOnStruct()
{
string source = @"using System;
struct A
{
public C CFld;
}
class C
{
public A AFld;
public static implicit operator A(C c)
{
return c.AFld;
}
public static explicit operator C(A a)
{
return a.CFld;
}
}
public class Test
{
public static void Main()
{
A a = new A();
a.CFld = new C();
a.CFld.AFld = a;
C c = a.CFld;
A? nubA = c; // Assert here
Console.Write(nubA.HasValue && nubA.Value.CFld == c);
C nubC = (C)nubA;
Console.Write(nubC.AFld.CFld == c);
}
}
";
CompileAndVerify(source, expectedOutput: "TrueTrue");
}
[Fact, WorkItem(543997, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543997")]
public void TestImplicitLiftedUDCOnStruct()
{
string source = @"using System;
namespace Test
{
static class Program
{
static void Main()
{
S.v = 0;
S? S2 = 123; // not lifted, int=>int?, int?=>S, S=>S?
Console.WriteLine(S.v == 123);
}
}
public struct S
{
public static int v;
// s == null, return v = -1
public static implicit operator S(int? s)
{
Console.Write(""Imp S::int? -> S "");
S ss = new S();
S.v = s ?? -1;
return ss;
}
}
}
";
CompileAndVerify(source, expectedOutput: "Imp S::int? -> S True");
}
[Fact]
public void TestExplicitUnliftedUDC()
{
string source = @"
using System;
namespace Test
{
static class Program
{
static void Main()
{
int? i = 123;
C c = (C)i;
Console.WriteLine(c.v == 123 ? 't' : 'f');
}
}
public class C
{
public readonly int v;
public C(int v) { this.v = v; }
public static implicit operator C(int v)
{
Console.Write(v);
return new C(v);
}
}
}
";
CompileAndVerify(source, expectedOutput: "123t");
}
[Fact, WorkItem(545091, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545091")]
public void TestImplicitUDCInNullCoalescingOperand()
{
string source = @"using System;
class C
{
public static implicit operator C(string s)
{
Console.Write(""implicit "");
return new C();
}
public override string ToString() { return ""C""; }
}
class A
{
static void Main()
{
var ret = ""str"" ?? new C();
Console.Write(ret.GetType());
}
}
";
CompileAndVerify(source, expectedOutput: "implicit C");
}
[WorkItem(545377, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545377")]
[Fact]
public void TestLiftedVsUnlifted()
{
// The correct behavior here is to choose operator 2. Binary operator overload
// resolution should determine that the best built-in addition operator is
// lifted int + int, which has signature int? + int? --> int?. However, the
// native compiler gets this wrong. The native compiler, pretends
// that there are *three* lifted operators: int + int? --> int?, int? + int --> int?,
// and int? + int? --> int?. Therefore the native compiler decides that the
// int? + int --> int operator is the best, because it is the applicable operator
// with the most specific types, and therefore chooses operator 1.
//
// This does not match the specification.
// It seems reasonable that if someone has done this very strange thing of making
// conversions S --> int and S --> int?, that they probably intend for the
// lifted operation to use the conversion specifically designed for nullables.
//
// Roslyn matches the specification and takes the break from the native compiler.
// See the next test case for more thoughts on this.
string source = @"
using System;
public struct S
{
public static implicit operator int(S n) // 1 native compiler
{
Console.WriteLine(1);
return 0;
}
public static implicit operator int?(S n) // 2 Roslyn compiler
{
Console.WriteLine(2);
return null;
}
public static void Main()
{
int? qa = 5;
S b = default(S);
var sum = qa + b;
}
}
";
CompileAndVerify(source, expectedOutput: "2");
}
[WorkItem(545377, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545377")]
[Fact]
public void TestLiftedVsUnlifted_Combinations()
{
// The point of this test is to show that Roslyn and the native compiler
// agree on resolution of *conversions* but do not agree on resolution
// of *binary operators*. That is, we wish to show that we are isolating
// the breaking change to the binary operator overload resolution, and not
// to the conversion resolution code. See the previous bug for details.
string source = @"
using System;
struct S___A
{
public static implicit operator int(S___A n) { Console.Write('A'); return 0; }
}
struct S__B_
{
public static implicit operator int(S__B_? n) { Console.Write('B'); return 0; }
}
struct S__BA
{
public static implicit operator int(S__BA? n) { Console.Write('B'); return 0; }
public static implicit operator int(S__BA n) { Console.Write('A'); return 0; }
}
struct S_C__
{
public static implicit operator int?(S_C__ n) { Console.Write('C'); return 0; }
}
struct S_C_A
{
public static implicit operator int?(S_C_A n) { Console.Write('C'); return 0; }
public static implicit operator int(S_C_A n) { Console.Write('A'); return 0; }
}
struct S_CB_
{
public static implicit operator int?(S_CB_ n) { Console.Write('C'); return 0; }
public static implicit operator int(S_CB_? n) { Console.Write('B'); return 0; }
}
struct S_CBA
{
public static implicit operator int?(S_CBA n) { Console.Write('C'); return 0; }
public static implicit operator int(S_CBA? n) { Console.Write('B'); return 0; }
public static implicit operator int(S_CBA n) { Console.Write('A'); return 0; }
}
struct SD___
{
public static implicit operator int?(SD___? n){ Console.Write('D'); return 0; }
}
struct SD__A
{
public static implicit operator int?(SD__A? n){ Console.Write('D'); return 0; }
public static implicit operator int(SD__A n) { Console.Write('A'); return 0; }
}
struct SD_B_
{
public static implicit operator int?(SD_B_? n){ Console.Write('D'); return 0; }
public static implicit operator int(SD_B_? n) { Console.Write('B'); return 0; }
}
struct SD_BA
{
public static implicit operator int?(SD_BA? n){ Console.Write('D'); return 0; }
public static implicit operator int(SD_BA? n) { Console.Write('B'); return 0; }
public static implicit operator int(SD_BA n) { Console.Write('A'); return 0; }
}
struct SDC__
{
public static implicit operator int?(SDC__? n){ Console.Write('D'); return 0; }
public static implicit operator int?(SDC__ n) { Console.Write('C'); return 0; }
}
struct SDC_A
{
public static implicit operator int?(SDC_A? n){ Console.Write('D'); return 0; }
public static implicit operator int?(SDC_A n) { Console.Write('C'); return 0; }
public static implicit operator int(SDC_A n) { Console.Write('A'); return 0; }
}
struct SDCB_
{
public static implicit operator int?(SDCB_? n){ Console.Write('D'); return 0; }
public static implicit operator int?(SDCB_ n) { Console.Write('C'); return 0; }
public static implicit operator int(SDCB_? n) { Console.Write('B'); return 0; }
}
struct SDCBA
{
public static implicit operator int?(SDCBA? n){ Console.Write('D'); return 0; }
public static implicit operator int?(SDCBA n) { Console.Write('C'); return 0; }
public static implicit operator int(SDCBA? n) { Console.Write('B'); return 0; }
public static implicit operator int(SDCBA n) { Console.Write('A'); return 0; }
}
class Program
{
static S___A s___a1;
static S__B_ s__b_1;
static S__BA s__ba1;
static S_C__ s_c__1;
static S_C_A s_c_a1;
static S_CB_ s_cb_1;
static S_CBA s_cba1;
static SD___ sd___1;
static SD__A sd__a1;
static SD_B_ sd_b_1;
static SD_BA sd_ba1;
static SDC__ sdc__1;
static SDC_A sdc_a1;
static SDCB_ sdcb_1;
static SDCBA sdcba1;
static S___A? s___a2;
static S__B_? s__b_2;
static S__BA? s__ba2;
static S_C__? s_c__2;
static S_C_A? s_c_a2;
static S_CB_? s_cb_2;
static S_CBA? s_cba2;
static SD___? sd___2;
static SD__A? sd__a2;
static SD_B_? sd_b_2;
static SD_BA? sd_ba2;
static SDC__? sdc__2;
static SDC_A? sdc_a2;
static SDCB_? sdcb_2;
static SDCBA? sdcba2;
static int i1 = 0;
static int? i2 = 0;
static void Main()
{
TestConversions();
Console.WriteLine();
TestAdditions();
}
static void TestConversions()
{
i1 = s___a1;
i1 = s__b_1;
i1 = s__ba1;
// i1 = s_c__1;
i1 = s_c_a1;
i1 = s_cb_1;
i1 = s_cba1;
// i1 = sd___1;
i1 = sd__a1;
i1 = sd_b_1;
i1 = sd_ba1;
// i1 = sdc__1;
i1 = sdc_a1;
i1 = sdcb_1;
i1 = sdcba1;
// i1 = s___a2;
i1 = s__b_2;
i1 = s__ba2;
// i1 = s_c__2;
// i1 = s_c_a2;
i1 = s_cb_2;
i1 = s_cba2;
//i1 = sd___2;
//i1 = sd__a2;
i1 = sd_b_2;
i1 = sd_ba2;
//i1 = sdc__2;
//i1 = sdc_a2;
i1 = sdcb_2;
i1 = sdcba2;
i2 = s___a1;
i2 = s__b_1;
i2 = s__ba1;
i2 = s_c__1;
i2 = s_c_a1;
i2 = s_cb_1;
i2 = s_cba1;
i2 = sd___1;
i2 = sd__a1;
i2 = sd_b_1;
i2 = sd_ba1;
i2 = sdc__1;
i2 = sdc_a1;
i2 = sdcb_1;
i2 = sdcba1;
i2 = s___a2;
i2 = s__b_2;
i2 = s__ba2;
i2 = s_c__2;
i2 = s_c_a2;
//i2 = s_cb_2;
//i2 = s_cba2;
i2 = sd___2;
i2 = sd__a2;
i2 = sd_b_2;
i2 = sd_ba2;
i2 = sdc__2;
i2 = sdc_a2;
i2 = sdcb_2;
i2 = sdcba2;
}
static void TestAdditions()
{
i2 = i1 + s___a1;
i2 = i1 + s__b_1;
i2 = i1 + s__ba1;
i2 = i1 + s_c__1;
i2 = i1 + s_c_a1;
i2 = i1 + s_cb_1;
i2 = i1 + s_cba1;
i2 = i1 + sd___1;
i2 = i1 + sd__a1;
i2 = i1 + sd_b_1;
i2 = i1 + sd_ba1;
i2 = i1 + sdc__1;
i2 = i1 + sdc_a1;
i2 = i1 + sdcb_1;
i2 = i1 + sdcba1;
i2 = i1 + s___a2;
i2 = i1 + s__b_2;
i2 = i1 + s__ba2;
i2 = i1 + s_c__2;
i2 = i1 + s_c_a2;
i2 = i1 + s_cb_2;
i2 = i1 + s_cba2;
i2 = i1 + sd___2;
i2 = i1 + sd__a2;
i2 = i1 + sd_b_2;
i2 = i1 + sd_ba2;
i2 = i1 + sdc__2;
i2 = i1 + sdc_a2;
i2 = i1 + sdcb_2;
i2 = i1 + sdcba2;
i2 = i2 + s___a1;
i2 = i2 + s__b_1;
i2 = i2 + s__ba1;
i2 = i2 + s_c__1;
i2 = i2 + s_c_a1;
i2 = i2 + s_cb_1;
i2 = i2 + s_cba1;
i2 = i2 + sd___1;
i2 = i2 + sd__a1;
i2 = i2 + sd_b_1;
i2 = i2 + sd_ba1;
i2 = i2 + sdc__1;
i2 = i2 + sdc_a1;
i2 = i2 + sdcb_1;
i2 = i2 + sdcba1;
i2 = i2 + s___a2;
i2 = i2 + s__b_2;
i2 = i2 + s__ba2;
i2 = i2 + s_c__2;
i2 = i2 + s_c_a2;
// i2 = i2 + s_cb_2; // Native compiler allows these because it actually converts to int,
// i2 = i2 + s_cba2; // not int?, which is not ambiguous. Roslyn takes the breaking change.
i2 = i2 + sd___2;
i2 = i2 + sd__a2;
i2 = i2 + sd_b_2;
i2 = i2 + sd_ba2;
i2 = i2 + sdc__2;
i2 = i2 + sdc_a2;
i2 = i2 + sdcb_2;
i2 = i2 + sdcba2;
}
}
";
var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe.WithWarningLevel(0));
// Roslyn and native compiler both produce ABAABAABAABABBBBBBBBABACCCCDADACCCCBBDDDDDDDD
// for straight conversions.
// Because Roslyn (correctly) prefers converting to int? instead of int when doing lifted addition,
// native compiler produces ABACABADABACABABBBBDDBBDDBBABACABADABACABABBDDBBDDBB for additions.
// Roslyn compiler produces ABACABADABACABABBBBDDBBDDBBABACCCCDADACCCCBBDDDDDDDD. That is,
// preference is given to int?-returning conversions C and D over int-returning A and B.
string expected = @"ABAABAABAABABBBBBBBBABACCCCDADACCCCBBDDDDDDDD
ABACABADABACABABBBBDDBBDDBBABACCCCDADACCCCBBDDDDDDDD";
CompileAndVerify(compilation, expectedOutput: expected);
}
[Fact]
[WorkItem(1084278, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1084278")]
public void NullableConversionFromFloatingPointConst()
{
var source = @"
class C
{
void Use(int? p)
{
}
void Test()
{
int? i;
// double checks
i = (int?)3.5d;
i = (int?)double.MaxValue;
i = (int?)double.NaN;
i = (int?)double.NegativeInfinity;
i = (int?)double.PositiveInfinity;
// float checks
i = (int?)3.5d;
i = (int?)float.MaxValue;
i = (int?)float.NaN;
i = (int?)float.NegativeInfinity;
i = (int?)float.PositiveInfinity;
// double checks
_ = (int)3.5d;
_ = (int)double.MaxValue;
_ = (int)double.NaN;
_ = (int)double.NegativeInfinity;
_ = (int)double.PositiveInfinity;
// float checks
_ = (int)3.5d;
_ = (int)float.MaxValue;
_ = (int)float.NaN;
_ = (int)float.NegativeInfinity;
_ = (int)float.PositiveInfinity;
Use(i);
unchecked {
// double checks
i = (int?)3.5d;
i = (int?)double.MaxValue;
i = (int?)double.NaN;
i = (int?)double.NegativeInfinity;
i = (int?)double.PositiveInfinity;
// float checks
i = (int?)3.5d;
i = (int?)float.MaxValue;
i = (int?)float.NaN;
i = (int?)float.NegativeInfinity;
i = (int?)float.PositiveInfinity;
// double checks
_ = (int)3.5d;
_ = (int)double.MaxValue;
_ = (int)double.NaN;
_ = (int)double.NegativeInfinity;
_ = (int)double.PositiveInfinity;
// float checks
_ = (int)3.5d;
_ = (int)float.MaxValue;
_ = (int)float.NaN;
_ = (int)float.NegativeInfinity;
_ = (int)float.PositiveInfinity;
}
}
}
";
var compilation = CreateCompilation(source);
using (new EnsureEnglishUICulture())
{
compilation.VerifyDiagnostics(
// (15,13): error CS0221: Constant value '1.7976931348623157E+308' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)double.MaxValue;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)double.MaxValue").WithArguments(double.MaxValue.ToString(), "int").WithLocation(15, 13),
// (16,13): error CS0221: Constant value 'NaN' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)double.NaN;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)double.NaN").WithArguments(double.NaN.ToString(), "int").WithLocation(16, 13),
// (17,13): error CS0221: Constant value '-∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)double.NegativeInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)double.NegativeInfinity").WithArguments(double.NegativeInfinity.ToString(), "int").WithLocation(17, 13),
// (18,13): error CS0221: Constant value '∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)double.PositiveInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)double.PositiveInfinity").WithArguments(double.PositiveInfinity.ToString(), "int").WithLocation(18, 13),
// (22,13): error CS0221: Constant value '3.4028235E+38' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)float.MaxValue;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)float.MaxValue").WithArguments(float.MaxValue.ToString(), "int").WithLocation(22, 13),
// (23,13): error CS0221: Constant value 'NaN' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)float.NaN;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)float.NaN").WithArguments(float.NaN.ToString(), "int").WithLocation(23, 13),
// (24,13): error CS0221: Constant value '-∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)float.NegativeInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)float.NegativeInfinity").WithArguments(float.NegativeInfinity.ToString(), "int").WithLocation(24, 13),
// (25,13): error CS0221: Constant value '∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// i = (int?)float.PositiveInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int?)float.PositiveInfinity").WithArguments(float.PositiveInfinity.ToString(), "int").WithLocation(25, 13),
// (29,13): error CS0221: Constant value '1.7976931348623157E+308' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)double.MaxValue;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)double.MaxValue").WithArguments(double.MaxValue.ToString(), "int").WithLocation(29, 13),
// (30,13): error CS0221: Constant value 'NaN' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)double.NaN;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)double.NaN").WithArguments(double.NaN.ToString(), "int").WithLocation(30, 13),
// (31,13): error CS0221: Constant value '-∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)double.NegativeInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)double.NegativeInfinity").WithArguments(double.NegativeInfinity.ToString(), "int").WithLocation(31, 13),
// (32,13): error CS0221: Constant value '∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)double.PositiveInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)double.PositiveInfinity").WithArguments(double.PositiveInfinity.ToString(), "int").WithLocation(32, 13),
// (36,13): error CS0221: Constant value '3.4028235E+38' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)float.MaxValue;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)float.MaxValue").WithArguments(float.MaxValue.ToString(), "int").WithLocation(36, 13),
// (37,13): error CS0221: Constant value 'NaN' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)float.NaN;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)float.NaN").WithArguments(float.NaN.ToString(), "int").WithLocation(37, 13),
// (38,13): error CS0221: Constant value '-∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)float.NegativeInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)float.NegativeInfinity").WithArguments(float.NegativeInfinity.ToString(), "int").WithLocation(38, 13),
// (39,13): error CS0221: Constant value '∞' cannot be converted to a 'int' (use 'unchecked' syntax to override)
// _ = (int)float.PositiveInfinity;
Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)float.PositiveInfinity").WithArguments(float.PositiveInfinity.ToString(), "int").WithLocation(39, 13)
);
}
var syntaxTree = compilation.SyntaxTrees.First();
var target = syntaxTree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().ToList()[2];
var operand = target.Expression;
Assert.Equal("double.NaN", operand.ToFullString());
// Note: there is a valid conversion here at the type level. It's the process of evaluating the conversion, which for
// constants happens at compile time, that triggers the error.
HashSet<DiagnosticInfo> unused = null;
var bag = DiagnosticBag.GetInstance();
var nullableIntType = compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(compilation.GetSpecialType(SpecialType.System_Int32));
var conversion = compilation.Conversions.ClassifyConversionFromExpression(
compilation.GetBinder(target).BindExpression(operand, bag),
nullableIntType,
ref unused);
Assert.True(conversion.IsExplicit && conversion.IsNullable);
}
}
}
|