|
// 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 Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
[CompilerTrait(CompilerFeature.NullCoalescingAssignment)]
public class CodeGenNullCoalescingAssignmentTests : CompilingTestBase
{
[Fact]
public void LocalLvalue()
{
var source = @"
using System;
public class C
{
public static void Main()
{
TestNullable();
TestObject();
TestAsStatement();
}
static void TestNullable()
{
int? i1 = null;
Console.WriteLine(i1 ??= GetInt());
}
static void TestObject()
{
string s1 = null;
Console.WriteLine(s1 ??= GetString());
}
static void TestAsStatement()
{
object o = null;
o ??= ""As Statement"";
Console.WriteLine(o);
}
static int GetInt()
{
Console.WriteLine(""In GetInt"");
return 0;
}
static string GetString()
{
Console.WriteLine(""In GetString"");
return ""Test"";
}
}
";
var verifier = CompileAndVerify(source, expectedOutput: @"
In GetInt
0
In GetString
Test
As Statement
");
verifier.VerifyIL("C.TestNullable()", @"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (int? V_0, //i1
int V_1)
IL_0000: ldloca.s V_0
IL_0002: initobj ""int?""
IL_0008: ldloca.s V_0
IL_000a: call ""int int?.GetValueOrDefault()""
IL_000f: stloc.1
IL_0010: ldloca.s V_0
IL_0012: call ""bool int?.HasValue.get""
IL_0017: brtrue.s IL_002a
IL_0019: call ""int C.GetInt()""
IL_001e: stloc.1
IL_001f: ldloca.s V_0
IL_0021: ldloc.1
IL_0022: call ""int?..ctor(int)""
IL_0027: ldloc.1
IL_0028: br.s IL_002b
IL_002a: ldloc.1
IL_002b: call ""void System.Console.WriteLine(int)""
IL_0030: ret
}");
// When the optimizer is on, it turns the local into a stack local, so we don't see assignments.
verifier.VerifyIL("C.TestObject()", expectedIL: @"
{
// Code size 16 (0x10)
.maxstack 2
IL_0000: ldnull
IL_0001: dup
IL_0002: brtrue.s IL_000a
IL_0004: pop
IL_0005: call ""string C.GetString()""
IL_000a: call ""void System.Console.WriteLine(string)""
IL_000f: ret
}
");
verifier.VerifyIL("C.TestAsStatement()", expectedIL: @"
{
// Code size 18 (0x12)
.maxstack 1
.locals init (object V_0) //o
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: brtrue.s IL_000b
IL_0005: ldstr ""As Statement""
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: call ""void System.Console.WriteLine(object)""
IL_0011: ret
}
");
// With the optimizer off, the local is not elided
CompileAndVerify(source, options: TestOptions.DebugDll).VerifyIL("C.TestObject()", expectedIL: @"
{
// Code size 22 (0x16)
.maxstack 2
.locals init (string V_0) //s1
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: dup
IL_0005: brtrue.s IL_000f
IL_0007: pop
IL_0008: call ""string C.GetString()""
IL_000d: dup
IL_000e: stloc.0
IL_000f: call ""void System.Console.WriteLine(string)""
IL_0014: nop
IL_0015: ret
}
");
}
[Fact]
public void FieldLvalue()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
int? f1 = null;
object f2 = null;
public static void Main()
{
TestInt();
TestObject();
}
static void TestInt()
{
var c = new C();
Console.WriteLine(c.f1 ??= GetInt());
}
public static void TestObject()
{
var c = new C();
Console.WriteLine(c.f2 ??= GetObject());
}
static int GetInt()
{
Console.WriteLine(""In GetInt"");
return 0;
}
static object GetObject()
{
Console.WriteLine(""In GetObject"");
return ""object"";
}
}
", expectedOutput: @"
In GetInt
0
In GetObject
object
");
verifier.VerifyIL("C.TestInt()", @"
{
// Code size 59 (0x3b)
.maxstack 2
.locals init (C V_0,
int V_1)
IL_0000: newobj ""C..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldflda ""int? C.f1""
IL_000c: call ""int int?.GetValueOrDefault()""
IL_0011: stloc.1
IL_0012: ldloc.0
IL_0013: ldflda ""int? C.f1""
IL_0018: call ""bool int?.HasValue.get""
IL_001d: brtrue.s IL_0034
IL_001f: call ""int C.GetInt()""
IL_0024: stloc.1
IL_0025: ldloc.0
IL_0026: ldloc.1
IL_0027: newobj ""int?..ctor(int)""
IL_002c: stfld ""int? C.f1""
IL_0031: ldloc.1
IL_0032: br.s IL_0035
IL_0034: ldloc.1
IL_0035: call ""void System.Console.WriteLine(int)""
IL_003a: ret
}");
verifier.VerifyIL("C.TestObject()", @"
{
// Code size 36 (0x24)
.maxstack 3
.locals init (C V_0,
object V_1)
IL_0000: newobj ""C..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldfld ""object C.f2""
IL_000c: dup
IL_000d: brtrue.s IL_001e
IL_000f: pop
IL_0010: ldloc.0
IL_0011: call ""object C.GetObject()""
IL_0016: dup
IL_0017: stloc.1
IL_0018: stfld ""object C.f2""
IL_001d: ldloc.1
IL_001e: call ""void System.Console.WriteLine(object)""
IL_0023: ret
}");
}
[ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/mono/mono/issues/11036")]
public void IndexerLvalue()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
public static void Main()
{
TestInt();
TestObject();
}
static void TestInt()
{
int?[] a = new int?[1];
Console.WriteLine(a[GetInt()] ??= 1);
Console.WriteLine(a[GetInt()] ??= 2);
}
static void TestObject()
{
object[] a = new object[1];
Console.WriteLine(a[GetInt()] ??= 1);
Console.WriteLine(a[GetInt()] ??= 2);
}
static int GetInt()
{
Console.WriteLine(""In GetInt"");
return 0;
}
}
", expectedOutput: @"
In GetInt
1
In GetInt
1
In GetInt
1
In GetInt
1
");
verifier.VerifyIL("C.TestInt()", @"
{
// Code size 106 (0x6a)
.maxstack 3
.locals init (int?& V_0,
int V_1)
IL_0000: ldc.i4.1
IL_0001: newarr ""int?""
IL_0006: dup
IL_0007: call ""int C.GetInt()""
IL_000c: ldelema ""int?""
IL_0011: stloc.0
IL_0012: ldloc.0
IL_0013: call ""int int?.GetValueOrDefault()""
IL_0018: stloc.1
IL_0019: ldloc.0
IL_001a: call ""bool int?.HasValue.get""
IL_001f: brtrue.s IL_0032
IL_0021: ldc.i4.1
IL_0022: stloc.1
IL_0023: ldloc.0
IL_0024: ldloc.1
IL_0025: newobj ""int?..ctor(int)""
IL_002a: stobj ""int?""
IL_002f: ldloc.1
IL_0030: br.s IL_0033
IL_0032: ldloc.1
IL_0033: call ""void System.Console.WriteLine(int)""
IL_0038: call ""int C.GetInt()""
IL_003d: ldelema ""int?""
IL_0042: stloc.0
IL_0043: ldloc.0
IL_0044: call ""int int?.GetValueOrDefault()""
IL_0049: stloc.1
IL_004a: ldloc.0
IL_004b: call ""bool int?.HasValue.get""
IL_0050: brtrue.s IL_0063
IL_0052: ldc.i4.2
IL_0053: stloc.1
IL_0054: ldloc.0
IL_0055: ldloc.1
IL_0056: newobj ""int?..ctor(int)""
IL_005b: stobj ""int?""
IL_0060: ldloc.1
IL_0061: br.s IL_0064
IL_0063: ldloc.1
IL_0064: call ""void System.Console.WriteLine(int)""
IL_0069: ret
}
");
verifier.VerifyIL("C.TestObject()", @"
{
// Code size 70 (0x46)
.maxstack 5
.locals init (object[] V_0,
int V_1,
object V_2)
IL_0000: ldc.i4.1
IL_0001: newarr ""object""
IL_0006: dup
IL_0007: stloc.0
IL_0008: call ""int C.GetInt()""
IL_000d: stloc.1
IL_000e: ldloc.0
IL_000f: ldloc.1
IL_0010: ldelem.ref
IL_0011: dup
IL_0012: brtrue.s IL_0021
IL_0014: pop
IL_0015: ldloc.0
IL_0016: ldloc.1
IL_0017: ldc.i4.1
IL_0018: box ""int""
IL_001d: dup
IL_001e: stloc.2
IL_001f: stelem.ref
IL_0020: ldloc.2
IL_0021: call ""void System.Console.WriteLine(object)""
IL_0026: stloc.0
IL_0027: call ""int C.GetInt()""
IL_002c: stloc.1
IL_002d: ldloc.0
IL_002e: ldloc.1
IL_002f: ldelem.ref
IL_0030: dup
IL_0031: brtrue.s IL_0040
IL_0033: pop
IL_0034: ldloc.0
IL_0035: ldloc.1
IL_0036: ldc.i4.2
IL_0037: box ""int""
IL_003c: dup
IL_003d: stloc.2
IL_003e: stelem.ref
IL_003f: ldloc.2
IL_0040: call ""void System.Console.WriteLine(object)""
IL_0045: ret
}
");
}
[Fact]
public void RefIndexerLvalue()
{
var source = @"
using System;
class C
{
public static void Main()
{
var d = new D();
Console.WriteLine(d[0] ??= ""Test String"");
}
}
class D
{
object field = null;
public ref object this[int i]
{
get => ref field;
}
}
";
var verifier = CompileAndVerify(source, expectedOutput: @"
Test String
");
verifier.VerifyIL("C.Main()", expectedIL: @"
{
// Code size 34 (0x22)
.maxstack 3
.locals init (object& V_0,
object V_1)
IL_0000: newobj ""D..ctor()""
IL_0005: ldc.i4.0
IL_0006: callvirt ""ref object D.this[int].get""
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: ldind.ref
IL_000e: dup
IL_000f: brtrue.s IL_001c
IL_0011: pop
IL_0012: ldloc.0
IL_0013: ldstr ""Test String""
IL_0018: dup
IL_0019: stloc.1
IL_001a: stind.ref
IL_001b: ldloc.1
IL_001c: call ""void System.Console.WriteLine(object)""
IL_0021: ret
}
");
}
[Fact]
public void PropertyLvalue()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
int? f1 = null;
int? P1
{
get
{
Console.WriteLine(""In Get P1"");
return f1;
}
set
{
Console.WriteLine(""In Set P1"");
f1 = value;
}
}
object f2 = null;
object P2
{
get
{
Console.WriteLine(""In Get P2"");
return f2;
}
set
{
Console.WriteLine(""In Set P2"");
f2 = value;
}
}
public static int GetInt(int i)
{
Console.WriteLine(""In GetInt"");
return i;
}
public static void Main()
{
TestInt();
TestObject();
}
public static void TestInt()
{
var c = new C();
Console.WriteLine(c.P1 ??= GetInt(1));
Console.WriteLine(c.P1 ??= GetInt(2));
}
public static void TestObject()
{
var c = new C();
Console.WriteLine(c.P2 ??= GetInt(3));
Console.WriteLine(c.P2 ??= GetInt(4));
}
}
", expectedOutput: @"
In Get P1
In GetInt
In Set P1
1
In Get P1
1
In Get P2
In GetInt
In Set P2
3
In Get P2
3");
verifier.VerifyIL("C.TestInt()", @"
{
// Code size 119 (0x77)
.maxstack 4
.locals init (C V_0,
int? V_1,
int V_2,
int? V_3)
IL_0000: newobj ""C..ctor()""
IL_0005: dup
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt ""int? C.P1.get""
IL_000d: stloc.1
IL_000e: ldloca.s V_1
IL_0010: call ""int int?.GetValueOrDefault()""
IL_0015: stloc.2
IL_0016: ldloca.s V_1
IL_0018: call ""bool int?.HasValue.get""
IL_001d: brtrue.s IL_0038
IL_001f: ldc.i4.1
IL_0020: call ""int C.GetInt(int)""
IL_0025: stloc.2
IL_0026: ldloc.0
IL_0027: ldloca.s V_3
IL_0029: ldloc.2
IL_002a: call ""int?..ctor(int)""
IL_002f: ldloc.3
IL_0030: callvirt ""void C.P1.set""
IL_0035: ldloc.2
IL_0036: br.s IL_0039
IL_0038: ldloc.2
IL_0039: call ""void System.Console.WriteLine(int)""
IL_003e: stloc.0
IL_003f: ldloc.0
IL_0040: callvirt ""int? C.P1.get""
IL_0045: stloc.1
IL_0046: ldloca.s V_1
IL_0048: call ""int int?.GetValueOrDefault()""
IL_004d: stloc.2
IL_004e: ldloca.s V_1
IL_0050: call ""bool int?.HasValue.get""
IL_0055: brtrue.s IL_0070
IL_0057: ldc.i4.2
IL_0058: call ""int C.GetInt(int)""
IL_005d: stloc.2
IL_005e: ldloc.0
IL_005f: ldloca.s V_3
IL_0061: ldloc.2
IL_0062: call ""int?..ctor(int)""
IL_0067: ldloc.3
IL_0068: callvirt ""void C.P1.set""
IL_006d: ldloc.2
IL_006e: br.s IL_0071
IL_0070: ldloc.2
IL_0071: call ""void System.Console.WriteLine(int)""
IL_0076: ret
}");
verifier.VerifyIL("C.TestObject()", @"
{
// Code size 79 (0x4f)
.maxstack 4
.locals init (C V_0,
object V_1)
IL_0000: newobj ""C..ctor()""
IL_0005: dup
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt ""object C.P2.get""
IL_000d: dup
IL_000e: brtrue.s IL_0025
IL_0010: pop
IL_0011: ldloc.0
IL_0012: ldc.i4.3
IL_0013: call ""int C.GetInt(int)""
IL_0018: box ""int""
IL_001d: dup
IL_001e: stloc.1
IL_001f: callvirt ""void C.P2.set""
IL_0024: ldloc.1
IL_0025: call ""void System.Console.WriteLine(object)""
IL_002a: stloc.0
IL_002b: ldloc.0
IL_002c: callvirt ""object C.P2.get""
IL_0031: dup
IL_0032: brtrue.s IL_0049
IL_0034: pop
IL_0035: ldloc.0
IL_0036: ldc.i4.4
IL_0037: call ""int C.GetInt(int)""
IL_003c: box ""int""
IL_0041: dup
IL_0042: stloc.1
IL_0043: callvirt ""void C.P2.set""
IL_0048: ldloc.1
IL_0049: call ""void System.Console.WriteLine(object)""
IL_004e: ret
}
");
}
[Fact]
public void EventLvalue()
{
CompileAndVerify(@"
using System;
class C
{
static event EventHandler E;
public static void Main()
{
E ??= (sender, args) => {};
}
}").VerifyIL("C.Main()", @"
{
// Code size 44 (0x2c)
.maxstack 2
IL_0000: ldsfld ""System.EventHandler C.E""
IL_0005: brtrue.s IL_002b
IL_0007: ldsfld ""System.EventHandler C.<>c.<>9__3_0""
IL_000c: dup
IL_000d: brtrue.s IL_0026
IL_000f: pop
IL_0010: ldsfld ""C.<>c C.<>c.<>9""
IL_0015: ldftn ""void C.<>c.<Main>b__3_0(object, System.EventArgs)""
IL_001b: newobj ""System.EventHandler..ctor(object, System.IntPtr)""
IL_0020: dup
IL_0021: stsfld ""System.EventHandler C.<>c.<>9__3_0""
IL_0026: stsfld ""System.EventHandler C.E""
IL_002b: ret
}
");
}
[Fact]
public void InvalidLHS()
{
CreateCompilation(@"
public class C
{
int? WriteOnlyProperty { set {} }
int? ReadOnlyProperty { get; }
public static void Main()
{
var c = new C();
c.WriteOnlyProperty ??= 1; // Non rvalue write only
c.ReadOnlyProperty ??= 1; // Non lvalue readonly
GetInt() ??= 1; // Non lvalue method invocation
GetInt ??= null; // Non lvalue method group
() => {} ??= null; // Non lvalue lambda
}
static int? GetInt() => null;
}
").VerifyDiagnostics(
// (10,9): error CS0154: The property or indexer 'C.WriteOnlyProperty' cannot be used in this context because it lacks the get accessor
// c.WriteOnlyProperty ??= 1; // Non rvalue write only
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c.WriteOnlyProperty").WithArguments("C.WriteOnlyProperty").WithLocation(10, 9),
// (11,9): error CS0200: Property or indexer 'C.ReadOnlyProperty' cannot be assigned to -- it is read only
// c.ReadOnlyProperty ??= 1; // Non lvalue readonly
Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "c.ReadOnlyProperty").WithArguments("C.ReadOnlyProperty").WithLocation(11, 9),
// (12,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// GetInt() ??= 1; // Non lvalue method invocation
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "GetInt()").WithLocation(12, 9),
// (13,9): error CS1656: Cannot assign to 'GetInt' because it is a 'method group'
// GetInt ??= null; // Non lvalue method group
Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "GetInt").WithArguments("GetInt", "method group").WithLocation(13, 9),
// (14,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// () => {} ??= null; // Non lvalue lambda
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "() => {}").WithLocation(14, 9)
);
}
[Fact]
public void ValidRHS()
{
CompileAndVerify(@"
using System;
public class C
{
public static void Main()
{
Action a = null;
(a ??= TestMethod)();
(a ??= () => {})();
}
static void TestMethod() => Console.WriteLine(""In TestMethod"");
}
", parseOptions: TestOptions.Regular10, expectedOutput: @"
In TestMethod
In TestMethod
").VerifyIL("C.Main()", @"
{
// Code size 70 (0x46)
.maxstack 2
.locals init (System.Action V_0) //a
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: dup
IL_0004: brtrue.s IL_0015
IL_0006: pop
IL_0007: ldnull
IL_0008: ldftn ""void C.TestMethod()""
IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)""
IL_0013: dup
IL_0014: stloc.0
IL_0015: callvirt ""void System.Action.Invoke()""
IL_001a: ldloc.0
IL_001b: dup
IL_001c: brtrue.s IL_0040
IL_001e: pop
IL_001f: ldsfld ""System.Action C.<>c.<>9__0_0""
IL_0024: dup
IL_0025: brtrue.s IL_003e
IL_0027: pop
IL_0028: ldsfld ""C.<>c C.<>c.<>9""
IL_002d: ldftn ""void C.<>c.<Main>b__0_0()""
IL_0033: newobj ""System.Action..ctor(object, System.IntPtr)""
IL_0038: dup
IL_0039: stsfld ""System.Action C.<>c.<>9__0_0""
IL_003e: dup
IL_003f: stloc.0
IL_0040: callvirt ""void System.Action.Invoke()""
IL_0045: ret
}
");
}
[Fact]
public void InvalidRHS()
{
CreateCompilation(@"
public class C
{
static int P1 { set {} }
public static void Main()
{
var c = new C();
c ??= P1;
}
}
").VerifyDiagnostics(
// (9,15): error CS0154: The property or indexer 'C.P1' cannot be used in this context because it lacks the get accessor
// c ??= P1;
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "P1").WithArguments("C.P1").WithLocation(9, 15)
);
}
[Fact]
public void RefReturnLvalue()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
int? f1 = null;
ref int? P1
{
get
{
Console.WriteLine(""In Get P1"");
return ref f1;
}
}
ref int? GetF1()
{
Console.WriteLine(""In GetF1"");
return ref f1;
}
object f2 = null;
ref object P2
{
get
{
Console.WriteLine(""In Get P2"");
return ref f2;
}
}
ref object GetF2()
{
Console.WriteLine(""In GetF2"");
return ref f2;
}
public static void TestInt()
{
var c = new C();
Console.WriteLine(c.P1 ??= 1);
c.f1 = null;
Console.WriteLine(c.GetF1() ??= 2);
}
public static void TestObject()
{
var c = new C();
Console.WriteLine(c.P2 ??= 1);
c.f2 = null;
Console.WriteLine(c.GetF2() ??= 2);
}
public static void Main()
{
TestInt();
TestObject();
}
}
", expectedOutput: @"
In Get P1
1
In GetF1
2
In Get P2
1
In GetF2
2");
verifier.VerifyIL("C.TestInt()", @"
{
// Code size 107 (0x6b)
.maxstack 3
.locals init (int?& V_0,
int V_1)
IL_0000: newobj ""C..ctor()""
IL_0005: dup
IL_0006: callvirt ""ref int? C.P1.get""
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: call ""int int?.GetValueOrDefault()""
IL_0012: stloc.1
IL_0013: ldloc.0
IL_0014: call ""bool int?.HasValue.get""
IL_0019: brtrue.s IL_002c
IL_001b: ldc.i4.1
IL_001c: stloc.1
IL_001d: ldloc.0
IL_001e: ldloc.1
IL_001f: newobj ""int?..ctor(int)""
IL_0024: stobj ""int?""
IL_0029: ldloc.1
IL_002a: br.s IL_002d
IL_002c: ldloc.1
IL_002d: call ""void System.Console.WriteLine(int)""
IL_0032: dup
IL_0033: ldflda ""int? C.f1""
IL_0038: initobj ""int?""
IL_003e: callvirt ""ref int? C.GetF1()""
IL_0043: stloc.0
IL_0044: ldloc.0
IL_0045: call ""int int?.GetValueOrDefault()""
IL_004a: stloc.1
IL_004b: ldloc.0
IL_004c: call ""bool int?.HasValue.get""
IL_0051: brtrue.s IL_0064
IL_0053: ldc.i4.2
IL_0054: stloc.1
IL_0055: ldloc.0
IL_0056: ldloc.1
IL_0057: newobj ""int?..ctor(int)""
IL_005c: stobj ""int?""
IL_0061: ldloc.1
IL_0062: br.s IL_0065
IL_0064: ldloc.1
IL_0065: call ""void System.Console.WriteLine(int)""
IL_006a: ret
}");
verifier.VerifyIL("C.TestObject()", @"
{
// Code size 70 (0x46)
.maxstack 4
.locals init (object& V_0,
object V_1)
IL_0000: newobj ""C..ctor()""
IL_0005: dup
IL_0006: callvirt ""ref object C.P2.get""
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: ldind.ref
IL_000e: dup
IL_000f: brtrue.s IL_001d
IL_0011: pop
IL_0012: ldloc.0
IL_0013: ldc.i4.1
IL_0014: box ""int""
IL_0019: dup
IL_001a: stloc.1
IL_001b: stind.ref
IL_001c: ldloc.1
IL_001d: call ""void System.Console.WriteLine(object)""
IL_0022: dup
IL_0023: ldnull
IL_0024: stfld ""object C.f2""
IL_0029: callvirt ""ref object C.GetF2()""
IL_002e: stloc.0
IL_002f: ldloc.0
IL_0030: ldind.ref
IL_0031: dup
IL_0032: brtrue.s IL_0040
IL_0034: pop
IL_0035: ldloc.0
IL_0036: ldc.i4.2
IL_0037: box ""int""
IL_003c: dup
IL_003d: stloc.1
IL_003e: stind.ref
IL_003f: ldloc.1
IL_0040: call ""void System.Console.WriteLine(object)""
IL_0045: ret
}
");
}
[Fact]
public void RefReadonlyReturnLvalue()
{
CreateCompilation(@"
using System;
public class C
{
int? f1 = null;
ref readonly int? P1
{
get
{
Console.WriteLine(""In Get P1"");
return ref f1;
}
}
ref readonly int? GetF1()
{
Console.WriteLine(""In GetF1"");
return ref f1;
}
public static void Main()
{
var c = new C();
Console.WriteLine(c.P1 ??= 1);
c.f1 = null;
Console.WriteLine(c.GetF1() ??= 2);
}
}").VerifyDiagnostics(
// (24,27): error CS8331: Cannot assign to property 'P1' or use it as the right hand side of a ref assignment because it is a readonly variable
// Console.WriteLine(c.P1 ??= 1);
Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.P1").WithArguments("property", "P1").WithLocation(24, 27),
// (26,27): error CS8331: Cannot assign to method 'GetF1' or use it as the right hand side of a ref assignment because it is a readonly variable
// Console.WriteLine(c.GetF1() ??= 2);
Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.GetF1()").WithArguments("method", "GetF1").WithLocation(26, 27)
);
}
[Fact]
public void NonNullableLHS()
{
CreateCompilation(@"
public class C
{
public static void Main()
{
int i1 = 0;
i1 ??= 0;
}
}").VerifyDiagnostics(
// (8,9): error CS0019: Operator '??=' cannot be applied to operands of type 'int' and 'int'
// i1 ??= 0;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "i1 ??= 0").WithArguments("??=", "int", "int").WithLocation(7, 9)
);
}
[Fact]
public void ReferenceTypeLHS()
{
CompileAndVerify(@"
public class C
{
static C P1 { get; set; }
public static void Main()
{
Test(P1 ??= new C());
}
static void Test(C test) {}
}").VerifyIL("C.Main()", expectedIL: @"
{
// Code size 26 (0x1a)
.maxstack 2
IL_0000: call ""C C.P1.get""
IL_0005: dup
IL_0006: brtrue.s IL_0014
IL_0008: pop
IL_0009: newobj ""C..ctor()""
IL_000e: dup
IL_000f: call ""void C.P1.set""
IL_0014: call ""void C.Test(C)""
IL_0019: ret
}
");
}
[Fact]
public void RefVariable()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
static int? f1 = null;
static object f2 = null;
public static void TestInt()
{
ref int? i1 = ref f1;
Console.WriteLine(i1 ??= 1);
MInt(ref f1);
Console.WriteLine(f1);
}
public static void MInt(ref int? i)
{
Console.WriteLine(i ??= 2);
i = null;
Console.WriteLine(i ??= 3);
}
public static void TestObject()
{
ref object o1 = ref f2;
Console.WriteLine(o1 ??= 1);
MObject(ref f2);
Console.WriteLine(f2);
}
public static void MObject(ref object o)
{
Console.WriteLine(o ??= 2);
o = null;
Console.WriteLine(o ??= 3);
}
public static void Main()
{
TestInt();
TestObject();
}
}", expectedOutput: @"
1
1
3
3
1
1
3
3
");
verifier.VerifyIL("C.TestInt()", expectedIL: @"
{
// Code size 70 (0x46)
.maxstack 2
.locals init (int?& V_0, //i1
int V_1)
IL_0000: ldsflda ""int? C.f1""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call ""int int?.GetValueOrDefault()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: call ""bool int?.HasValue.get""
IL_0013: brtrue.s IL_0026
IL_0015: ldc.i4.1
IL_0016: stloc.1
IL_0017: ldloc.0
IL_0018: ldloc.1
IL_0019: newobj ""int?..ctor(int)""
IL_001e: stobj ""int?""
IL_0023: ldloc.1
IL_0024: br.s IL_0027
IL_0026: ldloc.1
IL_0027: call ""void System.Console.WriteLine(int)""
IL_002c: ldsflda ""int? C.f1""
IL_0031: call ""void C.MInt(ref int?)""
IL_0036: ldsfld ""int? C.f1""
IL_003b: box ""int?""
IL_0040: call ""void System.Console.WriteLine(object)""
IL_0045: ret
}");
verifier.VerifyIL("C.MInt(ref int?)", expectedIL: @"
{
// Code size 84 (0x54)
.maxstack 2
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: call ""int int?.GetValueOrDefault()""
IL_0006: stloc.0
IL_0007: ldarg.0
IL_0008: call ""bool int?.HasValue.get""
IL_000d: brtrue.s IL_0020
IL_000f: ldc.i4.2
IL_0010: stloc.0
IL_0011: ldarg.0
IL_0012: ldloc.0
IL_0013: newobj ""int?..ctor(int)""
IL_0018: stobj ""int?""
IL_001d: ldloc.0
IL_001e: br.s IL_0021
IL_0020: ldloc.0
IL_0021: call ""void System.Console.WriteLine(int)""
IL_0026: ldarg.0
IL_0027: initobj ""int?""
IL_002d: ldarg.0
IL_002e: call ""int int?.GetValueOrDefault()""
IL_0033: stloc.0
IL_0034: ldarg.0
IL_0035: call ""bool int?.HasValue.get""
IL_003a: brtrue.s IL_004d
IL_003c: ldc.i4.3
IL_003d: stloc.0
IL_003e: ldarg.0
IL_003f: ldloc.0
IL_0040: newobj ""int?..ctor(int)""
IL_0045: stobj ""int?""
IL_004a: ldloc.0
IL_004b: br.s IL_004e
IL_004d: ldloc.0
IL_004e: call ""void System.Console.WriteLine(int)""
IL_0053: ret
}
");
verifier.VerifyIL("C.TestObject()", expectedIL: @"
{
// Code size 49 (0x31)
.maxstack 3
.locals init (object& V_0, //o1
object V_1)
IL_0000: ldsflda ""object C.f2""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldind.ref
IL_0008: dup
IL_0009: brtrue.s IL_0017
IL_000b: pop
IL_000c: ldloc.0
IL_000d: ldc.i4.1
IL_000e: box ""int""
IL_0013: dup
IL_0014: stloc.1
IL_0015: stind.ref
IL_0016: ldloc.1
IL_0017: call ""void System.Console.WriteLine(object)""
IL_001c: ldsflda ""object C.f2""
IL_0021: call ""void C.MObject(ref object)""
IL_0026: ldsfld ""object C.f2""
IL_002b: call ""void System.Console.WriteLine(object)""
IL_0030: ret
}
");
verifier.VerifyIL("C.MObject(ref object)", expectedIL: @"
{
// Code size 48 (0x30)
.maxstack 3
.locals init (object V_0)
IL_0000: ldarg.0
IL_0001: ldind.ref
IL_0002: dup
IL_0003: brtrue.s IL_0011
IL_0005: pop
IL_0006: ldarg.0
IL_0007: ldc.i4.2
IL_0008: box ""int""
IL_000d: dup
IL_000e: stloc.0
IL_000f: stind.ref
IL_0010: ldloc.0
IL_0011: call ""void System.Console.WriteLine(object)""
IL_0016: ldarg.0
IL_0017: ldnull
IL_0018: stind.ref
IL_0019: ldarg.0
IL_001a: ldind.ref
IL_001b: dup
IL_001c: brtrue.s IL_002a
IL_001e: pop
IL_001f: ldarg.0
IL_0020: ldc.i4.3
IL_0021: box ""int""
IL_0026: dup
IL_0027: stloc.0
IL_0028: stind.ref
IL_0029: ldloc.0
IL_002a: call ""void System.Console.WriteLine(object)""
IL_002f: ret
}
");
}
[Fact]
public void RefAssignment()
{
var source1 = @"
using System;
class C
{
static object f1 = null;
static object f2 = ""F2"";
static void Main()
{
ref object o1 = ref f1;
ref object o2 = ref f2;
Console.WriteLine(o1 ??= o2);
}
}";
CompileAndVerify(source1, expectedOutput: "F2").VerifyIL("C.Main()", expectedIL: @"
{
// Code size 31 (0x1f)
.maxstack 3
.locals init (object& V_0, //o1
object& V_1, //o2
object V_2)
IL_0000: ldsflda ""object C.f1""
IL_0005: stloc.0
IL_0006: ldsflda ""object C.f2""
IL_000b: stloc.1
IL_000c: ldloc.0
IL_000d: ldind.ref
IL_000e: dup
IL_000f: brtrue.s IL_0019
IL_0011: pop
IL_0012: ldloc.0
IL_0013: ldloc.1
IL_0014: ldind.ref
IL_0015: dup
IL_0016: stloc.2
IL_0017: stind.ref
IL_0018: ldloc.2
IL_0019: call ""void System.Console.WriteLine(object)""
IL_001e: ret
}
");
var source2 = @"
class C
{
object f1;
void M()
{
ref object o1 = ref f1;
ref object o2 = ref f1;
o1 ??= ref o2;
ref o1 ??= ref o2;
}
}";
CreateCompilation(source2).VerifyDiagnostics(
// (9,16): error CS1525: Invalid expression term 'ref'
// o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref o2").WithArguments("ref").WithLocation(9, 16),
// (9,16): error CS1073: Unexpected token 'ref'
// o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 16),
// (10,13): error CS0118: 'o1' is a variable but is used like a type
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_BadSKknown, "o1").WithArguments("o1", "variable", "type").WithLocation(10, 13),
// (10,16): error CS1001: Identifier expected
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_IdentifierExpected, "??=").WithLocation(10, 16),
// (10,16): error CS1002: ; expected
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_SemicolonExpected, "??=").WithLocation(10, 16),
// (10,16): error CS1525: Invalid expression term '??='
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "??=").WithArguments("??=").WithLocation(10, 16),
// (10,16): error CS8174: A declaration of a by-reference variable must have an initializer
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_ByReferenceVariableMustBeInitialized, "").WithLocation(10, 16),
// (10,20): error CS1525: Invalid expression term 'ref'
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref o2").WithArguments("ref").WithLocation(10, 20),
// (10,20): error CS1073: Unexpected token 'ref'
// ref o1 ??= ref o2;
Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(10, 20));
}
[Fact]
public void InParameter()
{
var source = @"
using System;
class C
{
static void Main()
{
M1(null);
}
static void M1(object o)
{
M2(o ??= ""Test String"");
}
static void M2(in object o) => Console.WriteLine(o);
}";
var verifier = CompileAndVerify(source, expectedOutput: "Test String");
verifier.VerifyIL("C.M1(object)", expectedIL: @"
{
// Code size 22 (0x16)
.maxstack 2
.locals init (object V_0)
IL_0000: ldarg.0
IL_0001: dup
IL_0002: brtrue.s IL_000d
IL_0004: pop
IL_0005: ldstr ""Test String""
IL_000a: dup
IL_000b: starg.s V_0
IL_000d: stloc.0
IL_000e: ldloca.s V_0
IL_0010: call ""void C.M2(in object)""
IL_0015: ret
}
");
var source2 = @"
class C
{
static void Main()
{
object o = null;
M(in (o ??= ""Test String""));
}
static void M(in object o) {}
}";
CreateCompilation(source2).VerifyDiagnostics(
// (7,15): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// M(in (o ??= "Test String"));
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, @"o ??= ""Test String""").WithLocation(7, 15)
);
}
[Fact]
public void AsyncLHSAndRHS()
{
CompileAndVerify(@"
using System;
using System.Threading.Tasks;
public class C
{
public static async Task Main()
{
Console.WriteLine((await Run()).P ??= 1);
int? i = null;
Console.WriteLine(i ??= await GetIntAsync(2));
Console.WriteLine(i ??= await GetIntAsync(3));
}
int? f1 = null;
int? P {
get
{
Console.WriteLine(""In Get"");
return f1;
}
// Purposefully don't set field to ensure property isn't read twice (wrong value will print if it is)
set => Console.WriteLine(""In Set"");
}
static Task<C> Run()
{
return Task.Run(() => new C());
}
static Task<int> GetIntAsync(int i)
{
Console.WriteLine(""In GetInt"");
return Task.Run(() => i);
}
}", expectedOutput: @"
In Get
In Set
1
In GetInt
2
2");
}
[Fact]
public void TupleLHS()
{
var verifier = CompileAndVerify(@"
using System;
public class C
{
public static void Main()
{
TestInt();
TestObject();
}
public static void TestInt()
{
(int, int)? a = null;
Console.WriteLine(a ??= (1, 2));
}
public static void TestObject()
{
(object f1, object f2)? b = null;
Console.WriteLine(b ??= (f3: null, f4: null));
}
}", expectedOutput: @"
(1, 2)
(, )
");
verifier.VerifyIL("C.TestInt()", expectedIL: @"
{
// Code size 57 (0x39)
.maxstack 3
.locals init (System.ValueTuple<int, int>? V_0, //a
System.ValueTuple<int, int> V_1)
IL_0000: ldloca.s V_0
IL_0002: initobj ""System.ValueTuple<int, int>?""
IL_0008: ldloca.s V_0
IL_000a: call ""System.ValueTuple<int, int> System.ValueTuple<int, int>?.GetValueOrDefault()""
IL_000f: stloc.1
IL_0010: ldloca.s V_0
IL_0012: call ""bool System.ValueTuple<int, int>?.HasValue.get""
IL_0017: brtrue.s IL_002d
IL_0019: ldloca.s V_1
IL_001b: ldc.i4.1
IL_001c: ldc.i4.2
IL_001d: call ""System.ValueTuple<int, int>..ctor(int, int)""
IL_0022: ldloca.s V_0
IL_0024: ldloc.1
IL_0025: call ""System.ValueTuple<int, int>?..ctor(System.ValueTuple<int, int>)""
IL_002a: ldloc.1
IL_002b: br.s IL_002e
IL_002d: ldloc.1
IL_002e: box ""System.ValueTuple<int, int>""
IL_0033: call ""void System.Console.WriteLine(object)""
IL_0038: ret
}");
verifier.VerifyIL("C.TestObject()", expectedIL: @"
{
// Code size 57 (0x39)
.maxstack 3
.locals init (System.ValueTuple<object, object>? V_0, //b
System.ValueTuple<object, object> V_1)
IL_0000: ldloca.s V_0
IL_0002: initobj ""System.ValueTuple<object, object>?""
IL_0008: ldloca.s V_0
IL_000a: call ""System.ValueTuple<object, object> System.ValueTuple<object, object>?.GetValueOrDefault()""
IL_000f: stloc.1
IL_0010: ldloca.s V_0
IL_0012: call ""bool System.ValueTuple<object, object>?.HasValue.get""
IL_0017: brtrue.s IL_002d
IL_0019: ldloca.s V_1
IL_001b: ldnull
IL_001c: ldnull
IL_001d: call ""System.ValueTuple<object, object>..ctor(object, object)""
IL_0022: ldloca.s V_0
IL_0024: ldloc.1
IL_0025: call ""System.ValueTuple<object, object>?..ctor(System.ValueTuple<object, object>)""
IL_002a: ldloc.1
IL_002b: br.s IL_002e
IL_002d: ldloc.1
IL_002e: box ""System.ValueTuple<object, object>""
IL_0033: call ""void System.Console.WriteLine(object)""
IL_0038: ret
}");
}
[Fact]
public void ValidRHSConversions()
{
var compilation = CreateCompilation(@"
class C
{
public static void Main()
{
// Implicit reference conversion
C c1 = null;
UseParam(c1 ??= new D());
// Implicit user-defined conversion
C c2 = null;
UseParam(c2 ??= new E());
// Implicit user-defined conversion on the rhs type
C c3 = null;
UseParam(c3 ??= new F());
// Implicit user-defined conversion to the underlying non-nullable type of lhs
int? i1 = null;
UseParam(i1 ??= new C());
// Implicit conversion from null to reference type
C c4 = null;
UseParam(c4 ??= null);
// Implicit conversion from default literal to reference type
C c5 = null;
UseParam(c5 ??= default);
int? i2 = null;
UseParamAsInt(i2 ??= default);
}
static void UseParam(object o) {}
static void UseParamAsInt(int i) {}
static public implicit operator C(E e) => null;
static public implicit operator int(C c) => 0;
}
class D : C {}
class E {}
class F
{
static public implicit operator C(F e) => null;
}
");
compilation.VerifyDiagnostics();
CompileAndVerify(compilation).VerifyIL("C.Main()", @"
{
// Code size 180 (0xb4)
.maxstack 2
.locals init (int? V_0, //i1
int? V_1, //i2
int V_2)
IL_0000: ldnull
IL_0001: dup
IL_0002: brtrue.s IL_000a
IL_0004: pop
IL_0005: newobj ""D..ctor()""
IL_000a: call ""void C.UseParam(object)""
IL_000f: ldnull
IL_0010: dup
IL_0011: brtrue.s IL_001e
IL_0013: pop
IL_0014: newobj ""E..ctor()""
IL_0019: call ""C C.op_Implicit(E)""
IL_001e: call ""void C.UseParam(object)""
IL_0023: ldnull
IL_0024: dup
IL_0025: brtrue.s IL_0032
IL_0027: pop
IL_0028: newobj ""F..ctor()""
IL_002d: call ""C F.op_Implicit(F)""
IL_0032: call ""void C.UseParam(object)""
IL_0037: ldloca.s V_0
IL_0039: initobj ""int?""
IL_003f: ldloca.s V_0
IL_0041: call ""int int?.GetValueOrDefault()""
IL_0046: stloc.2
IL_0047: ldloca.s V_0
IL_0049: call ""bool int?.HasValue.get""
IL_004e: brtrue.s IL_0066
IL_0050: newobj ""C..ctor()""
IL_0055: call ""int C.op_Implicit(C)""
IL_005a: stloc.2
IL_005b: ldloca.s V_0
IL_005d: ldloc.2
IL_005e: call ""int?..ctor(int)""
IL_0063: ldloc.2
IL_0064: br.s IL_0067
IL_0066: ldloc.2
IL_0067: box ""int""
IL_006c: call ""void C.UseParam(object)""
IL_0071: ldnull
IL_0072: dup
IL_0073: brtrue.s IL_0077
IL_0075: pop
IL_0076: ldnull
IL_0077: call ""void C.UseParam(object)""
IL_007c: ldnull
IL_007d: dup
IL_007e: brtrue.s IL_0082
IL_0080: pop
IL_0081: ldnull
IL_0082: call ""void C.UseParam(object)""
IL_0087: ldloca.s V_1
IL_0089: initobj ""int?""
IL_008f: ldloca.s V_1
IL_0091: call ""int int?.GetValueOrDefault()""
IL_0096: stloc.2
IL_0097: ldloca.s V_1
IL_0099: call ""bool int?.HasValue.get""
IL_009e: brtrue.s IL_00ad
IL_00a0: ldc.i4.0
IL_00a1: stloc.2
IL_00a2: ldloca.s V_1
IL_00a4: ldloc.2
IL_00a5: call ""int?..ctor(int)""
IL_00aa: ldloc.2
IL_00ab: br.s IL_00ae
IL_00ad: ldloc.2
IL_00ae: call ""void C.UseParamAsInt(int)""
IL_00b3: ret
}");
}
[Fact]
public void InvalidRHSConversions()
{
CreateCompilation(@"
class C
{
public void M()
{
// Explicit numeric conversion
int? i1 = null;
i1 ??= 1.0;
// Explicit reference conversion
D d1 = null;
d1 ??= new C();
// Explicit user-defined conversion
C c1 = null;
c1 ??= new E();
// No conversion between lhs and rhs
C c2 = null;
c2 ??= new F();
}
static public explicit operator C(E e) => null;
}
class D : C {}
class E {}
class F {}
").VerifyDiagnostics(
// (8,9): error CS0019: Operator '??=' cannot be applied to operands of type 'int?' and 'double'
// i1 ??= 1.0;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "i1 ??= 1.0").WithArguments("??=", "int?", "double").WithLocation(8, 9),
// (12,9): error CS0019: Operator '??=' cannot be applied to operands of type 'D' and 'C'
// d1 ??= new C();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "d1 ??= new C()").WithArguments("??=", "D", "C").WithLocation(12, 9),
// (16,9): error CS0019: Operator '??=' cannot be applied to operands of type 'C' and 'E'
// c1 ??= new E();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "c1 ??= new E()").WithArguments("??=", "C", "E").WithLocation(16, 9),
// (20,9): error CS0019: Operator '??=' cannot be applied to operands of type 'C' and 'F'
// c2 ??= new F();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "c2 ??= new F()").WithArguments("??=", "C", "F").WithLocation(20, 9)
);
}
[Fact]
public void AsInvalidSubexpression()
{
CreateCompilation(@"
class C
{
public void M()
{
double? d = 1.0;
M3(d ??= 3.0);
object o = null;
M3(o ??= null);
(o ??= null) = null;
}
public void M3(C c) {}
}
").VerifyDiagnostics(
// (7,12): error CS1503: Argument 1: cannot convert from 'double' to 'C'
// M3(d ??= 3.0);
Diagnostic(ErrorCode.ERR_BadArgType, "d ??= 3.0").WithArguments("1", "double", "C").WithLocation(7, 12),
// (9,12): error CS1503: Argument 1: cannot convert from 'object' to 'C'
// M3(o ??= null);
Diagnostic(ErrorCode.ERR_BadArgType, "o ??= null").WithArguments("1", "object", "C").WithLocation(9, 12),
// (10,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// (o ??= null) = null;
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "o ??= null").WithLocation(10, 10)
);
}
[Fact]
public void DynamicLHS()
{
var verifier = CompileAndVerify(@"
using System;
using System.Collections.Generic;
public class C
{
public static void Main()
{
Console.WriteLine(VerifyField());
Console.WriteLine(VerifyProperty());
Console.WriteLine(VerifyIndexer());
}
public static int? VerifyField()
{
return GetDynamicClass().F1 ??= 0;
}
public static int? VerifyProperty()
{
return GetDynamicClass().P1 ??= 1;
}
public static int? VerifyIndexer()
{
return GetDynamicClass()[0] ??= 2;
}
public static dynamic GetDynamicClass() => new DynamicClass();
}
public class DynamicClass
{
#pragma warning disable CS0169
public int? F1;
#pragma warning restore CS0169
private int? f2;
public int? P1
{
get
{
Console.WriteLine(""In P1 Getter"");
return f2;
}
set
{
Console.WriteLine(""In P1 Setter"");
f2 = value;
}
}
private Dictionary<int, int?> dictionary;
public int? this[int i]
{
get
{
Console.WriteLine(""In Indexer Getter"");
if (dictionary == null)
{
dictionary = new Dictionary<int, int?>();
dictionary[i] = null;
}
return dictionary[i];
}
set
{
Console.WriteLine(""In Indexer Setter"");
dictionary[i] = value;
}
}
}", new[] { CSharpRef }, expectedOutput: @"
0
In P1 Getter
In P1 Setter
1
In Indexer Getter
In Indexer Setter
2");
verifier.VerifyIL("C.VerifyField()", expectedIL: @"
{
// Code size 240 (0xf0)
.maxstack 10
.locals init (object V_0)
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__1.<>p__2""
IL_0005: brtrue.s IL_002b
IL_0007: ldc.i4.0
IL_0008: ldtoken ""int?""
IL_000d: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0012: ldtoken ""C""
IL_0017: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_001c: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)""
IL_0021: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0026: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__1.<>p__2""
IL_002b: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__1.<>p__2""
IL_0030: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Target""
IL_0035: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__1.<>p__2""
IL_003a: call ""dynamic C.GetDynamicClass()""
IL_003f: stloc.0
IL_0040: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__1.<>p__0""
IL_0045: brtrue.s IL_0076
IL_0047: ldc.i4.0
IL_0048: ldstr ""F1""
IL_004d: ldtoken ""C""
IL_0052: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0057: ldc.i4.1
IL_0058: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_005d: dup
IL_005e: ldc.i4.0
IL_005f: ldc.i4.0
IL_0060: ldnull
IL_0061: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0066: stelem.ref
IL_0067: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_006c: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0071: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__1.<>p__0""
IL_0076: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__1.<>p__0""
IL_007b: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>>.Target""
IL_0080: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__1.<>p__0""
IL_0085: ldloc.0
IL_0086: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_008b: dup
IL_008c: brtrue.s IL_00ea
IL_008e: pop
IL_008f: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__1.<>p__1""
IL_0094: brtrue.s IL_00cf
IL_0096: ldc.i4.0
IL_0097: ldstr ""F1""
IL_009c: ldtoken ""C""
IL_00a1: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_00a6: ldc.i4.2
IL_00a7: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_00ac: dup
IL_00ad: ldc.i4.0
IL_00ae: ldc.i4.0
IL_00af: ldnull
IL_00b0: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00b5: stelem.ref
IL_00b6: dup
IL_00b7: ldc.i4.1
IL_00b8: ldc.i4.0
IL_00b9: ldnull
IL_00ba: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00bf: stelem.ref
IL_00c0: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_00c5: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_00ca: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__1.<>p__1""
IL_00cf: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__1.<>p__1""
IL_00d4: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>>.Target""
IL_00d9: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__1.<>p__1""
IL_00de: ldloc.0
IL_00df: ldc.i4.0
IL_00e0: box ""int""
IL_00e5: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, dynamic)""
IL_00ea: callvirt ""int? System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_00ef: ret
}
");
verifier.VerifyIL("C.VerifyProperty()", expectedIL: @"
{
// Code size 240 (0xf0)
.maxstack 10
.locals init (object V_0)
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__2.<>p__2""
IL_0005: brtrue.s IL_002b
IL_0007: ldc.i4.0
IL_0008: ldtoken ""int?""
IL_000d: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0012: ldtoken ""C""
IL_0017: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_001c: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)""
IL_0021: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0026: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__2.<>p__2""
IL_002b: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__2.<>p__2""
IL_0030: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Target""
IL_0035: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__2.<>p__2""
IL_003a: call ""dynamic C.GetDynamicClass()""
IL_003f: stloc.0
IL_0040: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__2.<>p__0""
IL_0045: brtrue.s IL_0076
IL_0047: ldc.i4.0
IL_0048: ldstr ""P1""
IL_004d: ldtoken ""C""
IL_0052: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0057: ldc.i4.1
IL_0058: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_005d: dup
IL_005e: ldc.i4.0
IL_005f: ldc.i4.0
IL_0060: ldnull
IL_0061: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0066: stelem.ref
IL_0067: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_006c: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0071: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__2.<>p__0""
IL_0076: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__2.<>p__0""
IL_007b: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>>.Target""
IL_0080: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>> C.<>o__2.<>p__0""
IL_0085: ldloc.0
IL_0086: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_008b: dup
IL_008c: brtrue.s IL_00ea
IL_008e: pop
IL_008f: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__2.<>p__1""
IL_0094: brtrue.s IL_00cf
IL_0096: ldc.i4.0
IL_0097: ldstr ""P1""
IL_009c: ldtoken ""C""
IL_00a1: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_00a6: ldc.i4.2
IL_00a7: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_00ac: dup
IL_00ad: ldc.i4.0
IL_00ae: ldc.i4.0
IL_00af: ldnull
IL_00b0: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00b5: stelem.ref
IL_00b6: dup
IL_00b7: ldc.i4.1
IL_00b8: ldc.i4.0
IL_00b9: ldnull
IL_00ba: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00bf: stelem.ref
IL_00c0: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_00c5: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_00ca: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__2.<>p__1""
IL_00cf: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__2.<>p__1""
IL_00d4: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>>.Target""
IL_00d9: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>> C.<>o__2.<>p__1""
IL_00de: ldloc.0
IL_00df: ldc.i4.1
IL_00e0: box ""int""
IL_00e5: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, dynamic)""
IL_00ea: callvirt ""int? System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_00ef: ret
}
");
verifier.VerifyIL("C.VerifyIndexer()", expectedIL: @"
{
// Code size 252 (0xfc)
.maxstack 9
.locals init (object V_0)
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__3.<>p__2""
IL_0005: brtrue.s IL_002b
IL_0007: ldc.i4.0
IL_0008: ldtoken ""int?""
IL_000d: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0012: ldtoken ""C""
IL_0017: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_001c: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)""
IL_0021: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0026: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__3.<>p__2""
IL_002b: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__3.<>p__2""
IL_0030: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>>.Target""
IL_0035: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>> C.<>o__3.<>p__2""
IL_003a: call ""dynamic C.GetDynamicClass()""
IL_003f: stloc.0
IL_0040: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>> C.<>o__3.<>p__0""
IL_0045: brtrue.s IL_007b
IL_0047: ldc.i4.0
IL_0048: ldtoken ""C""
IL_004d: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0052: ldc.i4.2
IL_0053: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_0058: dup
IL_0059: ldc.i4.0
IL_005a: ldc.i4.0
IL_005b: ldnull
IL_005c: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0061: stelem.ref
IL_0062: dup
IL_0063: ldc.i4.1
IL_0064: ldc.i4.3
IL_0065: ldnull
IL_0066: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_006b: stelem.ref
IL_006c: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_0071: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0076: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>> C.<>o__3.<>p__0""
IL_007b: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>> C.<>o__3.<>p__0""
IL_0080: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>>.Target""
IL_0085: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>> C.<>o__3.<>p__0""
IL_008a: ldloc.0
IL_008b: ldc.i4.0
IL_008c: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, int)""
IL_0091: dup
IL_0092: brtrue.s IL_00f6
IL_0094: pop
IL_0095: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>> C.<>o__3.<>p__1""
IL_009a: brtrue.s IL_00da
IL_009c: ldc.i4.0
IL_009d: ldtoken ""C""
IL_00a2: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_00a7: ldc.i4.3
IL_00a8: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_00ad: dup
IL_00ae: ldc.i4.0
IL_00af: ldc.i4.0
IL_00b0: ldnull
IL_00b1: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00b6: stelem.ref
IL_00b7: dup
IL_00b8: ldc.i4.1
IL_00b9: ldc.i4.3
IL_00ba: ldnull
IL_00bb: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00c0: stelem.ref
IL_00c1: dup
IL_00c2: ldc.i4.2
IL_00c3: ldc.i4.0
IL_00c4: ldnull
IL_00c5: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_00ca: stelem.ref
IL_00cb: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_00d0: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_00d5: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>> C.<>o__3.<>p__1""
IL_00da: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>> C.<>o__3.<>p__1""
IL_00df: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>>.Target""
IL_00e4: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>> C.<>o__3.<>p__1""
IL_00e9: ldloc.0
IL_00ea: ldc.i4.0
IL_00eb: ldc.i4.2
IL_00ec: box ""int""
IL_00f1: callvirt ""dynamic System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, int, dynamic)""
IL_00f6: callvirt ""int? System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int?>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_00fb: ret
}
");
}
[Fact]
public void DynamicRHS()
{
CompileAndVerify(@"
using System;
using System.Collections.Generic;
public class C
{
public static void Main()
{
int? d = null;
Console.WriteLine(d ??= GetDynamicClass().Property1);
Console.WriteLine(d ??= GetDynamicClass().Property2);
}
public static dynamic GetDynamicClass() => new DynamicClass();
}
public class DynamicClass
{
public int Property1
{
get
{
Console.WriteLine(""In get_Property1"");
return 1;
}
}
public int Property2
{
get
{
Console.WriteLine(""In get_Property2"");
return 2;
}
}
}
", new[] { CSharpRef }, expectedOutput: @"
In get_Property1
1
1
");
}
[Fact]
public void UseBeforeAssignment()
{
CreateCompilation(@"
public class C
{
public static void Main()
{
C c1, c2;
c1 ??= new C(); // LHS unassigned
c1 = null;
c1 ??= c2; // RHS unassigned
object x1;
object y1;
GetC(x1 = 1).Prop ??= (y1 = 2);
x1.ToString();
y1.ToString();
object x2;
object y2;
GetC(x2 = 1).Field ??= (y2 = 2);
x2.ToString();
y2.ToString();
}
static C GetC(object i) => null;
object Prop { get; set; }
object Field;
}").VerifyDiagnostics(
// (7,9): error CS0165: Use of unassigned local variable 'c1'
// c1 ??= new C(); // LHS unassigned
Diagnostic(ErrorCode.ERR_UseDefViolation, "c1").WithArguments("c1").WithLocation(7, 9),
// (10,16): error CS0165: Use of unassigned local variable 'c2'
// c1 ??= c2; // RHS unassigned
Diagnostic(ErrorCode.ERR_UseDefViolation, "c2").WithArguments("c2").WithLocation(10, 16),
// (16,9): error CS0165: Use of unassigned local variable 'y1'
// y1.ToString();
Diagnostic(ErrorCode.ERR_UseDefViolation, "y1").WithArguments("y1").WithLocation(16, 9),
// (22,9): error CS0165: Use of unassigned local variable 'y2'
// y2.ToString();
Diagnostic(ErrorCode.ERR_UseDefViolation, "y2").WithArguments("y2").WithLocation(22, 9)
);
}
[Fact]
public void NonStaticInStaticContext()
{
CreateCompilation(@"
public class C
{
C P { get; set; }
public static void Main()
{
P ??= new C();
}
}").VerifyDiagnostics(
// (7,9): error CS0120: An object reference is required for the non-static field, method, or property 'C.P'
// P ??= new C();
Diagnostic(ErrorCode.ERR_ObjectRequired, "P").WithArguments("C.P").WithLocation(7, 9)
);
}
[Fact]
public void ThrowExpressionRHS()
{
CreateCompilation(@"
using System;
public class C
{
public static void Main()
{
object o = null;
o ??= throw new Exception();
}
}
").VerifyDiagnostics(
// (8,15): error CS8115: A throw expression is not allowed in this context.
// o ??= throw new Exception();
Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(8, 15)
);
}
[Fact]
public void TypeParameterLHS()
{
var verifier = CompileAndVerify(@"
using System;
class C
{
static void Main() => M(""Non Null Input"");
static void M<T>(T t)
{
t ??= default;
Console.WriteLine(t);
}
}
", expectedOutput: "Non Null Input");
verifier.VerifyDiagnostics();
verifier.VerifyIL("C.M<T>(T)", expectedIL: @"
{
// Code size 30 (0x1e)
.maxstack 1
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: box ""T""
IL_0008: brtrue.s IL_0012
IL_000a: ldarga.s V_0
IL_000c: initobj ""T""
IL_0012: ldarg.0
IL_0013: box ""T""
IL_0018: call ""void System.Console.WriteLine(object)""
IL_001d: ret
}
");
CompileAndVerify(@"
using System;
class C
{
static void Main()
{
Verify<object>(null, ""Assignment Evaluated"");
Verify<int>(default, 10);
Verify<int?>(null, 1);
Verify<int?>(2, 10);
}
static void Verify<T>(T t1, T t2)
{
Console.WriteLine(t1 ??= t2);
Console.WriteLine(t1);
}
}", expectedOutput: @"
Assignment Evaluated
Assignment Evaluated
0
0
1
1
2
2
");
}
[Fact]
public void ConstrainedTypeParameter()
{
var verifier = CompileAndVerify(@"
using System;
class C
{
static void Main()
{
M1(null, ""Test String"");
M2((int?)null, 1);
}
static void M1<T>(T t1, T t2) where T : class
{
t1 ??= t2;
Console.WriteLine(t1);
}
static void M2<T>(T? t1, T t2) where T : struct
{
t1 ??= t2;
Console.WriteLine(t1);
}
}", expectedOutput: @"
Test String
1
");
verifier.VerifyDiagnostics();
verifier.VerifyIL("C.M1<T>(T, T)", expectedIL: @"
{
// Code size 23 (0x17)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000b
IL_0008: ldarg.1
IL_0009: starg.s V_0
IL_000b: ldarg.0
IL_000c: box ""T""
IL_0011: call ""void System.Console.WriteLine(object)""
IL_0016: ret
}
");
verifier.VerifyIL("C.M2<T>(T?, T)", expectedIL: @"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (T V_0)
IL_0000: ldarga.s V_0
IL_0002: call ""T T?.GetValueOrDefault()""
IL_0007: stloc.0
IL_0008: ldarga.s V_0
IL_000a: call ""bool T?.HasValue.get""
IL_000f: brtrue.s IL_001b
IL_0011: ldarg.1
IL_0012: stloc.0
IL_0013: ldarga.s V_0
IL_0015: ldloc.0
IL_0016: call ""T?..ctor(T)""
IL_001b: ldarg.0
IL_001c: box ""T?""
IL_0021: call ""void System.Console.WriteLine(object)""
IL_0026: ret
}
");
CreateCompilation(@"
class C
{
void M<T>(T t1, T t2) where T : struct
{
t1 ??= t2;
}
}").VerifyDiagnostics(
// (6,9): error CS0019: Operator '??=' cannot be applied to operands of type 'T' and 'T'
// t1 ??= t2;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "t1 ??= t2").WithArguments("??=", "T", "T").WithLocation(6, 9)
);
}
[Fact]
public void DynamicRuntimeCastFailure()
{
var source = @"
using System;
class C
{
byte? B { get; set; } = null;
static void Main() => M(new C(), int.MaxValue);
static void M(dynamic d1, dynamic d2)
{
try
{
d1.B ??= d2;
Console.WriteLine(""Should have thrown!"");
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) {}
d1.B = (byte)1;
// Should not throw, as B is non-null
d1.B ??= d2;
}
}";
CompileAndVerify(source, new[] { CSharpRef }, expectedOutput: "");
}
[Fact]
public void AwaitLHS()
{
var source = @"
using System.Threading.Tasks;
class C
{
static async Task M(Task<Task> t1, Task t2)
{
await t1 ??= t2;
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// await t1 ??= t2;
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "await t1").WithLocation(7, 9)
);
}
[Fact]
public void OperatorEqualsIgnored()
{
var source = @"
using System;
class C
{
public static bool operator==(C c1, C c2)
{
Console.WriteLine(""In operator=="");
return false;
}
public static bool operator!=(C c1, C c2)
{
Console.WriteLine(""In operator!="");
return false;
}
public override bool Equals(object o)
{
Console.WriteLine(""In C.Equals"");
return false;
}
public override int GetHashCode() => 1;
public static void Main()
{
C c1 = null, c2 = new C();
c1 ??= c2;
Console.WriteLine(c1.GetHashCode());
}
}";
var verifier = CompileAndVerify(source, expectedOutput: "1");
verifier.VerifyIL("C.Main()", expectedIL: @"
{
// Code size 25 (0x19)
.maxstack 1
.locals init (C V_0, //c1
C V_1) //c2
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: newobj ""C..ctor()""
IL_0007: stloc.1
IL_0008: ldloc.0
IL_0009: brtrue.s IL_000d
IL_000b: ldloc.1
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: callvirt ""int object.GetHashCode()""
IL_0013: call ""void System.Console.WriteLine(int)""
IL_0018: ret
}
");
}
[Fact]
public void Associativity()
{
var source = @"
using System;
class C
{
public static void Main()
{
int? a1 = null, b1 = null, c1 = 1;
Console.WriteLine(a1 ??= b1 ??= c1);
Console.WriteLine(b1);
int? a2 = null, b2 = null, c2 = 1;
Console.WriteLine(a2 ??= (b2 ??= c2));
Console.WriteLine(b2);
}
}";
CompileAndVerify(source, expectedOutput: @"
1
1
1
1
");
}
[Fact]
public void ThisDisallowed()
{
var source = @"
class C
{
public void M1()
{
this ??= new C();
}
}
struct S
{
public void M2()
{
this ??= new S();
}
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (6,9): error CS1604: Cannot assign to 'this' because it is read-only
// this ??= new C();
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(6, 9),
// (13,9): error CS0019: Operator '??=' cannot be applied to operands of type 'S' and 'S'
// this ??= new S();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "this ??= new S()").WithArguments("??=", "S", "S").WithLocation(13, 9)
);
}
[Fact]
public void DefiniteAssignment()
{
var source = @"
class C
{
void M()
{
object o1, o2;
C c1 = new C();
GetRef(o1 = """") ??= GetObject(o1);
o1.ToString();
GetRef() ??= GetObject(o2 = """", o2);
o2.ToString();
GetRef() ??= OutVar(out var o3).GetObject(o3);
o3.ToString();
}
object o;
ref object GetRef(object param = null) => ref o;
object GetObject(object param1, object param2 = null) => null;
C OutVar(out object o3)
{
o3 = null;
return this;
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (14,9): error CS0165: Use of unassigned local variable 'o2'
// o2.ToString();
Diagnostic(ErrorCode.ERR_UseDefViolation, "o2").WithArguments("o2").WithLocation(14, 9),
// (17,9): error CS0165: Use of unassigned local variable 'o3'
// o3.ToString();
Diagnostic(ErrorCode.ERR_UseDefViolation, "o3").WithArguments("o3").WithLocation(17, 9)
);
}
[Fact]
public void COMIndexedProperties()
{
var source1 =
@"Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
<Assembly: PrimaryInteropAssembly(0, 0)>
<Assembly: Guid(""165F752D-E9C4-4F7E-B0D0-CDFD7A36E210"")>
<ComImport()>
<Guid(""165F752D-E9C4-4F7E-B0D0-CDFD7A36E211"")>
Public Class COMProp
Private _p As New Dictionary(Of Integer, Object)
Property P(index As Integer) As Object
Get
Return _p(index)
End Get
Set
_p(index) = Value
End Set
End Property
End Class";
var reference1 = BasicCompilationUtils.CompileToMetadata(source1);
var testSource = @"
using System;
class C
{
static void Main()
{
var com = new COMProp();
Console.WriteLine(com.P[1] ??= 2);
}
}";
var verifier = CompileAndVerify(testSource, references: new[] { reference1 });
verifier.VerifyIL("C.Main()", expectedIL: @"
{
// Code size 39 (0x27)
.maxstack 4
.locals init (COMProp V_0,
object V_1)
IL_0000: newobj ""COMProp..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.1
IL_0008: callvirt ""object COMProp.P[int].get""
IL_000d: dup
IL_000e: brtrue.s IL_0021
IL_0010: pop
IL_0011: ldloc.0
IL_0012: ldc.i4.1
IL_0013: ldc.i4.2
IL_0014: box ""int""
IL_0019: dup
IL_001a: stloc.1
IL_001b: callvirt ""void COMProp.P[int].set""
IL_0020: ldloc.1
IL_0021: call ""void System.Console.WriteLine(object)""
IL_0026: ret
}
");
}
[Fact]
public void NullableValueDefaultLiteralRHS()
{
var source = @"
class C
{
void M()
{
int? a = null;
int b = (a ??= default);
b = (a ??= default(int));
b = (a ??= default(int?)); // 1
}
}";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (9,14): error CS0266: Cannot implicitly convert type 'int?' to 'int'. An explicit conversion exists (are you missing a cast?)
// b = (a ??= default(int?)); // 1
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "a ??= default(int?)").WithArguments("int?", "int").WithLocation(9, 14));
}
[Fact]
public void InOutParamAssignment()
{
var source = @"
class C
{
void M(in object o1, out object o2)
{
o1 ??= null;
o2 ??= null;
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (6,9): error CS8331: Cannot assign to variable 'o1' or use it as the right hand side of a ref assignment because it is a readonly variable
// o1 ??= null;
Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "o1").WithArguments("variable", "o1").WithLocation(6, 9),
// (7,9): error CS0269: Use of unassigned out parameter 'o2'
// o2 ??= null;
Diagnostic(ErrorCode.ERR_UseDefViolationOut, "o2").WithArguments("o2").WithLocation(7, 9)
);
}
[Fact]
public void ObsoleteConversion()
{
var source = @"
using System;
class C
{
void M()
{
C c = null;
c ??= new D();
c ??= new E();
}
}
class D
{
[Obsolete(""D is obsolete"", true)]
public static implicit operator C(D d) => null;
}
class E
{
[Obsolete(""E is obsolete"", false)]
public static implicit operator C(E e) => null;
}";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (8,15): error CS0619: 'D.implicit operator C(D)' is obsolete: 'D is obsolete'
// c ??= new D();
Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new D()").WithArguments("D.implicit operator C(D)", "D is obsolete").WithLocation(8, 15),
// (9,15): warning CS0618: 'E.implicit operator C(E)' is obsolete: 'E is obsolete'
// c ??= new E();
Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "new E()").WithArguments("E.implicit operator C(E)", "E is obsolete").WithLocation(9, 15)
);
}
[Fact]
public void DestructuringSyntaxDoesNotWork()
{
var source = @"
class C
{
void M()
{
int? x = null, y = null;
(x, y) ??= (1, 2);
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// (x, y) ??= (1, 2);
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "(x, y)").WithLocation(7, 9)
);
}
[Fact]
public void UseSiteDiagnostics()
{
var aSource = "public class A {}";
var aRef = CreateCompilation(aSource).EmitToImageReference();
var bSource = "public class B : A {}";
var bRef = CreateCompilation(bSource, new[] { aRef }).EmitToImageReference();
var testSource = @"
class C
{
void M()
{
var c = new C();
c ??= new B();
}
}";
var compilation = CreateCompilation(testSource, new[] { bRef });
compilation.VerifyDiagnostics(
// (7,9): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly 'c3ba0fb2-4f4f-4916-afc5-50566473adcc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// c ??= new B();
Diagnostic(ErrorCode.ERR_NoTypeDef, "c ??= new B()").WithArguments("A", $"{aRef.Display}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 9),
// (7,9): error CS0019: Operator '??=' cannot be applied to operands of type 'C' and 'B'
// c ??= new B();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "c ??= new B()").WithArguments("??=", "C", "B").WithLocation(7, 9));
}
[Fact]
[WorkItem(30024, "https://github.com/dotnet/roslyn/issues/30024")]
public void SpanNotAllowedLHS()
{
CreateCompilationWithMscorlibAndSpan(@"
using System;
class C
{
void M()
{
Span<byte> s1 = new Span<byte>();
s1 ??= new Span<byte>();
Span<byte>? s2 = null;
s2 ??= new Span<byte>();
}
}").VerifyDiagnostics(
// (8,9): error CS0019: Operator '??=' cannot be applied to operands of type 'Span<byte>' and 'Span<byte>'
// s1 ??= new Span<byte>();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 ??= new Span<byte>()").WithArguments("??=", "System.Span<byte>", "System.Span<byte>").WithLocation(8, 9),
// (9,9): error CS9244: The type 'Span<byte>' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Nullable<T>'
// Span<byte>? s2 = null;
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Span<byte>?").WithArguments("System.Nullable<T>", "T", "System.Span<byte>").WithLocation(9, 9)
);
}
[Fact]
[WorkItem(32138, "https://github.com/dotnet/roslyn/issues/31238")]
public void ExpressionTreeNotAllowed()
{
CreateCompilation(@"
using System;
using System.Linq.Expressions;
public class C {
public object o = null;
public void M() {
String x = null;
Expression<Func<string>> e0 = () => x ??= null;
Expression<Func<object>> e1 = () => o ??= null;
}
}").VerifyDiagnostics(
// (8,45): error CS8642: An expression tree may not contain a null coalescing assignment
// Expression<Func<string>> e0 = () => x ??= null;
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainNullCoalescingAssignment, "x ??= null").WithLocation(8, 45),
// (9,45): error CS8642: An expression tree may not contain a null coalescing assignment
// Expression<Func<object>> e1 = () => o ??= null;
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainNullCoalescingAssignment, "o ??= null").WithLocation(9, 45));
}
[Fact]
public void PointersDisallowed()
{
CreateCompilation(@"
class C
{
unsafe void M(int* i1, int* i2)
{
i1 ??= i2;
}
}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (6,9): error CS0019: Operator '??=' cannot be applied to operands of type 'int*' and 'int*'
// i1 ??= i2;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "i1 ??= i2").WithArguments("??=", "int*", "int*").WithLocation(6, 9));
}
[Fact]
public void MissingMembers()
{
var source = @"
class C
{
void M()
{
int? i = null;
i ??= 1;
}
}
";
var c1 = CreateCompilation(source);
c1.MakeMemberMissing(SpecialMember.System_Nullable_T_GetValueOrDefault);
c1.VerifyEmitDiagnostics(
// (7,9): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault'
// i ??= 1;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(7, 9));
var c2 = CreateCompilation(source);
c2.MakeMemberMissing(SpecialMember.System_Nullable_T_get_HasValue);
c2.VerifyEmitDiagnostics(
// (7,9): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue'
// i ??= 1;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(7, 9));
}
[Fact]
public void DefaultConvertedToNonNullableValueType()
{
CompileAndVerify(@"
using System;
class C
{
static void Main()
{
int? a = null;
Console.WriteLine(a ??= default);
Console.WriteLine(a ??= 1);
}
}
", expectedOutput: @"
0
0");
}
[Fact]
public void ArrayAccessLHS()
{
var verifier = CompileAndVerify(@"
using System;
class C
{
static void Main()
{
TestValueTypes();
TestReferenceTypes();
}
static void TestValueTypes()
{
int?[] arr = new[] { (int?)0 };
Console.WriteLine(arr[0] ??= 1);
}
static void TestReferenceTypes()
{
string[] arr = new string[] { null };
Console.WriteLine(arr[0] ??= ""2"");
}
}", expectedOutput: @"
0
2");
verifier.VerifyIL("C.TestValueTypes()", @"
{
// Code size 65 (0x41)
.maxstack 4
.locals init (int?& V_0,
int V_1)
IL_0000: ldc.i4.1
IL_0001: newarr ""int?""
IL_0006: dup
IL_0007: ldc.i4.0
IL_0008: ldc.i4.0
IL_0009: newobj ""int?..ctor(int)""
IL_000e: stelem ""int?""
IL_0013: ldc.i4.0
IL_0014: ldelema ""int?""
IL_0019: stloc.0
IL_001a: ldloc.0
IL_001b: call ""int int?.GetValueOrDefault()""
IL_0020: stloc.1
IL_0021: ldloc.0
IL_0022: call ""bool int?.HasValue.get""
IL_0027: brtrue.s IL_003a
IL_0029: ldc.i4.1
IL_002a: stloc.1
IL_002b: ldloc.0
IL_002c: ldloc.1
IL_002d: newobj ""int?..ctor(int)""
IL_0032: stobj ""int?""
IL_0037: ldloc.1
IL_0038: br.s IL_003b
IL_003a: ldloc.1
IL_003b: call ""void System.Console.WriteLine(int)""
IL_0040: ret
}");
verifier.VerifyIL("C.TestReferenceTypes()", @"
{
// Code size 35 (0x23)
.maxstack 3
.locals init (string& V_0,
string V_1)
IL_0000: ldc.i4.1
IL_0001: newarr ""string""
IL_0006: ldc.i4.0
IL_0007: ldelema ""string""
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: ldind.ref
IL_000f: dup
IL_0010: brtrue.s IL_001d
IL_0012: pop
IL_0013: ldloc.0
IL_0014: ldstr ""2""
IL_0019: dup
IL_001a: stloc.1
IL_001b: stind.ref
IL_001c: ldloc.1
IL_001d: call ""void System.Console.WriteLine(string)""
IL_0022: ret
}");
}
[Fact]
[WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")]
public void StructLHSAcrossAwait()
{
// This only works when compiling for debug mode currently.
// https://github.com/dotnet/roslyn/issues/36443
CompileAndVerify(@"
using System;
using System.Threading.Tasks;
struct S
{
int? i;
static async Task Main()
{
S s = default;
Console.WriteLine(s.i ??= await GetInt());
Console.WriteLine(s.i ??= await GetInt());
}
static Task<int?> GetInt() => Task.FromResult((int?)1);
}", expectedOutput: @"
1
1", options: TestOptions.DebugExe);
}
}
}
|