From ffa2fc7794ebbd13f2e6f6d2b93cb67b0a14f658 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 11 Sep 2015 16:14:20 -0700 Subject: [PATCH] Elimination of reference duplicates in the ReferenceManager should not depend on order --- Roslyn.sln | 1 + .../Compilation/ReferenceManagerTests.cs | 163 ++++++++++-------- .../AssemblyIdentityComparerTests.cs | 22 +++ .../DesktopAssemblyIdentityComparer.cs | 12 +- .../AssemblyIdentityComparer.cs | 21 ++- .../CommonReferenceManager.Resolution.cs | 9 +- .../Core/CompilerTestResources.csproj | 3 +- src/Compilers/Test/Resources/Core/TestKeys.cs | 35 ++++ .../Compilation/ReferenceManagerTests.vb | 5 +- 9 files changed, 181 insertions(+), 90 deletions(-) create mode 100644 src/Compilers/Test/Resources/Core/TestKeys.cs diff --git a/Roslyn.sln b/Roslyn.sln index e09498d4a21..c4729a3f45e 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -2748,6 +2748,7 @@ Global {DEB3D675-5A3C-46DA-8945-F2EFAB135EA0} = {62F787B2-1E8B-4A3C-BCC0-0EBAE50B42B7} {E3CD2895-76A8-4D11-A316-EA67CB5EA42C} = {32A48625-F0AD-419D-828B-A50BDABA38EA} {8CE3A581-2969-4864-A803-013E9D977C3A} = {C65C6143-BED3-46E6-869E-9F0BE6E84C37} + {6FF42825-5464-4151-AC55-ED828168C192} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9} {CCBD3438-3E84-40A9-83AD-533F23BCFCA5} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9} {6FD1CC3E-6A99-4736-9B8D-757992DDE75D} = {38940C5F-97FD-4B2A-B2CD-C4E4EF601B05} {286B01F3-811A-40A7-8C1F-10C9BB0597F7} = {38940C5F-97FD-4B2A-B2CD-C4E4EF601B05} diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs index ed385bdfe20..2fb8413cfcc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs @@ -12,17 +12,15 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using System.Reflection.PortableExecutable; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class ReferenceManagerTests : CSharpTestBase { - private static readonly CSharpCompilationOptions s_signedDll = TestOptions.ReleaseDll. - WithCryptoKeyFile(SigningTestHelpers.KeyPairFile). - WithStrongNameProvider(new SigningTestHelpers.VirtualizedStrongNameProvider(ImmutableArray.Create())); + private static readonly CSharpCompilationOptions s_signedDll = + TestOptions.ReleaseDll.WithCryptoPublicKey(TestResources.TestKeys.PublicKey_ce65828c82a341f2); - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void WinRtCompilationReferences() { var ifaceDef = CreateCompilationWithMscorlib( @@ -50,7 +48,7 @@ public static void Main() implDef2.VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void VersionUnification_SymbolUsed() { // Identity: C, Version=1.0.0.0, Culture=neutral, PublicKeyToken=374d0c2befcd8cc9 @@ -94,7 +92,7 @@ public void VersionUnification_SymbolUsed() "C, Version=1.0.0.0, Culture=neutral, PublicKeyToken=374d0c2befcd8cc9")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(546080, "DevDiv")] public void VersionUnification_SymbolNotUsed() { @@ -114,7 +112,7 @@ public void VersionUnification_SymbolNotUsed() testRefV2.VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void VersionUnification_MultipleVersions() { string sourceLibV1 = @" @@ -214,7 +212,7 @@ public void F() "Lib")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(529808, "DevDiv"), WorkItem(530246, "DevDiv")] public void VersionUnification_UseSiteWarnings() { @@ -398,7 +396,7 @@ public class OKImpl : I verify: false); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(546080, "DevDiv")] public void VersionUnification_UseSiteDiagnostics_Multiple() { @@ -502,7 +500,7 @@ public void F() "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void VersionUnification_UseSiteDiagnostics_OptionalAttributes() { string sourceLibV1 = @" @@ -586,7 +584,7 @@ public class C main.VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void VersionUnification_SymbolEquality() { string sourceLibV1 = @" @@ -651,7 +649,7 @@ public void F() "Lib")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(546752, "DevDiv")] public void VersionUnification_NoPiaMissingCanonicalTypeSymbol() { @@ -728,7 +726,7 @@ static void Main() } [WorkItem(546525, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void AssemblyReferencesWithAliases() { var source = @@ -755,7 +753,7 @@ public static void Main(string[] args) } [WorkItem(545062, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateReferences() { CSharpCompilation c; @@ -850,7 +848,7 @@ private static string MakeEquivalentPath(string path) return newParts.Join(Path.DirectorySeparatorChar.ToString()); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateAssemblyReferences_EquivalentPath() { string p1 = Temp.CreateFile().WriteAllBytes(TestResources.General.MDTestLib1).Path; @@ -920,7 +918,7 @@ public void DuplicateAssemblyReferences_EquivalentPath() Assert.Equal(p3, dr3.FilePath); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateModuleReferences_EquivalentPath() { var dir = Temp.CreateDirectory(); @@ -956,7 +954,7 @@ public void DuplicateModuleReferences_EquivalentPath() /// /// Two metadata files with the same strong identity referenced twice, with embedInteropTypes=true and embedInteropTypes=false. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateAssemblyReferences_EquivalentStrongNames_Metadata() { var ref1 = AssemblyMetadata.CreateFromImage(TestResources.General.C2).GetReference(embedInteropTypes: true, filePath: @"R:\A\MTTestLib1.dll"); @@ -971,7 +969,7 @@ public void DuplicateAssemblyReferences_EquivalentStrongNames_Metadata() /// /// Two compilations with the same strong identity referenced twice, with embedInteropTypes=true and embedInteropTypes=false. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateAssemblyReferences_EquivalentStrongNames_Compilations() { var sourceLib = @" @@ -990,7 +988,7 @@ public interface I {}"; Diagnostic(ErrorCode.ERR_AssemblySpecifiedForLinkAndRef).WithArguments("Lib", "Lib")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void DuplicateAssemblyReferences_EquivalentName() { string p1 = Temp.CreateFile().WriteAllBytes(TestResources.NetFX.v4_0_30319.System_Core).Path; @@ -1009,7 +1007,7 @@ public void DuplicateAssemblyReferences_EquivalentName() /// /// Two Framework identities with unified versions. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(546026, "DevDiv"), WorkItem(546169, "DevDiv")] public void CS1703ERR_DuplicateImport() { @@ -1028,7 +1026,7 @@ public void CS1703ERR_DuplicateImport() Diagnostic(ErrorCode.ERR_DuplicateImport).WithArguments(p1, p2)); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void CS1704ERR_DuplicateImportSimple() { var libSource = @" @@ -1036,31 +1034,13 @@ public void CS1704ERR_DuplicateImportSimple() public class A { }"; - var c1 = CreateCompilationWithMscorlib(libSource, options: TestOptions.ReleaseDll, assemblyName: "CS1704"); + var peImage = CreateCompilationWithMscorlib(libSource, options: TestOptions.ReleaseDll, assemblyName: "CS1704").EmitToArray(); var dir1 = Temp.CreateDirectory(); - var exe1 = dir1.CreateFile("CS1704.dll"); - var pdb1 = dir1.CreateFile("CS1704.pdb"); + var exe1 = dir1.CreateFile("CS1704.dll").WriteAllBytes(peImage); var dir2 = Temp.CreateDirectory(); - var exe2 = dir2.CreateFile("CS1704.dll"); - var pdb2 = dir2.CreateFile("CS1704.pdb"); - - using (var output = exe1.Open()) - { - using (var outputPdb = pdb1.Open()) - { - c1.Emit(output, outputPdb); - } - } - - using (var output = exe2.Open()) - { - using (var outputPdb = pdb2.Open()) - { - c1.Emit(output, outputPdb); - } - } + var exe2 = dir2.CreateFile("CS1704.dll").WriteAllBytes(peImage); var ref1 = AssemblyMetadata.CreateFromFile(exe1.Path).GetReference(aliases: ImmutableArray.Create("A1")); var ref2 = AssemblyMetadata.CreateFromFile(exe2.Path).GetReference(aliases: ImmutableArray.Create("A2")); @@ -1078,7 +1058,7 @@ class C : A2::A { } CreateCompilationWithMscorlib(source, new[] { ref1, ref2 }).VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void WeakIdentitiesWithDifferentVersions() { var sourceLibV1 = @" @@ -1132,7 +1112,7 @@ public class Q /// Although the CLR considers all WinRT references equivalent the Dev11 C# and VB compilers still /// compare their identities as if they were regular managed dlls. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void WinMd_SameSimpleNames_SameVersions() { var sourceMain = @" @@ -1167,7 +1147,7 @@ public class Q /// Although the CLR considers all WinRT references equivalent the Dev11 C# and VB compilers still /// compare their identities as if they were regular managed dlls. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void WinMd_DifferentSimpleNames() { var sourceMain = @" @@ -1197,7 +1177,7 @@ public class Q /// Although the CLR considers all WinRT references equivalent the Dev11 C# and VB compilers still /// compare their identities as if they were regular managed dlls. /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void WinMd_SameSimpleNames_DifferentVersions() { var sourceMain = @" @@ -1230,7 +1210,7 @@ public class Q /// /// We replicate the Dev11 behavior here but is there any real world scenario for this? /// - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void MetadataReferencesDifferInCultureOnly() { var arSA = TestReferences.SymbolsTests.Versioning.AR_SA; @@ -1284,7 +1264,7 @@ public override string ResolveReference(string reference, string baseFilePath) public bool resolved1, resolved2; } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ReferenceResolution1() { var path1 = Temp.CreateFile().WriteAllBytes(TestResources.General.MDTestLib1).Path; @@ -1347,7 +1327,7 @@ public override PortableExecutableReference GetReference(string fullPath, Metada } } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ReferenceResolution_ExceptionsFromResolver() { var options = TestOptions.ReleaseDll. @@ -1363,7 +1343,7 @@ public void ReferenceResolution_ExceptionsFromResolver() } } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ReferenceResolution_ExceptionsFromProvider() { var provider = new ErroneousMetadataReferenceProvider(); @@ -1387,7 +1367,7 @@ public void ReferenceResolution_ExceptionsFromProvider() Diagnostic(ErrorCode.ERR_NoMetadataFile, @"#r ""c:\null.dll""").WithArguments(@"c:\null.dll")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ResolvedReferencesCaching() { var c1 = CSharpCompilation.Create("foo", @@ -1401,7 +1381,7 @@ public void ResolvedReferencesCaching() var a2 = c2.SourceAssembly; } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void CyclesInReferences() { var sourceA = @" @@ -1457,7 +1437,7 @@ public void Bar() Diagnostic(ErrorCode.ERR_MissingTypeInSource, "x").WithArguments("Foo")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void BoundReferenceCaching_CyclesInReferences() { var a = CreateCompilationWithMscorlib("public class A { }", assemblyName: "A"); @@ -1490,7 +1470,7 @@ public void BoundReferenceCaching_CyclesInReferences() } [WorkItem(546828, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void MetadataDependsOnSource() { // {0} is the body of the ReachFramework assembly reference. @@ -1598,7 +1578,7 @@ static void Main() } [WorkItem(546828, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void MetadataDependsOnMetadataOrSource() { var il = @" @@ -1687,7 +1667,7 @@ public class PrintTicket Assert.NotEqual(comp.Assembly.Identity, actualIdentity); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(546900, "DevDiv")] public void MetadataRefersToSourceAssemblyModule() { @@ -1732,7 +1712,7 @@ public class B b.VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(530839, "DevDiv")] public void EmbedInteropTypesReferences() { @@ -1770,7 +1750,7 @@ public class C : I { } } [WorkItem(531537, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ModuleSymbolReuse() { var text1 = @" @@ -1826,7 +1806,7 @@ class D } [WorkItem(531537, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ModuleSymbolReuse_ImplicitType() { var text1 = @" @@ -1859,7 +1839,7 @@ namespace A Assert.Equal(1, implicitTypeCount2); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void CachingAndVisibility() { var cPublic = CreateCompilationWithMscorlib("class C { }", options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.Public)); @@ -1879,7 +1859,7 @@ public void CachingAndVisibility() Assert.Same(cAll.Assembly.CorLibrary, cAll2.Assembly.CorLibrary); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ImportingPrivateNetModuleMembers() { string moduleSource = @" @@ -1909,7 +1889,7 @@ internal class C Assert.Equal(0, mPublic.Length); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(531342, "DevDiv"), WorkItem(727122, "DevDiv")] public void PortableLibrary() { @@ -1930,7 +1910,7 @@ public void PortableLibrary() main.VerifyDiagnostics(); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(762729, "DevDiv")] public void OverloadResolutionUseSiteWarning() { @@ -1975,7 +1955,7 @@ public void Test() Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "a.M").WithArguments("B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(762729, "DevDiv")] public void MethodGroupConversionUseSiteWarning() { @@ -2020,7 +2000,7 @@ public void Test() Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "a.M").WithArguments("B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(762729, "DevDiv")] public void IndexerUseSiteWarning() { @@ -2061,7 +2041,7 @@ public void Test() Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "a[null]").WithArguments("B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); } - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] [WorkItem(762729, "DevDiv")] public void Repro762729() { @@ -2092,6 +2072,9 @@ public static void Extension(this IGeneric i) var libBv1 = CreateCompilationWithMscorlibAndSystemCore(string.Format(libBTemplate, "1"), assemblyName: "B", options: s_signedDll); var libBv2 = CreateCompilationWithMscorlibAndSystemCore(string.Format(libBTemplate, "2"), assemblyName: "B", options: s_signedDll); + Assert.Equal("B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", libBv1.Assembly.Identity.GetDisplayName()); + Assert.Equal("B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", libBv2.Assembly.Identity.GetDisplayName()); + libBv1.EmitToImageReference(); libBv2.EmitToImageReference(); @@ -2140,7 +2123,7 @@ public void Test(A a) } [WorkItem(905495, "DevDiv")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ReferenceWithNoMetadataSection() { var c = CreateCompilationWithMscorlib("", new[] { new TestImageReference(TestResources.Basic.NativeApp, "NativeApp.exe") }); @@ -2150,7 +2133,7 @@ public void ReferenceWithNoMetadataSection() } [WorkItem(2988, "https://github.com/dotnet/roslyn/issues/2988")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void EmptyReference1() { var source = "class C { public static void Main() { } }"; @@ -2161,7 +2144,7 @@ public void EmptyReference1() } [WorkItem(2992, "https://github.com/dotnet/roslyn/issues/2992")] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void MetadataDisposed() { var md = AssemblyMetadata.CreateFromImage(TestResources.NetFX.Minimal.mincorlib); @@ -2176,7 +2159,7 @@ public void MetadataDisposed() } [WorkItem(43)] - [ClrOnlyFact(ClrOnlyReason.Signing)] + [Fact] public void ReusingCorLibManager() { var corlib1 = CreateCompilation(""); @@ -2189,5 +2172,43 @@ public void ReusingCorLibManager() Assert.Same(assembly2.CorLibrary, assembly2); Assert.True(corlib1.ReferenceManagerEquals(corlib2)); } + + [WorkItem(5138)] + [Fact] + public void AsymmetricUnification() + { + var vectors40 = CreateCompilationWithMscorlib( + @"[assembly: System.Reflection.AssemblyVersion(""4.0.0.0"")]", + options: TestOptions.ReleaseDll.WithCryptoPublicKey(TestResources.TestKeys.PublicKey_b03f5f7f11d50a3a), + assemblyName: "System.Numerics.Vectors"); + + Assert.Equal("System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", vectors40.Assembly.Identity.GetDisplayName()); + + var vectors41 = CreateCompilationWithMscorlib( + @"[assembly: System.Reflection.AssemblyVersion(""4.1.0.0"")]", + options: TestOptions.ReleaseDll.WithCryptoPublicKey(TestResources.TestKeys.PublicKey_b03f5f7f11d50a3a), + assemblyName: "System.Numerics.Vectors"); + + Assert.Equal("System.Numerics.Vectors, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", vectors41.Assembly.Identity.GetDisplayName()); + + var refVectors40 = vectors40.EmitToImageReference(); + var refVectors41 = vectors41.EmitToImageReference(); + + var c1 = CreateCompilationWithMscorlib("", new[] { refVectors40, refVectors41 }, options: TestOptions.ReleaseDll.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)); + c1.VerifyDiagnostics(); + + var a0 = c1.GetAssemblyOrModuleSymbol(refVectors40); + var a1 = c1.GetAssemblyOrModuleSymbol(refVectors41); + Assert.Equal("System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a0).Identity.GetDisplayName()); + Assert.Equal("System.Numerics.Vectors, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a1).Identity.GetDisplayName()); + + var c2 = CreateCompilationWithMscorlib("", new[] { refVectors41, refVectors40 }, options: TestOptions.ReleaseDll.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)); + c2.VerifyDiagnostics(); + + a0 = c2.GetAssemblyOrModuleSymbol(refVectors40); + a1 = c2.GetAssemblyOrModuleSymbol(refVectors41); + Assert.Equal("System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a0).Identity.GetDisplayName()); + Assert.Equal("System.Numerics.Vectors, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a1).Identity.GetDisplayName()); + } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityComparerTests.cs b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityComparerTests.cs index cb5a365171e..8508adbee91 100644 --- a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityComparerTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityComparerTests.cs @@ -569,6 +569,28 @@ public void IgnoreOrFwUnifyVersion() } } + [Fact] + public void AsymmetricUnification() + { + // Note: + // System.Numerics.Vectors, Version=4.0 is an FX assembly + // System.Numerics.Vectors, Version=4.1+ is not an FX assembly + // + // It seems like a bug in fusion: it only determines whether the definition is an FX assembly + // and calculates the result based upon that, regardless of whether the reference is an FX assembly or not. + // We do replicate that behavior. + TestMatch( + "System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Numerics.Vectors, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + AssemblyIdentityComparer.ComparisonResult.NotEquivalent); + + TestMatch( + "System.Numerics.Vectors, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + AssemblyIdentityComparer.ComparisonResult.Equivalent, + unificationApplied: true); + } + [Fact] public void Portability() { diff --git a/src/Compilers/Core/Portable/Desktop/DesktopAssemblyIdentityComparer.cs b/src/Compilers/Core/Portable/Desktop/DesktopAssemblyIdentityComparer.cs index a7e598a454f..31f5793872e 100644 --- a/src/Compilers/Core/Portable/Desktop/DesktopAssemblyIdentityComparer.cs +++ b/src/Compilers/Core/Portable/Desktop/DesktopAssemblyIdentityComparer.cs @@ -81,13 +81,13 @@ internal AssemblyPortabilityPolicy PortabilityPolicy ref AssemblyIdentity reference, ref AssemblyIdentity definition, AssemblyIdentityParts referenceParts, - out bool isFxAssembly) + out bool isDefinitionFxAssembly) { if (reference.ContentType == AssemblyContentType.Default && SimpleNameComparer.Equals(reference.Name, definition.Name) && SimpleNameComparer.Equals(reference.Name, "mscorlib")) { - isFxAssembly = true; + isDefinitionFxAssembly = true; reference = definition; return true; } @@ -96,7 +96,7 @@ internal AssemblyPortabilityPolicy PortabilityPolicy { // Reference is not retargetable, but definition is retargetable. // Non-equivalent. - isFxAssembly = false; + isDefinitionFxAssembly = false; return false; } @@ -113,7 +113,7 @@ internal AssemblyPortabilityPolicy PortabilityPolicy { if (!AssemblyIdentity.IsFullName(referenceParts)) { - isFxAssembly = false; + isDefinitionFxAssembly = false; return false; } @@ -139,11 +139,11 @@ internal AssemblyPortabilityPolicy PortabilityPolicy if (reference.IsRetargetable && definition.IsRetargetable) { - isFxAssembly = IsRetargetableAssembly(definition); + isDefinitionFxAssembly = IsRetargetableAssembly(definition); } else { - isFxAssembly = IsFrameworkAssembly(definition); + isDefinitionFxAssembly = IsFrameworkAssembly(definition); } return true; diff --git a/src/Compilers/Core/Portable/MetadataReference/AssemblyIdentityComparer.cs b/src/Compilers/Core/Portable/MetadataReference/AssemblyIdentityComparer.cs index 05d65f619d6..d4f1ff6c254 100644 --- a/src/Compilers/Core/Portable/MetadataReference/AssemblyIdentityComparer.cs +++ b/src/Compilers/Core/Portable/MetadataReference/AssemblyIdentityComparer.cs @@ -113,8 +113,8 @@ internal ComparisonResult Compare(AssemblyIdentity reference, string referenceDi Debug.Assert(reference.ContentType == definition.ContentType); - bool isFxAssembly; - if (!ApplyUnificationPolicies(ref reference, ref definition, parts, out isFxAssembly)) + bool isDefinitionFxAssembly; + if (!ApplyUnificationPolicies(ref reference, ref definition, parts, out isDefinitionFxAssembly)) { return ComparisonResult.NotEquivalent; } @@ -151,7 +151,7 @@ internal ComparisonResult Compare(AssemblyIdentity reference, string referenceDi return ComparisonResult.Equivalent; } - isFxAssembly = false; + isDefinitionFxAssembly = false; } if (!SimpleNameComparer.Equals(reference.Name, definition.Name)) @@ -177,7 +177,16 @@ internal ComparisonResult Compare(AssemblyIdentity reference, string referenceDi hasSomeVersionParts && (hasPartialVersion || reference.Version != definition.Version)) { - if (isFxAssembly) + // Note: + // System.Numerics.Vectors, Version=4.0 is an FX assembly + // System.Numerics.Vectors, Version=4.1+ is not an FX assembly + // + // It seems like a bug in Fusion: it only determines whether the definition is an FX assembly + // and calculates the result based upon that, regardless of whether the reference is an FX assembly or not. + // We do replicate the behavior. + // + // As a result unification is asymmetric when comparing the above identities. + if (isDefinitionFxAssembly) { unificationApplied = true; return ComparisonResult.Equivalent; @@ -211,9 +220,9 @@ internal ComparisonResult Compare(AssemblyIdentity reference, string referenceDi return AssemblyIdentity.MemberwiseEqual(x, y); } - internal virtual bool ApplyUnificationPolicies(ref AssemblyIdentity reference, ref AssemblyIdentity definition, AssemblyIdentityParts referenceParts, out bool isFxAssembly) + internal virtual bool ApplyUnificationPolicies(ref AssemblyIdentity reference, ref AssemblyIdentity definition, AssemblyIdentityParts referenceParts, out bool isDefinitionFxAssembly) { - isFxAssembly = false; + isDefinitionFxAssembly = false; return true; } } diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs index aad10d5136f..1ffd4efbcd5 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs @@ -584,8 +584,13 @@ private static void AddModule(PEModule module, int referenceIndex, ResolvedRefer { foreach (var other in sameSimpleNameIdentities) { - // only compare strong with strong (weak is never equivalent to strong and vice versa) - if (other.Identity.IsStrongName && IdentityComparer.ReferenceMatchesDefinition(identity, other.Identity)) + // Only compare strong with strong (weak is never equivalent to strong and vice versa). + // In order to eliminate duplicate references we need to try to match their identities in both directions since + // ReferenceMatchesDefinition is not neccessarily symmetric. + // (e.g. System.Numerics.Vectors, Version=4.1+ matches System.Numerics.Vectors, Version=4.0, but not the other way around.) + if (other.Identity.IsStrongName && + IdentityComparer.ReferenceMatchesDefinition(identity, other.Identity) && + IdentityComparer.ReferenceMatchesDefinition(other.Identity, identity)) { equivalent = other; break; diff --git a/src/Compilers/Test/Resources/Core/CompilerTestResources.csproj b/src/Compilers/Test/Resources/Core/CompilerTestResources.csproj index fbcb974a390..316891e7533 100644 --- a/src/Compilers/Test/Resources/Core/CompilerTestResources.csproj +++ b/src/Compilers/Test/Resources/Core/CompilerTestResources.csproj @@ -411,6 +411,7 @@ + @@ -420,4 +421,4 @@ - + \ No newline at end of file diff --git a/src/Compilers/Test/Resources/Core/TestKeys.cs b/src/Compilers/Test/Resources/Core/TestKeys.cs new file mode 100644 index 00000000000..0ce63705f55 --- /dev/null +++ b/src/Compilers/Test/Resources/Core/TestKeys.cs @@ -0,0 +1,35 @@ +using System.Collections.Immutable; + +namespace TestResources +{ + public static class TestKeys + { + public static readonly ImmutableArray PublicKey_ce65828c82a341f2 = ImmutableArray.Create(new byte[] + { + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x2B, 0x98, 0x6F, 0x6B, 0x5E, 0xA5, 0x71, 0x7D, 0x35, 0xC7, 0x2D, 0x38, 0x56, 0x1F, 0x41, 0x3E, + 0x26, 0x70, 0x29, 0xEF, 0xA9, 0xB5, 0xF1, 0x07, 0xB9, 0x33, 0x1D, 0x83, 0xDF, 0x65, 0x73, 0x81, + 0x32, 0x5B, 0x3A, 0x67, 0xB7, 0x58, 0x12, 0xF6, 0x3A, 0x94, 0x36, 0xCE, 0xCC, 0xB4, 0x94, 0x94, + 0xDE, 0x8F, 0x57, 0x4F, 0x8E, 0x63, 0x9D, 0x4D, 0x26, 0xC0, 0xFC, 0xF8, 0xB0, 0xE9, 0xA1, 0xA1, + 0x96, 0xB8, 0x0B, 0x6F, 0x6E, 0xD0, 0x53, 0x62, 0x8D, 0x10, 0xD0, 0x27, 0xE0, 0x32, 0xDF, 0x2E, + 0xD1, 0xD6, 0x08, 0x35, 0xE5, 0xF4, 0x7D, 0x32, 0xC9, 0xEF, 0x6D, 0xA1, 0x0D, 0x03, 0x66, 0xA3, + 0x19, 0x57, 0x33, 0x62, 0xC8, 0x21, 0xB5, 0xF8, 0xFA, 0x5A, 0xBC, 0x5B, 0xB2, 0x22, 0x41, 0xDE, + 0x6F, 0x66, 0x6A, 0x85, 0xD8, 0x2D, 0x6B, 0xA8, 0xC3, 0x09, 0x0D, 0x01, 0x63, 0x6B, 0xD2, 0xBB, + }); + + public static readonly ImmutableArray PublicKey_b03f5f7f11d50a3a = ImmutableArray.Create(new byte[] + { + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x07, 0xd1, 0xfa, 0x57, 0xc4, 0xae, 0xd9, 0xf0, 0xa3, 0x2e, 0x84, 0xaa, 0x0f, 0xae, 0xfd, 0x0d, + 0xe9, 0xe8, 0xfd, 0x6a, 0xec, 0x8f, 0x87, 0xfb, 0x03, 0x76, 0x6c, 0x83, 0x4c, 0x99, 0x92, 0x1e, + 0xb2, 0x3b, 0xe7, 0x9a, 0xd9, 0xd5, 0xdc, 0xc1, 0xdd, 0x9a, 0xd2, 0x36, 0x13, 0x21, 0x02, 0x90, + 0x0b, 0x72, 0x3c, 0xf9, 0x80, 0x95, 0x7f, 0xc4, 0xe1, 0x77, 0x10, 0x8f, 0xc6, 0x07, 0x77, 0x4f, + 0x29, 0xe8, 0x32, 0x0e, 0x92, 0xea, 0x05, 0xec, 0xe4, 0xe8, 0x21, 0xc0, 0xa5, 0xef, 0xe8, 0xf1, + 0x64, 0x5c, 0x4c, 0x0c, 0x93, 0xc1, 0xab, 0x99, 0x28, 0x5d, 0x62, 0x2c, 0xaa, 0x65, 0x2c, 0x1d, + 0xfa, 0xd6, 0x3d, 0x74, 0x5d, 0x6f, 0x2d, 0xe5, 0xf1, 0x7e, 0x5e, 0xaf, 0x0f, 0xc4, 0x96, 0x3d, + 0x26, 0x1c, 0x8a, 0x12, 0x43, 0x65, 0x18, 0x20, 0x6d, 0xc0, 0x93, 0x34, 0x4d, 0x5a, 0xd2, 0x93, + }); + } +} \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/ReferenceManagerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/ReferenceManagerTests.vb index 260be627520..544ad868d56 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/ReferenceManagerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/ReferenceManagerTests.vb @@ -11,10 +11,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Inherits BasicTestBase Private Shared ReadOnly s_signedDll As VisualBasicCompilationOptions = - New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, - optimizationLevel:=OptimizationLevel.Release, - cryptoKeyFile:=SigningTestHelpers.KeyPairFile, - strongNameProvider:=New SigningTestHelpers.VirtualizedStrongNameProvider(ImmutableArray.Create(Of String)())) + TestOptions.ReleaseDll.WithCryptoPublicKey(TestResources.TestKeys.PublicKey_ce65828c82a341f2) -- GitLab