diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index 2c28adf4cc8aa20d1a31bd331a91d8d48b8bde04..66bcfac999ee06e23abe0a6a24c4ae45d10a8cab 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -75,6 +75,7 @@ + diff --git a/src/Compilers/CSharp/Test/Emit/Emit/BinaryCompatibility.cs b/src/Compilers/CSharp/Test/Emit/Emit/BinaryCompatibility.cs new file mode 100644 index 0000000000000000000000000000000000000000..626392e2a82aa055389a49faf86176d990594524 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/Emit/BinaryCompatibility.cs @@ -0,0 +1,69 @@ +// 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 Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class BinaryCompatibility : EmitMetadataTestBase + { + [Fact] + public void InvokeVirtualBoundToOriginal() + { + // A method invocation of a virtual method is statically bound by the C# language to + // the original declaration of the virtual method, not the most derived override of + // that method. The difference between these two choices is visible in the binary + // compatibility behavior of the program at runtime (e.g. when executing the program + // against a modified library). This test checks that we bind the invocation to the + // virtual method, not the override. + var lib0 = @" +public class Base +{ + public virtual void M() { System.Console.WriteLine(""Base0""); } +} +public class Derived : Base +{ + public override void M() { System.Console.WriteLine(""Derived0""); } +} +"; + var lib0Image = CreateCompilationWithMscorlib46(lib0, options: TestOptions.ReleaseDll, assemblyName: "lib").EmitToImageReference(); + + var lib1 = @" +public class Base +{ + public virtual void M() { System.Console.WriteLine(""Base1""); } +} +public class Derived : Base +{ + public new virtual void M() { System.Console.WriteLine(""Derived1""); } +} +"; + var lib1Image = CreateCompilationWithMscorlib46(lib1, options: TestOptions.ReleaseDll, assemblyName: "lib").EmitToImageReference(); + + var client = @" +public class Client +{ + public static void M() + { + Derived d = new Derived(); + d.M(); + } +} +"; + var clientImage = CreateCompilationWithMscorlib46(client, references: new[] { lib0Image }, options: TestOptions.ReleaseDll).EmitToImageReference(); + + var program = @" +public class Program +{ + public static void Main() + { + Client.M(); + } +} +"; + var compilation = CreateCompilationWithMscorlib46(program, references: new[] { lib1Image, clientImage }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: @"Base1"); + } + } +}