diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
index 7e265ab65ab8c..d6a58243f0d5a 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
@@ -137,6 +137,11 @@ private bool IsDebugPlus()
return _module.Compilation.Options.DebugPlusMode;
}
+ private bool EnablePEVerifyCompat()
+ {
+ return _module.Compilation.FeaturePEVerifyCompatEnabled;
+ }
+
private LocalDefinition LazyReturnTemp
{
get
diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
index 16e074f09ae65..df34a3785a12a 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
@@ -385,7 +385,7 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
return false;
}
- if (!needWriteable)
+ if (!needWriteable && !EnablePEVerifyCompat())
{
return true;
}
@@ -398,9 +398,27 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
if (!field.IsReadOnly)
{
- //PROTOTYPE(verifier): should we dig through struct receivers?
- // roField.a.b.c.d.Method() // roField is readonly, all structs
- // is it cheaper to copy "d" than "roField", but getting rw ref of roField could upset verifier
+ // in a case if we have a writeable struct field with a receiver that only has a readable home we would need to pass it via a temp.
+ // it would be advantageous to make a temp for the field, not for the the outer struct, since the field is smaller and we can get to is by feching references.
+ // NOTE: this would not be profitable if we have to satisfy verifier, since for verifiability
+ // we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways.
+ if (!EnablePEVerifyCompat())
+ {
+ Debug.Assert(needWriteable == true);
+
+ var receiver = fieldAccess.ReceiverOpt;
+ if (receiver?.Type.IsValueType == true)
+ {
+ // Check receiver:
+ // has writeable home -> return true - the whole chain has writeable home (also a more common case)
+ // has readable home -> return false - we need to copy the field
+ // otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home
+
+ return HasHome(receiver, needWriteable: true) ||
+ !HasHome(receiver, needWriteable: false);
+ }
+ }
+
return true;
}
diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
index 0542440a0659c..70d9ab7acc7f4 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
@@ -424,9 +424,14 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
}
else
{
- //PROTOTYPE(verifier): this does not need to be writeable
- // we may call "HasValue" on this, but it is not mutating
- receiverTemp = EmitReceiverRef(receiver, AddressKind.Writeable);
+ // this does not need to be writeable
+ // we may call "HasValue" on this, but it is not mutating
+ // (however verification does not know that and considers all calls mutating)
+ var addressKind = EnablePEVerifyCompat()?
+ AddressKind.Writeable:
+ AddressKind.ReadOnly;
+
+ receiverTemp = EmitReceiverRef(receiver, addressKind);
_builder.EmitOpCode(ILOpCode.Dup);
// here we have loaded two copies of a reference { O, O } or {&nub, &nub}
}
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
index b36a7d30ec7b1..96a7a74e82fd2 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
@@ -163,6 +163,14 @@ internal override CommonAnonymousTypeManager CommonAnonymousTypeManager
///
internal bool FeatureStrictEnabled => Feature("strict") != null;
+ ///
+ /// True when "peverify-compat" is set
+ /// With this flag we will avoid certain patterns known not be compatible with PEVerify.
+ /// The code may be less efficient and may deviate from spec in corner cases.
+ /// The flag is only to be used if PEVerify pass is extremely important.
+ ///
+ internal bool FeaturePEVerifyCompatEnabled => Feature("peverify-compat") != null;
+
///
/// The language version that was used to parse the syntax trees of this compilation.
///
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
index f5d5c3e753ca0..17e64fb5fcee3 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
@@ -173,6 +173,7 @@ .locals init (int V_0,
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InParamPassRoField()
{
var text = @"
@@ -213,6 +214,23 @@ .maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}");
+
+ comp = CompileAndVerify(text, verify: false, expectedOutput: "42", parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
+
+ comp.VerifyIL("Program.Main()", @"
+{
+ // Code size 20 (0x14)
+ .maxstack 1
+ .locals init (int V_0)
+ IL_0000: ldsfld ""int Program.F""
+ IL_0005: stloc.0
+ IL_0006: ldloca.s V_0
+ IL_0008: call ""ref readonly int Program.M(in int)""
+ IL_000d: ldind.i4
+ IL_000e: call ""void System.Console.WriteLine(int)""
+ IL_0013: ret
+}");
+
}
[Fact]
@@ -1082,6 +1100,7 @@ public struct S1
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void ReadonlyParamAsyncSpillReadOnlyStructThis()
{
var text = @"
@@ -1164,10 +1183,26 @@ public void M1(in int arg2, in int arg3, in int arg4)
1
42
3
+3");
+
+ comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
+ CompileAndVerify(comp, verify: false, expectedOutput: @"
+3
+42
+2
+3
+1
+42
+2
+3
+1
+42
+2
3");
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void ReadonlyParamAsyncSpillReadOnlyStructThis_NoValCapture()
{
var text = @"
@@ -1302,7 +1337,102 @@ .locals init (int V_0,
IL_00a9: ret
}
");
- }
+ comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
+ v = CompileAndVerify(comp, verify: true, expectedOutput: @"
+1
+2
+3
+4");
+
+ // NOTE: s1, s3 and s4 are all directly loaded via ldsflda and not spilled.
+ v.VerifyIL("Program.d__5.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
+{
+ // Code size 183 (0xb7)
+ .maxstack 4
+ .locals init (int V_0,
+ S1 V_1,
+ System.Runtime.CompilerServices.TaskAwaiter V_2,
+ S1 V_3,
+ S1 V_4,
+ S1 V_5,
+ System.Exception V_6)
+ IL_0000: ldarg.0
+ IL_0001: ldfld ""int Program.d__5.<>1__state""
+ IL_0006: stloc.0
+ .try
+ {
+ IL_0007: ldloc.0
+ IL_0008: brfalse.s IL_0043
+ IL_000a: ldsfld ""S1 Program.s3""
+ IL_000f: call ""System.Threading.Tasks.Task Program.GetT(S1)""
+ IL_0014: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()""
+ IL_0019: stloc.2
+ IL_001a: ldloca.s V_2
+ IL_001c: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get""
+ IL_0021: brtrue.s IL_005f
+ IL_0023: ldarg.0
+ IL_0024: ldc.i4.0
+ IL_0025: dup
+ IL_0026: stloc.0
+ IL_0027: stfld ""int Program.d__5.<>1__state""
+ IL_002c: ldarg.0
+ IL_002d: ldloc.2
+ IL_002e: stfld ""System.Runtime.CompilerServices.TaskAwaiter Program.d__5.<>u__1""
+ IL_0033: ldarg.0
+ IL_0034: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__5.<>t__builder""
+ IL_0039: ldloca.s V_2
+ IL_003b: ldarg.0
+ IL_003c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.d__5>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.d__5)""
+ IL_0041: leave.s IL_00b6
+ IL_0043: ldarg.0
+ IL_0044: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Program.d__5.<>u__1""
+ IL_0049: stloc.2
+ IL_004a: ldarg.0
+ IL_004b: ldflda ""System.Runtime.CompilerServices.TaskAwaiter Program.d__5.<>u__1""
+ IL_0050: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
+ IL_0056: ldarg.0
+ IL_0057: ldc.i4.m1
+ IL_0058: dup
+ IL_0059: stloc.0
+ IL_005a: stfld ""int Program.d__5.<>1__state""
+ IL_005f: ldloca.s V_2
+ IL_0061: call ""S1 System.Runtime.CompilerServices.TaskAwaiter.GetResult()""
+ IL_0066: stloc.1
+ IL_0067: ldsfld ""S1 Program.s1""
+ IL_006c: stloc.3
+ IL_006d: ldloca.s V_3
+ IL_006f: ldsfld ""S1 Program.s2""
+ IL_0074: stloc.s V_4
+ IL_0076: ldloca.s V_4
+ IL_0078: ldloca.s V_1
+ IL_007a: ldsfld ""S1 Program.s4""
+ IL_007f: stloc.s V_5
+ IL_0081: ldloca.s V_5
+ IL_0083: call ""void S1.M1(in S1, in S1, in S1)""
+ IL_0088: leave.s IL_00a3
+ }
+ catch System.Exception
+ {
+ IL_008a: stloc.s V_6
+ IL_008c: ldarg.0
+ IL_008d: ldc.i4.s -2
+ IL_008f: stfld ""int Program.d__5.<>1__state""
+ IL_0094: ldarg.0
+ IL_0095: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__5.<>t__builder""
+ IL_009a: ldloc.s V_6
+ IL_009c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
+ IL_00a1: leave.s IL_00b6
+ }
+ IL_00a3: ldarg.0
+ IL_00a4: ldc.i4.s -2
+ IL_00a6: stfld ""int Program.d__5.<>1__state""
+ IL_00ab: ldarg.0
+ IL_00ac: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__5.<>t__builder""
+ IL_00b1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
+ IL_00b6: ret
+}
+");
+ }
}
}
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs
index 10d243d947eb4..d39f7393af424 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs
@@ -17,6 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
public class CodeGenReadOnlyStructTests : CompilingTestBase
{
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InvokeOnReadOnlyStaticField()
{
var text = @"
@@ -57,9 +58,31 @@ .maxstack 1
IL_001f: call ""void System.Console.Write(string)""
IL_0024: ret
}");
+
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true, expectedOutput: @"12");
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 43 (0x2b)
+ .maxstack 1
+ .locals init (Program.S1 V_0)
+ IL_0000: ldsfld ""Program.S1 Program.sf""
+ IL_0005: stloc.0
+ IL_0006: ldloca.s V_0
+ IL_0008: call ""string Program.S1.M1()""
+ IL_000d: call ""void System.Console.Write(string)""
+ IL_0012: ldsfld ""Program.S1 Program.sf""
+ IL_0017: stloc.0
+ IL_0018: ldloca.s V_0
+ IL_001a: constrained. ""Program.S1""
+ IL_0020: callvirt ""string object.ToString()""
+ IL_0025: call ""void System.Console.Write(string)""
+ IL_002a: ret
+}");
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InvokeOnReadOnlyStaticFieldMetadata()
{
var text1 = @"
@@ -106,9 +129,31 @@ .maxstack 1
IL_001f: call ""void System.Console.Write(string)""
IL_0024: ret
}");
+
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef, ref1 }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true, expectedOutput: @"12");
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 43 (0x2b)
+ .maxstack 1
+ .locals init (S1 V_0)
+ IL_0000: ldsfld ""S1 Program.sf""
+ IL_0005: stloc.0
+ IL_0006: ldloca.s V_0
+ IL_0008: call ""string S1.M1()""
+ IL_000d: call ""void System.Console.Write(string)""
+ IL_0012: ldsfld ""S1 Program.sf""
+ IL_0017: stloc.0
+ IL_0018: ldloca.s V_0
+ IL_001a: constrained. ""S1""
+ IL_0020: callvirt ""string object.ToString()""
+ IL_0025: call ""void System.Console.Write(string)""
+ IL_002a: ret
+}");
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InvokeOnReadOnlyInstanceField()
{
var text = @"
@@ -152,9 +197,33 @@ .maxstack 2
IL_0025: call ""void System.Console.Write(string)""
IL_002a: ret
}");
+
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true, expectedOutput: @"12");
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 49 (0x31)
+ .maxstack 2
+ .locals init (Program.S1 V_0)
+ IL_0000: newobj ""Program..ctor()""
+ IL_0005: dup
+ IL_0006: ldfld ""Program.S1 Program.f""
+ IL_000b: stloc.0
+ IL_000c: ldloca.s V_0
+ IL_000e: call ""string Program.S1.M1()""
+ IL_0013: call ""void System.Console.Write(string)""
+ IL_0018: ldfld ""Program.S1 Program.f""
+ IL_001d: stloc.0
+ IL_001e: ldloca.s V_0
+ IL_0020: constrained. ""Program.S1""
+ IL_0026: callvirt ""string object.ToString()""
+ IL_002b: call ""void System.Console.Write(string)""
+ IL_0030: ret
+}");
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InvokeOnReadOnlyInstanceFieldGeneric()
{
var text = @"
@@ -202,9 +271,34 @@ .maxstack 3
IL_002a: call ""void System.Console.Write(string)""
IL_002f: ret
}");
+
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true, expectedOutput: @"hello2");
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 54 (0x36)
+ .maxstack 3
+ .locals init (Program.S1 V_0)
+ IL_0000: newobj ""Program..ctor()""
+ IL_0005: dup
+ IL_0006: ldfld ""Program.S1 Program.f""
+ IL_000b: stloc.0
+ IL_000c: ldloca.s V_0
+ IL_000e: ldstr ""hello""
+ IL_0013: call ""string Program.S1.M1(string)""
+ IL_0018: call ""void System.Console.Write(string)""
+ IL_001d: ldfld ""Program.S1 Program.f""
+ IL_0022: stloc.0
+ IL_0023: ldloca.s V_0
+ IL_0025: constrained. ""Program.S1""
+ IL_002b: callvirt ""string object.ToString()""
+ IL_0030: call ""void System.Console.Write(string)""
+ IL_0035: ret
+}");
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InvokeOnReadOnlyInstanceFieldGenericMetadata()
{
var text1 = @"
@@ -257,6 +351,30 @@ .maxstack 3
IL_002a: call ""void System.Console.Write(string)""
IL_002f: ret
}");
+
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef, ref1 }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true, expectedOutput: @"hello2");
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 54 (0x36)
+ .maxstack 3
+ .locals init (S1 V_0)
+ IL_0000: newobj ""Program..ctor()""
+ IL_0005: dup
+ IL_0006: ldfld ""S1 Program.f""
+ IL_000b: stloc.0
+ IL_000c: ldloca.s V_0
+ IL_000e: ldstr ""hello""
+ IL_0013: call ""string S1.M1(string)""
+ IL_0018: call ""void System.Console.Write(string)""
+ IL_001d: ldfld ""S1 Program.f""
+ IL_0022: stloc.0
+ IL_0023: ldloca.s V_0
+ IL_0025: constrained. ""S1""
+ IL_002b: callvirt ""string object.ToString()""
+ IL_0030: call ""void System.Console.Write(string)""
+ IL_0035: ret
+}");
}
[Fact]
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
index 5aef2b906c28f..7f9b2fddcb660 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
@@ -408,6 +408,7 @@ .locals init (bool V_0) //b
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void ReadonlyFieldCanReturnByRefReadonly()
{
var text = @"
@@ -485,6 +486,48 @@ .locals init (bool V_0) //b
IL_002e: ldflda ""(int Alice, int Bob) Program.S.F1""
IL_0033: ldflda ""int System.ValueTuple.Item1""
IL_0038: ret
+}");
+ comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: false);
+
+ comp.VerifyIL("Program.Test", @"
+{
+ // Code size 70 (0x46)
+ .maxstack 1
+ .locals init (bool V_0, //b
+ int V_1,
+ System.ValueTuple V_2,
+ int V_3,
+ System.ValueTuple V_4)
+ IL_0000: ldc.i4.1
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: brfalse.s IL_0020
+ IL_0005: ldloc.0
+ IL_0006: brfalse.s IL_0012
+ IL_0008: ldarg.0
+ IL_0009: ldfld ""int Program.F""
+ IL_000e: stloc.1
+ IL_000f: ldloca.s V_1
+ IL_0011: ret
+ IL_0012: ldsfld ""(int Alice, int Bob) Program.F1""
+ IL_0017: stloc.2
+ IL_0018: ldloca.s V_2
+ IL_001a: ldflda ""int System.ValueTuple.Item1""
+ IL_001f: ret
+ IL_0020: ldloc.0
+ IL_0021: brfalse.s IL_0032
+ IL_0023: ldarg.0
+ IL_0024: ldfld ""Program.S Program.S1""
+ IL_0029: ldfld ""int Program.S.F""
+ IL_002e: stloc.3
+ IL_002f: ldloca.s V_3
+ IL_0031: ret
+ IL_0032: ldsfld ""Program.S Program.S2""
+ IL_0037: ldfld ""(int Alice, int Bob) Program.S.F1""
+ IL_003c: stloc.s V_4
+ IL_003e: ldloca.s V_4
+ IL_0040: ldflda ""int System.ValueTuple.Item1""
+ IL_0045: ret
}");
}
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs
index e6f3e1ee3e8ad..0b27e42cd4c47 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
@@ -7221,6 +7222,7 @@ .locals init (T V_0, //v
}
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void ConditionalAccessOffReadOnlyNullable1()
{
var source = @"
@@ -7236,33 +7238,58 @@ static void Main()
}
}
";
- var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: @"");
+ var comp = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: @"", verify: false);
- verifier.VerifyIL("Program.Main", @"
- {
- // Code size 47 (0x2f)
- .maxstack 2
- .locals init (System.Guid? V_0,
- System.Guid V_1)
- IL_0000: nop
- IL_0001: ldsfld ""System.Guid? Program.g""
- IL_0006: stloc.0
- IL_0007: ldloca.s V_0
- IL_0009: dup
- IL_000a: call ""bool System.Guid?.HasValue.get""
- IL_000f: brtrue.s IL_0015
- IL_0011: pop
- IL_0012: ldnull
- IL_0013: br.s IL_0028
- IL_0015: call ""System.Guid System.Guid?.GetValueOrDefault()""
- IL_001a: stloc.1
- IL_001b: ldloca.s V_1
- IL_001d: constrained. ""System.Guid""
- IL_0023: callvirt ""string object.ToString()""
- IL_0028: call ""void System.Console.WriteLine(string)""
- IL_002d: nop
- IL_002e: ret
- }");
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 44 (0x2c)
+ .maxstack 2
+ .locals init (System.Guid V_0)
+ IL_0000: nop
+ IL_0001: ldsflda ""System.Guid? Program.g""
+ IL_0006: dup
+ IL_0007: call ""bool System.Guid?.HasValue.get""
+ IL_000c: brtrue.s IL_0012
+ IL_000e: pop
+ IL_000f: ldnull
+ IL_0010: br.s IL_0025
+ IL_0012: call ""System.Guid System.Guid?.GetValueOrDefault()""
+ IL_0017: stloc.0
+ IL_0018: ldloca.s V_0
+ IL_001a: constrained. ""System.Guid""
+ IL_0020: callvirt ""string object.ToString()""
+ IL_0025: call ""void System.Console.WriteLine(string)""
+ IL_002a: nop
+ IL_002b: ret
+}");
+
+ comp = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: @"", parseOptions:TestOptions.Regular.WithPEVerifyCompatFeature(), verify: true);
+
+ comp.VerifyIL("Program.Main", @"
+{
+ // Code size 47 (0x2f)
+ .maxstack 2
+ .locals init (System.Guid? V_0,
+ System.Guid V_1)
+ IL_0000: nop
+ IL_0001: ldsfld ""System.Guid? Program.g""
+ IL_0006: stloc.0
+ IL_0007: ldloca.s V_0
+ IL_0009: dup
+ IL_000a: call ""bool System.Guid?.HasValue.get""
+ IL_000f: brtrue.s IL_0015
+ IL_0011: pop
+ IL_0012: ldnull
+ IL_0013: br.s IL_0028
+ IL_0015: call ""System.Guid System.Guid?.GetValueOrDefault()""
+ IL_001a: stloc.1
+ IL_001b: ldloca.s V_1
+ IL_001d: constrained. ""System.Guid""
+ IL_0023: callvirt ""string object.ToString()""
+ IL_0028: call ""void System.Console.WriteLine(string)""
+ IL_002d: nop
+ IL_002e: ret
+}");
}
[Fact]
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
index 269f94a9f846e..5406682f7420f 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
@@ -11973,6 +11973,7 @@ public override V PrintField(V v, W w)
#endregion
[Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void MutateReadonlyNested()
{
string source = @"
@@ -12024,10 +12025,42 @@ public MyManagedStruct(int x)
n.n.num = x;
}
}";
- var compilation = CompileAndVerify(source, expectedOutput: @"42", verify: false);
+ var comp = CompileAndVerify(source, expectedOutput: @"42", verify: false);
- // Dev10
- compilation.VerifyIL("Program.Main",
+ comp.VerifyIL("Program.Main",
+@"
+{
+ // Code size 76 (0x4c)
+ .maxstack 3
+ .locals init (MyManagedStruct V_0,
+ MyManagedStruct.Nested.Nested1 V_1)
+ IL_0000: newobj ""cls1..ctor()""
+ IL_0005: dup
+ IL_0006: ldfld ""MyManagedStruct cls1.y""
+ IL_000b: stloc.0
+ IL_000c: ldloca.s V_0
+ IL_000e: ldc.i4.s 123
+ IL_0010: call ""void MyManagedStruct.mutate(int)""
+ IL_0015: dup
+ IL_0016: ldflda ""MyManagedStruct cls1.y""
+ IL_001b: ldflda ""MyManagedStruct.Nested MyManagedStruct.n""
+ IL_0020: ldfld ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
+ IL_0025: stloc.1
+ IL_0026: ldloca.s V_1
+ IL_0028: ldc.i4 0x1c8
+ IL_002d: call ""void MyManagedStruct.Nested.Nested1.mutate(int)""
+ IL_0032: ldflda ""MyManagedStruct cls1.y""
+ IL_0037: ldflda ""MyManagedStruct.Nested MyManagedStruct.n""
+ IL_003c: ldflda ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
+ IL_0041: ldfld ""int MyManagedStruct.Nested.Nested1.num""
+ IL_0046: call ""void System.Console.WriteLine(int)""
+ IL_004b: ret
+}
+");
+
+ comp = CompileAndVerify(source, expectedOutput: @"42", verify: true, parseOptions:TestOptions.Regular.WithPEVerifyCompatFeature());
+
+ comp.VerifyIL("Program.Main",
@"
{
// Code size 76 (0x4c)
@@ -12048,9 +12081,9 @@ .locals init (MyManagedStruct V_0)
IL_0023: ldflda ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0028: ldc.i4 0x1c8
IL_002d: call ""void MyManagedStruct.Nested.Nested1.mutate(int)""
- IL_0032: ldflda ""MyManagedStruct cls1.y""
- IL_0037: ldflda ""MyManagedStruct.Nested MyManagedStruct.n""
- IL_003c: ldflda ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
+ IL_0032: ldfld ""MyManagedStruct cls1.y""
+ IL_0037: ldfld ""MyManagedStruct.Nested MyManagedStruct.n""
+ IL_003c: ldfld ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0041: ldfld ""int MyManagedStruct.Nested.Nested1.num""
IL_0046: call ""void System.Console.WriteLine(int)""
IL_004b: ret
@@ -12058,6 +12091,120 @@ .locals init (MyManagedStruct V_0)
");
}
+ [Fact]
+ [CompilerTrait(CompilerFeature.PEVerifyCompat)]
+ public void MutateReadonlyNested1()
+ {
+ string source = @"
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ GetRoRef().ro.ro.ro.ro.ToString();
+ System.Console.Write(GetRoRef().ro.ro.ro.ro.x);
+ }
+
+ private static ref readonly Largest GetRoRef()
+ {
+ return ref (new Largest[1])[0];
+ }
+ }
+
+ struct Largest
+ {
+ public int x;
+ public readonly Large ro;
+ }
+
+ struct Large
+ {
+ public int x;
+ public Medium ro;
+ }
+
+ struct Medium
+ {
+ public int x;
+ public Small ro;
+ }
+
+ struct Small
+ {
+ public int x;
+ public Smallest ro;
+ }
+
+ struct Smallest
+ {
+ public int x;
+
+ public override string ToString()
+ {
+ x = -1;
+ System.Console.Write(x);
+ return null;
+ }
+ }";
+ var comp = CompileAndVerify(source, expectedOutput: @"-10", verify: false);
+
+ comp.VerifyIL("Program.Main",
+@"
+{
+ // Code size 76 (0x4c)
+ .maxstack 1
+ .locals init (Smallest V_0)
+ IL_0000: call ""ref readonly Largest Program.GetRoRef()""
+ IL_0005: ldflda ""Large Largest.ro""
+ IL_000a: ldflda ""Medium Large.ro""
+ IL_000f: ldflda ""Small Medium.ro""
+ IL_0014: ldfld ""Smallest Small.ro""
+ IL_0019: stloc.0
+ IL_001a: ldloca.s V_0
+ IL_001c: constrained. ""Smallest""
+ IL_0022: callvirt ""string object.ToString()""
+ IL_0027: pop
+ IL_0028: call ""ref readonly Largest Program.GetRoRef()""
+ IL_002d: ldflda ""Large Largest.ro""
+ IL_0032: ldflda ""Medium Large.ro""
+ IL_0037: ldflda ""Small Medium.ro""
+ IL_003c: ldflda ""Smallest Small.ro""
+ IL_0041: ldfld ""int Smallest.x""
+ IL_0046: call ""void System.Console.Write(int)""
+ IL_004b: ret
+}
+");
+
+ comp = CompileAndVerify(source, expectedOutput: @"-10", verify: true, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
+
+ comp.VerifyIL("Program.Main",
+@"
+ {
+ // Code size 76 (0x4c)
+ .maxstack 1
+ .locals init (Large V_0)
+ IL_0000: call ""ref readonly Largest Program.GetRoRef()""
+ IL_0005: ldfld ""Large Largest.ro""
+ IL_000a: stloc.0
+ IL_000b: ldloca.s V_0
+ IL_000d: ldflda ""Medium Large.ro""
+ IL_0012: ldflda ""Small Medium.ro""
+ IL_0017: ldflda ""Smallest Small.ro""
+ IL_001c: constrained. ""Smallest""
+ IL_0022: callvirt ""string object.ToString()""
+ IL_0027: pop
+ IL_0028: call ""ref readonly Largest Program.GetRoRef()""
+ IL_002d: ldfld ""Large Largest.ro""
+ IL_0032: ldfld ""Medium Large.ro""
+ IL_0037: ldfld ""Small Medium.ro""
+ IL_003c: ldfld ""Smallest Small.ro""
+ IL_0041: ldfld ""int Smallest.x""
+ IL_0046: call ""void System.Console.Write(int)""
+ IL_004b: ret
+ }
+");
+ }
+
[Fact]
public void LocalNumericConstToString()
{
diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs
index cf98ad0462a6a..eb23668520f24 100644
--- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs
+++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs
@@ -58,6 +58,11 @@ public static CSharpParseOptions WithStrictFeature(this CSharpParseOptions optio
return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair("strict", "true") }));
}
+ public static CSharpParseOptions WithPEVerifyCompatFeature(this CSharpParseOptions options)
+ {
+ return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair("peverify-compat", "true") }));
+ }
+
public static CSharpParseOptions WithLocalFunctionsFeature(this CSharpParseOptions options)
{
return options;
diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs
index faed61585a64f..15fb04c3fa233 100644
--- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs
+++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs
@@ -23,5 +23,6 @@ public enum CompilerFeature
Patterns,
DefaultLiteral,
AsyncMain,
+ PEVerifyCompat,
}
}