From c99ad7156334da74c47362a189f6b23187f0fc3b Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 9 Jan 2020 11:47:41 -0800 Subject: [PATCH] Revert "Improve the code gen for a conditional access on a readonly unconstrained field (#35979)" This reverts commit 6b22ff182f53a17532ad384ea0fc07c23650b10f. --- .../CSharp/Portable/CodeGen/EmitExpression.cs | 63 ++- .../CodeGenConditionalOperatorTests.cs | 282 ------------- .../CodeGenShortCircuitOperatorTests.cs | 59 ++- .../Test/WinRT/AnonymousTypesSymbolTests.cs | 230 ++++++----- .../Portable/CodeGen/EmitExpression.vb | 59 ++- .../Semantics/ConditionalAccessTests.vb | 373 ++---------------- 6 files changed, 271 insertions(+), 795 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index f0ffc7d6315..94137c2deec 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -405,43 +405,32 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces // if T happens to be a value type, it could be a target of mutating calls. receiverTemp = EmitReceiverRef(receiver, AddressKind.Constrained); - if (receiverTemp is null) - { - // unconstrained case needs to handle case where T is actually a struct. - // such values are never nulls - // we will emit a check for such case, but the check is really a JIT-time - // constant since JIT will know if T is a struct or not. - - // if ((object)default(T) != null) - // { - // goto whenNotNull - // } - // else - // { - // temp = receiverRef - // receiverRef = ref temp - // } - EmitDefaultValue(receiverType, true, receiver.Syntax); - EmitBox(receiverType, receiver.Syntax); - _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel); - EmitLoadIndirect(receiverType, receiver.Syntax); - - cloneTemp = AllocateTemp(receiverType, receiver.Syntax); - _builder.EmitLocalStore(cloneTemp); - _builder.EmitLocalAddress(cloneTemp); - _builder.EmitLocalLoad(cloneTemp); - EmitBox(receiverType, receiver.Syntax); - - // here we have loaded a ref to a temp and its boxed value { &T, O } - } - else - { - // we are calling the expression on a copy of the target anyway, - // so even if T is a struct, we don't need to make sure we call the expression on the original target. - - _builder.EmitLocalLoad(receiverTemp); - EmitBox(receiverType, receiver.Syntax); - } + // unconstrained case needs to handle case where T is actually a struct. + // such values are never nulls + // we will emit a check for such case, but the check is really a JIT-time + // constant since JIT will know if T is a struct or not. + + // if ((object)default(T) != null) + // { + // goto whenNotNull + // } + // else + // { + // temp = receiverRef + // receiverRef = ref temp + // } + EmitDefaultValue(receiverType, true, receiver.Syntax); + EmitBox(receiverType, receiver.Syntax); + _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel); + EmitLoadIndirect(receiverType, receiver.Syntax); + + cloneTemp = AllocateTemp(receiverType, receiver.Syntax); + _builder.EmitLocalStore(cloneTemp); + _builder.EmitLocalAddress(cloneTemp); + _builder.EmitLocalLoad(cloneTemp); + EmitBox(receiver.Type, receiver.Syntax); + + // here we have loaded a ref to a temp and its boxed value { &T, O } } else { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs index 2810718c32c..0cd737849ec 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs @@ -2763,287 +2763,5 @@ .maxstack 1 IL_0064: ret }"); } - - [Fact] - [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] - public void ConditionalAccessUnconstrainedTField() - { - var source = @"using System; -public class C -{ - public C(T t) => this.t = t; - public C(){} - - private T t; - - public void Print() - { - Console.WriteLine(t?.ToString()); - Console.WriteLine(t); - } - -} - -public struct S -{ - int a; - public override string ToString() => a++.ToString(); -} - -public static class Program -{ - public static void Main() - { - new C().Print(); - new C().Print(); - new C(new S()).Print(); - new C(""hello"").Print(); - new C().Print(); - } -}"; - var verify = CompileAndVerify(source, expectedOutput: @"0 -1 - - -0 -0 -hello -hello"); - - verify.VerifyIL("C.Print()", @" -{ - // Code size 75 (0x4b) - .maxstack 2 - .locals init (T V_0) - IL_0000: ldarg.0 - IL_0001: ldflda ""T C.t"" - IL_0006: ldloca.s V_0 - IL_0008: initobj ""T"" - IL_000e: ldloc.0 - IL_000f: box ""T"" - IL_0014: brtrue.s IL_002a - IL_0016: ldobj ""T"" - IL_001b: stloc.0 - IL_001c: ldloca.s V_0 - IL_001e: ldloc.0 - IL_001f: box ""T"" - IL_0024: brtrue.s IL_002a - IL_0026: pop - IL_0027: ldnull - IL_0028: br.s IL_0035 - IL_002a: constrained. ""T"" - IL_0030: callvirt ""string object.ToString()"" - IL_0035: call ""void System.Console.WriteLine(string)"" - IL_003a: ldarg.0 - IL_003b: ldfld ""T C.t"" - IL_0040: box ""T"" - IL_0045: call ""void System.Console.WriteLine(object)"" - IL_004a: ret -}"); - - } - - [Fact] - [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] - public void ConditionalAccessReadonlyUnconstrainedTField() - { - var source = @"using System; -public class C -{ - public C(T t) => this.t = t; - public C(){} - - readonly T t; - - public void Print() - { - Console.WriteLine(t?.ToString()); - Console.WriteLine(t); - } -} - -public struct S -{ - int a; - public override string ToString() => a++.ToString(); -} - -public static class Program -{ - public static void Main() - { - new C().Print(); - new C().Print(); - new C(new S()).Print(); - new C(""hello"").Print(); - new C().Print(); - } -} -"; - var verify = CompileAndVerify(source, expectedOutput: @"0 -0 - - -0 -0 -hello -hello"); - - verify.VerifyIL("C.Print()", @" -{ - // Code size 54 (0x36) - .maxstack 2 - .locals init (T V_0) - IL_0000: ldarg.0 - IL_0001: ldfld ""T C.t"" - IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: ldloc.0 - IL_000a: box ""T"" - IL_000f: brtrue.s IL_0015 - IL_0011: pop - IL_0012: ldnull - IL_0013: br.s IL_0020 - IL_0015: constrained. ""T"" - IL_001b: callvirt ""string object.ToString()"" - IL_0020: call ""void System.Console.WriteLine(string)"" - IL_0025: ldarg.0 - IL_0026: ldfld ""T C.t"" - IL_002b: box ""T"" - IL_0030: call ""void System.Console.WriteLine(object)"" - IL_0035: ret -}"); - - } - - [Fact] - [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] - public void ConditionalAccessUnconstrainedTLocal() - { - var source = @"using System; -public class C -{ - public C(T t) => this.t = t; - public C(){} - - private T t; - - public void Print() - { - var temp = t; - Console.WriteLine(temp?.ToString()); - Console.WriteLine(temp); - } - -} - -public struct S -{ - int a; - public override string ToString() => a++.ToString(); -} - -public static class Program -{ - public static void Main() - { - new C().Print(); - new C().Print(); - new C(new S()).Print(); - new C(""hello"").Print(); - new C().Print(); - } -}"; - var verify = CompileAndVerify(source, expectedOutput: @"0 -1 - - -0 -1 -hello -hello"); - - verify.VerifyIL("C.Print()", @" -{ - // Code size 48 (0x30) - .maxstack 1 - .locals init (T V_0) //temp - IL_0000: ldarg.0 - IL_0001: ldfld ""T C.t"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: box ""T"" - IL_000d: brtrue.s IL_0012 - IL_000f: ldnull - IL_0010: br.s IL_001f - IL_0012: ldloca.s V_0 - IL_0014: constrained. ""T"" - IL_001a: callvirt ""string object.ToString()"" - IL_001f: call ""void System.Console.WriteLine(string)"" - IL_0024: ldloc.0 - IL_0025: box ""T"" - IL_002a: call ""void System.Console.WriteLine(object)"" - IL_002f: ret -}"); - - } - - [Fact] - [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] - public void ConditionalAccessUnconstrainedTTemp() - { - var source = @"using System; -public class C -{ - public C(T t) => this.t = t; - public C(){} - - T t; - - T M() => t; - - public void Print() => Console.WriteLine(M()?.ToString()); -} - -public static class Program -{ - public static void Main() - { - new C().Print(); - new C().Print(); - new C(0).Print(); - new C(""hello"").Print(); - new C().Print(); - } -} -"; - var verify = CompileAndVerify(source, expectedOutput: @"0 - -0 -hello -"); - - verify.VerifyIL("C.Print()", @" -{ - // Code size 38 (0x26) - .maxstack 2 - .locals init (T V_0) - IL_0000: ldarg.0 - IL_0001: call ""T C.M()"" - IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: ldloc.0 - IL_000a: box ""T"" - IL_000f: brtrue.s IL_0015 - IL_0011: pop - IL_0012: ldnull - IL_0013: br.s IL_0020 - IL_0015: constrained. ""T"" - IL_001b: callvirt ""string object.ToString()"" - IL_0020: call ""void System.Console.WriteLine(string)"" - IL_0025: ret -}"); - - } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs index ae47fd543c5..aac0bb5c4f6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs @@ -2461,32 +2461,49 @@ static void Main(string[] args) False"); comp.VerifyIL("Program.Test(System.Func)", @" { - // Code size 62 (0x3e) + // Code size 110 (0x6e) .maxstack 2 - .locals init (T V_0) + .locals init (T V_0, + T V_1) IL_0000: ldarg.0 IL_0001: callvirt ""T System.Func.Invoke()"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: ldloc.0 - IL_000a: box ""T"" - IL_000f: brtrue.s IL_0014 - IL_0011: pop - IL_0012: br.s IL_001f - IL_0014: constrained. ""T"" - IL_001a: callvirt ""void System.IDisposable.Dispose()"" - IL_001f: ldarg.0 - IL_0020: callvirt ""T System.Func.Invoke()"" - IL_0025: stloc.0 - IL_0026: ldloca.s V_0 - IL_0028: ldloc.0 - IL_0029: box ""T"" - IL_002e: brtrue.s IL_0032 - IL_0030: pop - IL_0031: ret - IL_0032: constrained. ""T"" - IL_0038: callvirt ""void System.IDisposable.Dispose()"" - IL_003d: ret + IL_0009: ldloca.s V_1 + IL_000b: initobj ""T"" + IL_0011: ldloc.1 + IL_0012: box ""T"" + IL_0017: brtrue.s IL_002c + IL_0019: ldobj ""T"" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: ldloc.1 + IL_0022: box ""T"" + IL_0027: brtrue.s IL_002c + IL_0029: pop + IL_002a: br.s IL_0037 + IL_002c: constrained. ""T"" + IL_0032: callvirt ""void System.IDisposable.Dispose()"" + IL_0037: ldarg.0 + IL_0038: callvirt ""T System.Func.Invoke()"" + IL_003d: stloc.0 + IL_003e: ldloca.s V_0 + IL_0040: ldloca.s V_1 + IL_0042: initobj ""T"" + IL_0048: ldloc.1 + IL_0049: box ""T"" + IL_004e: brtrue.s IL_0062 + IL_0050: ldobj ""T"" + IL_0055: stloc.1 + IL_0056: ldloca.s V_1 + IL_0058: ldloc.1 + IL_0059: box ""T"" + IL_005e: brtrue.s IL_0062 + IL_0060: pop + IL_0061: ret + IL_0062: constrained. ""T"" + IL_0068: callvirt ""void System.IDisposable.Dispose()"" + IL_006d: ret } "); } diff --git a/src/Compilers/CSharp/Test/WinRT/AnonymousTypesSymbolTests.cs b/src/Compilers/CSharp/Test/WinRT/AnonymousTypesSymbolTests.cs index 1169916c5fb..a53c81b9bc4 100644 --- a/src/Compilers/CSharp/Test/WinRT/AnonymousTypesSymbolTests.cs +++ b/src/Compilers/CSharp/Test/WinRT/AnonymousTypesSymbolTests.cs @@ -424,11 +424,14 @@ .maxstack 3 ).VerifyIL( "<>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.ToString", @"{ - // Code size 123 (0x7b) + // Code size 199 (0xc7) .maxstack 7 .locals init (j__TPar V_0, - j__TPar V_1, - j__TPar V_2) + j__TPar V_1, + j__TPar V_2, + j__TPar V_3, + j__TPar V_4, + j__TPar V_5) IL_0000: ldnull IL_0001: ldstr ""{{ Length = {0}, at1 = {1}, C = {2} }}"" IL_0006: ldc.i4.3 @@ -439,47 +442,71 @@ .maxstack 7 IL_000f: ldfld ""j__TPar <>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.i__Field"" IL_0014: stloc.0 IL_0015: ldloca.s V_0 - IL_0017: ldloc.0 - IL_0018: box ""j__TPar"" - IL_001d: brtrue.s IL_0023 - IL_001f: pop - IL_0020: ldnull - IL_0021: br.s IL_002e - IL_0023: constrained. ""j__TPar"" - IL_0029: callvirt ""string object.ToString()"" - IL_002e: stelem.ref - IL_002f: dup - IL_0030: ldc.i4.1 - IL_0031: ldarg.0 - IL_0032: ldfld ""j__TPar <>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.i__Field"" - IL_0037: stloc.1 - IL_0038: ldloca.s V_1 - IL_003a: ldloc.1 - IL_003b: box ""j__TPar"" - IL_0040: brtrue.s IL_0046 - IL_0042: pop - IL_0043: ldnull - IL_0044: br.s IL_0051 - IL_0046: constrained. ""j__TPar"" - IL_004c: callvirt ""string object.ToString()"" - IL_0051: stelem.ref - IL_0052: dup - IL_0053: ldc.i4.2 - IL_0054: ldarg.0 - IL_0055: ldfld ""j__TPar <>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.i__Field"" - IL_005a: stloc.2 - IL_005b: ldloca.s V_2 - IL_005d: ldloc.2 - IL_005e: box ""j__TPar"" - IL_0063: brtrue.s IL_0069 - IL_0065: pop - IL_0066: ldnull - IL_0067: br.s IL_0074 - IL_0069: constrained. ""j__TPar"" - IL_006f: callvirt ""string object.ToString()"" - IL_0074: stelem.ref - IL_0075: call ""string string.Format(System.IFormatProvider, string, params object[])"" - IL_007a: ret + IL_0017: ldloca.s V_1 + IL_0019: initobj ""j__TPar"" + IL_001f: ldloc.1 + IL_0020: box ""j__TPar"" + IL_0025: brtrue.s IL_003b + IL_0027: ldobj ""j__TPar"" + IL_002c: stloc.1 + IL_002d: ldloca.s V_1 + IL_002f: ldloc.1 + IL_0030: box ""j__TPar"" + IL_0035: brtrue.s IL_003b + IL_0037: pop + IL_0038: ldnull + IL_0039: br.s IL_0046 + IL_003b: constrained. ""j__TPar"" + IL_0041: callvirt ""string object.ToString()"" + IL_0046: stelem.ref + IL_0047: dup + IL_0048: ldc.i4.1 + IL_0049: ldarg.0 + IL_004a: ldfld ""j__TPar <>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.i__Field"" + IL_004f: stloc.2 + IL_0050: ldloca.s V_2 + IL_0052: ldloca.s V_3 + IL_0054: initobj ""j__TPar"" + IL_005a: ldloc.3 + IL_005b: box ""j__TPar"" + IL_0060: brtrue.s IL_0076 + IL_0062: ldobj ""j__TPar"" + IL_0067: stloc.3 + IL_0068: ldloca.s V_3 + IL_006a: ldloc.3 + IL_006b: box ""j__TPar"" + IL_0070: brtrue.s IL_0076 + IL_0072: pop + IL_0073: ldnull + IL_0074: br.s IL_0081 + IL_0076: constrained. ""j__TPar"" + IL_007c: callvirt ""string object.ToString()"" + IL_0081: stelem.ref + IL_0082: dup + IL_0083: ldc.i4.2 + IL_0084: ldarg.0 + IL_0085: ldfld ""j__TPar <>f__AnonymousType1<j__TPar, j__TPar, j__TPar>.i__Field"" + IL_008a: stloc.s V_4 + IL_008c: ldloca.s V_4 + IL_008e: ldloca.s V_5 + IL_0090: initobj ""j__TPar"" + IL_0096: ldloc.s V_5 + IL_0098: box ""j__TPar"" + IL_009d: brtrue.s IL_00b5 + IL_009f: ldobj ""j__TPar"" + IL_00a4: stloc.s V_5 + IL_00a6: ldloca.s V_5 + IL_00a8: ldloc.s V_5 + IL_00aa: box ""j__TPar"" + IL_00af: brtrue.s IL_00b5 + IL_00b1: pop + IL_00b2: ldnull + IL_00b3: br.s IL_00c0 + IL_00b5: constrained. ""j__TPar"" + IL_00bb: callvirt ""string object.ToString()"" + IL_00c0: stelem.ref + IL_00c1: call ""string string.Format(System.IFormatProvider, string, params object[])"" + IL_00c6: ret }" ); } @@ -846,11 +873,14 @@ .maxstack 3 ).VerifyIL( "<>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.ToString", @"{ - // Code size 123 (0x7b) + // Code size 199 (0xc7) .maxstack 7 .locals init (j__TPar V_0, - j__TPar V_1, - j__TPar V_2) + j__TPar V_1, + j__TPar V_2, + j__TPar V_3, + j__TPar V_4, + j__TPar V_5) IL_0000: ldnull IL_0001: ldstr ""{{ ToString = {0}, Equals = {1}, GetHashCode = {2} }}"" IL_0006: ldc.i4.3 @@ -861,47 +891,71 @@ .maxstack 7 IL_000f: ldfld ""j__TPar <>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.i__Field"" IL_0014: stloc.0 IL_0015: ldloca.s V_0 - IL_0017: ldloc.0 - IL_0018: box ""j__TPar"" - IL_001d: brtrue.s IL_0023 - IL_001f: pop - IL_0020: ldnull - IL_0021: br.s IL_002e - IL_0023: constrained. ""j__TPar"" - IL_0029: callvirt ""string object.ToString()"" - IL_002e: stelem.ref - IL_002f: dup - IL_0030: ldc.i4.1 - IL_0031: ldarg.0 - IL_0032: ldfld ""j__TPar <>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.i__Field"" - IL_0037: stloc.1 - IL_0038: ldloca.s V_1 - IL_003a: ldloc.1 - IL_003b: box ""j__TPar"" - IL_0040: brtrue.s IL_0046 - IL_0042: pop - IL_0043: ldnull - IL_0044: br.s IL_0051 - IL_0046: constrained. ""j__TPar"" - IL_004c: callvirt ""string object.ToString()"" - IL_0051: stelem.ref - IL_0052: dup - IL_0053: ldc.i4.2 - IL_0054: ldarg.0 - IL_0055: ldfld ""j__TPar <>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.i__Field"" - IL_005a: stloc.2 - IL_005b: ldloca.s V_2 - IL_005d: ldloc.2 - IL_005e: box ""j__TPar"" - IL_0063: brtrue.s IL_0069 - IL_0065: pop - IL_0066: ldnull - IL_0067: br.s IL_0074 - IL_0069: constrained. ""j__TPar"" - IL_006f: callvirt ""string object.ToString()"" - IL_0074: stelem.ref - IL_0075: call ""string string.Format(System.IFormatProvider, string, params object[])"" - IL_007a: ret + IL_0017: ldloca.s V_1 + IL_0019: initobj ""j__TPar"" + IL_001f: ldloc.1 + IL_0020: box ""j__TPar"" + IL_0025: brtrue.s IL_003b + IL_0027: ldobj ""j__TPar"" + IL_002c: stloc.1 + IL_002d: ldloca.s V_1 + IL_002f: ldloc.1 + IL_0030: box ""j__TPar"" + IL_0035: brtrue.s IL_003b + IL_0037: pop + IL_0038: ldnull + IL_0039: br.s IL_0046 + IL_003b: constrained. ""j__TPar"" + IL_0041: callvirt ""string object.ToString()"" + IL_0046: stelem.ref + IL_0047: dup + IL_0048: ldc.i4.1 + IL_0049: ldarg.0 + IL_004a: ldfld ""j__TPar <>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.i__Field"" + IL_004f: stloc.2 + IL_0050: ldloca.s V_2 + IL_0052: ldloca.s V_3 + IL_0054: initobj ""j__TPar"" + IL_005a: ldloc.3 + IL_005b: box ""j__TPar"" + IL_0060: brtrue.s IL_0076 + IL_0062: ldobj ""j__TPar"" + IL_0067: stloc.3 + IL_0068: ldloca.s V_3 + IL_006a: ldloc.3 + IL_006b: box ""j__TPar"" + IL_0070: brtrue.s IL_0076 + IL_0072: pop + IL_0073: ldnull + IL_0074: br.s IL_0081 + IL_0076: constrained. ""j__TPar"" + IL_007c: callvirt ""string object.ToString()"" + IL_0081: stelem.ref + IL_0082: dup + IL_0083: ldc.i4.2 + IL_0084: ldarg.0 + IL_0085: ldfld ""j__TPar <>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.i__Field"" + IL_008a: stloc.s V_4 + IL_008c: ldloca.s V_4 + IL_008e: ldloca.s V_5 + IL_0090: initobj ""j__TPar"" + IL_0096: ldloc.s V_5 + IL_0098: box ""j__TPar"" + IL_009d: brtrue.s IL_00b5 + IL_009f: ldobj ""j__TPar"" + IL_00a4: stloc.s V_5 + IL_00a6: ldloca.s V_5 + IL_00a8: ldloc.s V_5 + IL_00aa: box ""j__TPar"" + IL_00af: brtrue.s IL_00b5 + IL_00b1: pop + IL_00b2: ldnull + IL_00b3: br.s IL_00c0 + IL_00b5: constrained. ""j__TPar"" + IL_00bb: callvirt ""string object.ToString()"" + IL_00c0: stelem.ref + IL_00c1: call ""string string.Format(System.IFormatProvider, string, params object[])"" + IL_00c6: ret }" ).VerifyIL( "<>f__AnonymousType0<j__TPar, j__TPar, j__TPar>.ToString.get", diff --git a/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb b/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb index 8c83c4f54a5..16692a869b8 100644 --- a/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb +++ b/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb @@ -288,43 +288,35 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen Dim nullCheckOnCopy = conditional.CaptureReceiver OrElse (receiverType.IsReferenceType AndAlso receiverType.TypeKind = TypeKind.TypeParameter) If nullCheckOnCopy Then - receiverTemp = EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly) + EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly) If Not receiverType.IsReferenceType Then - If receiverTemp Is Nothing Then - ' unconstrained case needs to handle case where T Is actually a struct. - ' such values are never nulls - ' we will emit a check for such case, but the check Is really a JIT-time - ' constant since JIT will know if T Is a struct Or Not. - ' - ' if ((object)default(T) != null) - ' { - ' goto whenNotNull - ' } - ' else - ' { - ' temp = receiverRef - ' receiverRef = ref temp - ' } - EmitInitObj(receiverType, True, receiver.Syntax) - EmitBox(receiverType, receiver.Syntax) - _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel) - EmitLoadIndirect(receiverType, receiver.Syntax) - - temp = AllocateTemp(receiverType, receiver.Syntax) - _builder.EmitLocalStore(temp) - _builder.EmitLocalAddress(temp) - _builder.EmitLocalLoad(temp) - EmitBox(receiverType, receiver.Syntax) + ' unconstrained case needs to handle case where T Is actually a struct. + ' such values are never nulls + ' we will emit a check for such case, but the check Is really a JIT-time + ' constant since JIT will know if T Is a struct Or Not. + ' + ' if ((object)default(T) != null) + ' { + ' goto whenNotNull + ' } + ' else + ' { + ' temp = receiverRef + ' receiverRef = ref temp + ' } + EmitInitObj(receiverType, True, receiver.Syntax) + EmitBox(receiverType, receiver.Syntax) + _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel) + EmitLoadIndirect(receiverType, receiver.Syntax) - ' here we have loaded a ref to a temp And its boxed value { &T, O } - Else - ' we are calling the expression on a copy of the target anyway, - ' so even if T is a struct, we don't need to make sure we call the expression on the original target. + temp = AllocateTemp(receiverType, receiver.Syntax) + _builder.EmitLocalStore(temp) + _builder.EmitLocalAddress(temp) + _builder.EmitLocalLoad(temp) + EmitBox(receiver.Type, receiver.Syntax) - _builder.EmitLocalLoad(receiverTemp) - EmitBox(receiverType, receiver.Syntax) - End If + ' here we have loaded a ref to a temp And its boxed value { &T, O } Else _builder.EmitOpCode(ILOpCode.Dup) ' here we have loaded two copies of a reference { O, O } @@ -368,7 +360,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen _builder.MarkLabel(whenNotNullLabel) If Not nullCheckOnCopy Then - Debug.Assert(receiverTemp Is Nothing) receiverTemp = EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly) Debug.Assert(receiverTemp Is Nothing OrElse receiver.IsDefaultValue()) End If diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb index 4a12037333f..41869107c53 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ConditionalAccessTests.vb @@ -613,23 +613,32 @@ Null verifier.VerifyIL("Module1.Test8", ) @@ -3543,21 +3552,30 @@ Ext4 C1 verifier.VerifyIL("Module1.Test1_6", ) @@ -5288,317 +5306,6 @@ C1 ]]>) End Sub - - - Public Sub CodeGen_ConditionalAccessUnconstrainedTField() - Dim c = CompileAndVerify( - - -Imports System - -Public Class C(Of T) - Public Sub New(t As T) - field = t - End Sub - - Public Sub New() - End Sub - - Private field As T - - Public Sub Print() - Console.WriteLine(field?.ToString()) - Console.WriteLine(field) - End Sub -End Class - -Public Structure S - Private a As Integer - - Public Overrides Function ToString() As String - Dim result = a.ToString() - a = a + 1 - Return result - End Function -End Structure - -Module Program - Sub Main() - Call New C(Of S)().Print() - Call New C(Of S?)().Print() - Call New C(Of S?)(New S()).Print() - Call New C(Of String)("hello").Print() - Call New C(Of String)().Print() - End Sub -End Module - -, expectedOutput:="0 -1 - - -0 -0 -hello -hello") - - c.VerifyIL("C(Of T).Print()", - ) - End Sub - - - - Public Sub CodeGen_ConditionalAccessReadonlyUnconstrainedTField() - Dim c = CompileAndVerify( - - -Imports System - -Public Class C(Of T) - Public Sub New(ByVal t As T) - field = t - End Sub - - Public Sub New() - End Sub - - ReadOnly field As T - - Public Sub Print() - Console.WriteLine(field?.ToString()) - Console.WriteLine(field) - End Sub -End Class - -Public Structure S - Private a As Integer - - Public Overrides Function ToString() As String - Return Math.Min(System.Threading.Interlocked.Increment(a), a - 1).ToString() - End Function -End Structure - -Module Program - Sub Main() - Call New C(Of S)().Print() - Call New C(Of S?)().Print() - Call New C(Of S?)(New S()).Print() - Call New C(Of String)("hello").Print() - Call New C(Of String)().Print() - End Sub -End Module - -, expectedOutput:="0 -0 - - -0 -0 -hello -hello") - - c.VerifyIL("C(Of T).Print()", - ) - End Sub - - - - Public Sub CodeGen_ConditionalAccessUnconstrainedTLocal() - Dim c = CompileAndVerify( - - -Imports System - -Public Class C(Of T) - Public Sub New(ByVal t As T) - field = t - End Sub - - Public Sub New() - End Sub - - Private field As T - - Public Sub Print() - Dim temp = field - Console.WriteLine(temp?.ToString()) - Console.WriteLine(temp) - End Sub -End Class - -Public Structure S - Private a As Integer - - Public Overrides Function ToString() As String - Return Math.Min(System.Threading.Interlocked.Increment(a), a - 1).ToString() - End Function -End Structure - -Module Program - Sub Main() - Call New C(Of S)().Print() - Call New C(Of S?)().Print() - Call New C(Of S?)(New S()).Print() - Call New C(Of String)("hello").Print() - Call New C(Of String)().Print() - End Sub -End Module - -, expectedOutput:="0 -1 - - -0 -1 -hello -hello") - - c.VerifyIL("C(Of T).Print()", - ) - End Sub - - - - Public Sub CodeGen_ConditionalAccessUnconstrainedTTemp() - Dim c = CompileAndVerify( - - -Imports System - -Public Class C(Of T) - Public Sub New(ByVal t As T) - field = t - End Sub - - Public Sub New() - End Sub - - Private field As T - - Private Function M() As T - Return field - End Function - - Public Sub Print() - Console.WriteLine(M()?.ToString()) - End Sub -End Class - -Module Program - Sub Main() - Call New C(Of Integer)().Print() - Call New C(Of Integer?)().Print() - Call New C(Of Integer?)(0).Print() - Call New C(Of String)("hello").Print() - Call New C(Of String)().Print() - End Sub -End Module - -, expectedOutput:="0 - -0 -hello -") - - c.VerifyIL("C(Of T).Print()", - ) - End Sub - Public Sub InlineNullableIsTrue_01() -- GitLab