diff --git a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs index b9e1957c8e6a73d8950a72efed211d71743070e3..42aa584c5a0e3bf865f7438b2fbfb19f38e50db6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs @@ -8,11 +8,11 @@ using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { - using Symbols.Retargeting; using MetadataOrDiagnostic = System.Object; public partial class CSharpCompilation @@ -307,7 +307,6 @@ private void InitializeAssemblyReuseData(AssemblySymbol assemblySymbol, Immutabl private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) { var resolutionDiagnostics = DiagnosticBag.GetInstance(); - var implicitlyResolvedReferences = ArrayBuilder.GetInstance(); try { @@ -327,29 +326,36 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) resolutionDiagnostics); var assemblyBeingBuiltData = new AssemblyDataForAssemblyBeingBuilt(new AssemblyIdentity(name: SimpleAssemblyName), referencedAssemblies, modules); - var allAssemblyData = referencedAssemblies.Insert(0, assemblyBeingBuiltData); + var explicitAssemblyData = referencedAssemblies.Insert(0, assemblyBeingBuiltData); // Let's bind all the references and resolve missing one (if resolver is available) bool hasCircularReference; int corLibraryIndex; + ImmutableArray implicitlyResolvedReferences; + ImmutableArray implicitlyResolvedReferenceMap; + ImmutableArray allAssemblyData; + BoundInputAssembly[] bindingResult = Bind( - ref allAssemblyData, + explicitAssemblyData, compilation.Options.MetadataReferenceResolver, compilation.Options.MetadataImportOptions, - implicitlyResolvedReferences, + out allAssemblyData, + out implicitlyResolvedReferences, + out implicitlyResolvedReferenceMap, resolutionDiagnostics, out hasCircularReference, out corLibraryIndex); Debug.Assert(bindingResult.Length == allAssemblyData.Length); + references = references.AddRange(implicitlyResolvedReferences); + referenceMap = referenceMap.AddRange(implicitlyResolvedReferenceMap); + Dictionary referencedAssembliesMap, referencedModulesMap; ImmutableArray> aliasesOfReferencedAssemblies; BuildReferencedAssembliesAndModulesMaps( references, referenceMap, - implicitlyResolvedReferences, - referencedAssemblies.Length, modules.Length, out referencedAssembliesMap, out referencedModulesMap, @@ -394,12 +400,15 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) // This should be done after we created/found all AssemblySymbols Dictionary missingAssemblies = null; + // -1 for assembly being built: + int totalReferencedAssemblyCount = allAssemblyData.Length - 1; + // Setup bound references for newly created SourceAssemblySymbol ImmutableArray> moduleReferences; SetupReferencesForSourceAssembly( - allAssemblyData, assemblySymbol, modules, + totalReferencedAssemblyCount, bindingResult, ref missingAssemblies, out moduleReferences); @@ -461,7 +470,6 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) finally { resolutionDiagnostics.Free(); - implicitlyResolvedReferences.Free(); } } @@ -678,9 +686,9 @@ private static void UpdateSymbolCacheNoLock(List newSymbols, ImmutableArray } private static void SetupReferencesForSourceAssembly( - ImmutableArray allAssemblyData, SourceAssemblySymbol sourceAssembly, ImmutableArray modules, + int totalReferencedAssemblyCount, BoundInputAssembly[] bindingResult, ref Dictionary missingAssemblies, out ImmutableArray> moduleReferences) @@ -693,8 +701,7 @@ private static void UpdateSymbolCacheNoLock(List newSymbols, ImmutableArray int refsUsed = 0; for (int moduleIndex = 0; moduleIndex < moduleSymbols.Length; moduleIndex++) { - // -1 for assembly being built - int refsCount = (moduleIndex == 0) ? (allAssemblyData.Length - 1) : modules[moduleIndex - 1].ReferencedAssemblies.Length; + int refsCount = (moduleIndex == 0) ? totalReferencedAssemblyCount : modules[moduleIndex - 1].ReferencedAssemblies.Length; var identities = new AssemblyIdentity[refsCount]; var symbols = new AssemblySymbol[refsCount]; @@ -731,7 +738,7 @@ private static void UpdateSymbolCacheNoLock(List newSymbols, ImmutableArray refsUsed += refsCount; } - moduleReferences = moduleReferencesBuilder.AsImmutableOrEmpty(); + moduleReferences = moduleReferencesBuilder.ToImmutableOrEmptyAndFree(); } private static AssemblySymbol GetAssemblyDefinitionSymbol( diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs index 1ded5e36d5c841cbe79a6c6a4227155dc6a21817..afd6fb05db33fbff080da2190a968d1f1fa18df3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/ReferenceManagerTests.cs @@ -2178,7 +2178,7 @@ public void AsymmetricUnification() private class TestMissingMetadataReferenceResolver : MetadataReferenceResolver { private readonly Dictionary _map; - public readonly List ResolvedIdentities = new List(); + public readonly List ResolutionAttempts = new List(); public TestMissingMetadataReferenceResolver(Dictionary map) { @@ -2187,7 +2187,7 @@ public TestMissingMetadataReferenceResolver(Dictionary "b, V1" resolved to "b, V3" with alias X + // - d -> "b, V2" resolved to "b, V3" with alias Y + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + + var aRef = CreateCompilationWithMscorlib("public class A : B { }", new[] { b1Ref }, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib("public class D : B { }", new[] { b2Ref }, assemblyName: "D").EmitToImageReference(); + + var b3RefX = b3Ref.WithAliases(ImmutableArray.Create("X")); + var b3RefY = b3Ref.WithAliases(ImmutableArray.Create("Y")); + + var c = CreateCompilationWithMscorlib(@" +extern alias X; +extern alias Y; + +public class C : A +{ + X::B F() => new Y::B(); +} +", new[] { aRef, dRef }, + TestOptions.ReleaseDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 1.0.0.0", b3RefX }, + { "B, 2.0.0.0", b3RefY }, + }))); + + c.VerifyEmitDiagnostics( + // (5,18): warning CS1701: Assuming assembly reference + // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity + // 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments( + "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + + Assert.Equal("B", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b3RefY)).Name); + Assert.Null(c.GetAssemblyOrModuleSymbol(b3RefX)); + } + + [Fact] + public void MissingAssemblyResolution_WeakIdentities1() + { + // c - a -> "b,v1,PKT=null" + // - d -> "b,v2,PKT=null" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b4Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""4.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + + var aRef = CreateCompilationWithMscorlib(@"public interface A : B { }", new[] { b1Ref }, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib(@"public interface D : B { }", new[] { b2Ref }, assemblyName: "D").EmitToImageReference(); + + var c = CreateCompilationWithMscorlib(@"public interface C : A, D { }", new[] { aRef, dRef }, + TestOptions.ReleaseDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 1.0.0.0", b1Ref }, + { "B, 2.0.0.0", b2Ref }, + }))); + + c.VerifyEmitDiagnostics( + // error CS1704: An assembly with the same simple name 'B' has already been imported. Try removing one of the references (e.g. 'B') or sign them to enable side-by-side. + Diagnostic(ErrorCode.ERR_DuplicateImportSimple).WithArguments("B", "B")); + } + + [Fact] + public void MissingAssemblyResolution_WeakIdentities2() + { + // c - a -> "b,v1,PKT=null" + // - d -> "b,v2,PKT=null" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + var b4Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""4.0.0.0"")] public interface B { }", assemblyName: "B").EmitToImageReference(); + + var aRef = CreateCompilationWithMscorlib(@"public interface A : B { }", new[] { b1Ref }, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib(@"public interface D : B { }", new[] { b2Ref }, assemblyName: "D").EmitToImageReference(); + + var c = CreateCompilationWithMscorlib(@"public interface C : A, D { }", new[] { aRef, dRef }, + TestOptions.ReleaseDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 1.0.0.0", b3Ref }, + { "B, 2.0.0.0", b4Ref }, + }))); + + c.VerifyEmitDiagnostics( + // error CS1704: An assembly with the same simple name 'B' has already been imported. Try removing one of the references (e.g. 'B') or sign them to enable side-by-side. + Diagnostic(ErrorCode.ERR_DuplicateImportSimple).WithArguments("B", "B")); + } + [Fact] public void MissingAssemblyResolution_None() { @@ -2256,7 +2348,53 @@ public void MissingAssemblyResolution_None() c.VerifyDiagnostics(); - Assert.Equal(0, resolver.ResolvedIdentities.Count); + Assert.Equal(0, resolver.ResolutionAttempts.Count); + } + + [Fact] + public void MissingAssemblyResolution_ActualMissing() + { + // c - a -> d + var dRef = CreateCompilationWithMscorlib("public interface D { }", assemblyName: "D").EmitToImageReference(); + var aRef = CreateCompilationWithMscorlib("public interface A : D { }", new[] { dRef }, assemblyName: "A").ToMetadataReference(); + + var resolver = new TestMissingMetadataReferenceResolver(new Dictionary()); + + var c = CreateCompilationWithMscorlib("public interface C : A { }", new[] { aRef }, + TestOptions.ReleaseDll.WithMetadataReferenceResolver(resolver)); + + c.VerifyDiagnostics( + // (1,18): error CS0012: The type 'D' is defined in an assembly that is not referenced. You must add a reference to assembly 'D, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef, "C").WithArguments("D", "D, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")); + + Assert.Equal(1, resolver.ResolutionAttempts.Count); + } + + /// + /// Ignore assemblies returned by the resolver that don't match the reference identity. + /// + [Fact] + public void MissingAssemblyResolution_MissingDueToResolutionMismatch() + { + // c - a -> b + var bRef = CreateCompilationWithMscorlib("public interface D { }", assemblyName: "B").EmitToImageReference(); + var aRef = CreateCompilationWithMscorlib("public interface A : D { }", new[] { bRef }, assemblyName: "A").ToMetadataReference(); + + var eRef = CreateCompilationWithMscorlib("public interface E { }", assemblyName: "E").ToMetadataReference(); + + var resolver = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 1.0.0.0", eRef }, + }); + + var c = CreateCompilationWithMscorlib(@"public interface C : A { }", new[] { aRef }, + TestOptions.ReleaseDll.WithMetadataReferenceResolver(resolver)); + + c.VerifyDiagnostics( + // (1,18): error CS0012: The type 'D' is defined in an assembly that is not referenced. You must add a reference to assembly 'B, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef, "C").WithArguments("D", "B, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")); + + Assert.Equal(1, resolver.ResolutionAttempts.Count); } [Fact] @@ -2279,7 +2417,7 @@ public void MissingAssemblyResolution_Multiple() c.VerifyEmitDiagnostics(); Assert.Equal("D", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(dRef)).Name); - Assert.Equal(1, resolver.ResolvedIdentities.Count); + Assert.Equal(1, resolver.ResolutionAttempts.Count); } [Fact] @@ -2312,8 +2450,11 @@ public void MissingAssemblyResolution_Modules() Assert.Equal("D", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(dRef)).Name); } + /// + /// Don't try to resolve AssemblyRefs that already match explicitly specified definition. + /// [Fact] - public void MissingAssemblyResolution_BetterVersion_Higher_vs_Exact() + public void MissingAssemblyResolution_BindingToForExplicitReference1() { // c - a -> "b,v1" // - "b,v3" @@ -2324,160 +2465,244 @@ public void MissingAssemblyResolution_BetterVersion_Higher_vs_Exact() var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + var resolver = new TestMissingMetadataReferenceResolver(new Dictionary + { + // the compiler asked for v1, but we have v2 + { "B, 1.0.0.0", b2Ref } + }); + var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b3Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - // the compiler asked for v1, but we have v2 - { "B, 1.0.0.0", b2Ref } - }))); + s_signedDll.WithMetadataReferenceResolver(resolver)); c.VerifyEmitDiagnostics( // (1,18): warning CS1701: Assuming assembly reference // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity - // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + // 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments( "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", - "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + + Assert.Equal(0, resolver.ResolutionAttempts.Count); Assert.Equal( - "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)).Identity.GetDisplayName()); + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b3Ref)).Identity.GetDisplayName()); + + Assert.Null((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)); } + /// + /// Don't try to resolve AssemblyRefs that already match explicitly specified definition. + /// [Fact] - public void MissingAssemblyResolution_BetterVersion_Higher_vs_BetterHigher() + public void MissingAssemblyResolution_BindingToExplicitReference_WorseVersion() { - // c - a -> "b,v1" - // - "b,v3" - // - var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + // c - a -> d -> "b,v2" + // e -> "b,v1" + // - "b,v1" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface D : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "D").EmitToImageReference(); + var eRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface E : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "E").EmitToImageReference(); - var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b3Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - { "B, 1.0.0.0", b2Ref } - }))); + var resolverA = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 2.0.0.0", b2Ref }, + { "B, 1.0.0.0", b1Ref }, + }); + + var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface A : D, E { }", new[] { dRef, eRef }, + s_signedDll.WithMetadataReferenceResolver(resolverA), assemblyName: "A").EmitToImageReference(); + + Assert.Equal(2, resolverA.ResolutionAttempts.Count); + + var resolverC = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "D, 1.0.0.0", dRef }, + { "E, 1.0.0.0", eRef }, + }); + + var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b1Ref }, + s_signedDll.WithMetadataReferenceResolver(resolverC)); c.VerifyEmitDiagnostics( - // (1,18): warning CS1701: Assuming assembly reference - // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity - // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments( - "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", - "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + // (1,14): error CS1705: Assembly + // 'A' with identity 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' uses + // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly + // 'B' with identity 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' + Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "C").WithArguments( + "A", "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B", "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2"), + + // (1,14): error CS1705: Assembly + // 'D' with identity 'D, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' uses + // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly + // 'B' with identity 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' + Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "C").WithArguments( + "D", "D, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B", "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2")); + + Assert.Equal(2, resolverC.ResolutionAttempts.Count); } + /// + /// Don't try to resolve AssemblyRefs that already match explicitly specified definition. + /// [Fact] - public void MissingAssemblyResolution_BetterVersion_Higher_vs_WorseHigher() + public void MissingAssemblyResolution_BindingToExplicitReference_BetterVersion() { - // c - a -> "b,v1" - // - "b,v2" - // - var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + // c - a -> d -> "b,v2" + // e -> "b,v1" + // - "b,v2" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface D : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "D").EmitToImageReference(); + var eRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface E : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "E").EmitToImageReference(); + + var resolverA = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "B, 2.0.0.0", b2Ref }, + { "B, 1.0.0.0", b1Ref }, + }); + + var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface A : D, E { }", new[] { dRef, eRef }, + s_signedDll.WithMetadataReferenceResolver(resolverA), assemblyName: "A").EmitToImageReference(); + + Assert.Equal(2, resolverA.ResolutionAttempts.Count); + + var resolverC = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "D, 1.0.0.0", dRef }, + { "E, 1.0.0.0", eRef }, + }); var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b2Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - { "B, 1.0.0.0", b3Ref } - }))); + s_signedDll.WithMetadataReferenceResolver(resolverC)); c.VerifyEmitDiagnostics( - // (1,18): warning CS1701: Assuming assembly reference - // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity - // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments( + // (1,14): warning CS1701: Assuming assembly reference + // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by + // 'A' matches identity 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "C").WithArguments( "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"), + + // (1,14): warning CS1701: Assuming assembly reference + // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'E' matches identity + // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "C").WithArguments( + "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "E", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + + Assert.Equal(2, resolverC.ResolutionAttempts.Count); } [Fact] - public void MissingAssemblyResolution_BetterVersion_Lower_vs_Exact1() + public void MissingAssemblyResolution_BindingToImplicitReference1() { - // c - a -> "b,v2" - // - "b,v1" - // - var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + // c - a -> d -> "b,v2" + // e -> "b,v1" + // "b,v1" + // "b,v2" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + var dRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface D : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "D").EmitToImageReference(); + var eRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface E : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "E").EmitToImageReference(); - var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b1Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - { "B, 2.0.0.0", b2Ref } - }))); + var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface A : D, E { }", new[] { dRef, eRef, b1Ref, b2Ref }, + s_signedDll, assemblyName: "A").EmitToImageReference(); - c.VerifyEmitDiagnostics(); + var resolverC = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "D, 1.0.0.0", dRef }, + { "E, 1.0.0.0", eRef }, + { "B, 1.0.0.0", b1Ref }, + { "B, 2.0.0.0", b2Ref }, + }); - Assert.Equal( - "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)).Identity.GetDisplayName()); - } + var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef }, + s_signedDll.WithMetadataReferenceResolver(resolverC)); - [Fact] - public void MissingAssemblyResolution_BetterVersion_Lower_vs_WorseLower() - { - // c - a -> "b,v3" - // - "b,v2" - // - var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + c.VerifyEmitDiagnostics(); - var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b3Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + Assert.Equal(4, resolverC.ResolutionAttempts.Count); - var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b2Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - { "B, 3.0.0.0", b1Ref } - }))); + Assert.Equal( + "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b1Ref)).Identity.GetDisplayName()); - c.VerifyDiagnostics( - // (1,18): error CS1705: Assembly - // 'A' with identity 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' - // uses 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly - // 'B' with identity 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' - Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "A").WithArguments( - "A", "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - "B", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2")); + Assert.Equal( + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)).Identity.GetDisplayName()); } [Fact] - public void MissingAssemblyResolution_BetterVersion_Lower_vs_BetterLower() + public void MissingAssemblyResolution_BindingToImplicitReference2() { - // c - a -> "b,v3" - // - "b,v1" - // - var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); - - var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b3Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference(); + // c - a -> d -> "b,v2" + // e -> "b,v1" + // "b,v1" + // "b,v2" + var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + var b4Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""4.0.0.0"")] public interface B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference(); + + var dRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface D : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "D").EmitToImageReference(); + var eRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface E : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "E").EmitToImageReference(); + + var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public interface A : D, E { }", new[] { dRef, eRef, b1Ref, b2Ref }, + s_signedDll, assemblyName: "A").EmitToImageReference(); + + var resolverC = new TestMissingMetadataReferenceResolver(new Dictionary + { + { "D, 1.0.0.0", dRef }, + { "E, 1.0.0.0", eRef }, + { "B, 1.0.0.0", b3Ref }, + { "B, 2.0.0.0", b4Ref }, + }); - var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b1Ref }, - s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary - { - { "B, 3.0.0.0", b2Ref } - }))); + var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef }, + s_signedDll.WithMetadataReferenceResolver(resolverC)); - c.VerifyDiagnostics( - // (1,18): error CS1705: Assembly - // 'A' with identity 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' - // uses 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly - // 'B' with identity 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' - Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "A").WithArguments( - "A", "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", - "B", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2")); + c.VerifyEmitDiagnostics( + // (1,14): warning CS1701: Assuming assembly reference + // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity + // 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "C").WithArguments( + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A", + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"), + + // (1,14): warning CS1701: Assuming assembly reference + // 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'D' matches identity + // 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "C").WithArguments( + "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "D", + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"), + + // (1,14): warning CS1701: Assuming assembly reference + // 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'E' matches identity + // 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy + Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "C").WithArguments( + "B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "E", + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B")); + + Assert.Equal(4, resolverC.ResolutionAttempts.Count); + + AssertEx.Equal(new[] + { + "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "D, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B, Version=4.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "E, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", + "B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2" + }, c.GetBoundReferenceManager().ReferencedAssemblies.Select(a => a.Identity.GetDisplayName())); } } } diff --git a/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs b/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs index e2568998d4f09dc8322fb312a5c0dd5cdb69168f..c2d9da62415b19576cf2e1263bd1789a93ca8896 100644 --- a/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs @@ -139,5 +139,10 @@ public static T Peek(this ArrayBuilder builder) { return builder[builder.Count - 1]; } + + public static ImmutableArray ToImmutableOrEmptyAndFree(this ArrayBuilder builderOpt) + { + return builderOpt?.ToImmutableAndFree() ?? ImmutableArray.Empty; + } } } diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Binding.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Binding.cs index e9dee198a5f1ba3e7ea685df58a4b3f0457b703b..3e7e0602f289bc7a0f1a9bf3d67716ba4ebbe6d3 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Binding.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Binding.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; @@ -21,10 +22,9 @@ internal partial class CommonReferenceManager /// build executable for the assembly being built. /// /// - /// - /// Input: An array of objects describing assemblies, for which this method should - /// resolve references and find suitable AssemblySymbols. - /// Output: Updated array with resolved implicitly referenced assemblies appended. + /// + /// An array of objects describing assemblies, for which this method should + /// resolve references and find suitable AssemblySymbols. The first slot contains the assembly being built. /// /// /// Reference resolver used to look up missing assemblies. @@ -32,8 +32,14 @@ internal partial class CommonReferenceManager /// /// Import options applied to implicitly resolved references. /// + /// + /// Updated array with resolved implicitly referenced assemblies appended. + /// /// - /// Implicitly resolved references, corresponding to appended to the array. + /// Implicitly resolved references. + /// + /// + /// Maps index to all references (explicit + implicit) to an index of a resolved assembly in all assemblies (explicit + implicit). /// /// /// Any diagnostics reported while resolving missing assemblies. @@ -58,40 +64,51 @@ internal partial class CommonReferenceManager /// against provided set of assembly definitions. Essentially, this is an array returned by /// method. /// - internal BoundInputAssembly[] Bind( - ref ImmutableArray assemblies, + protected BoundInputAssembly[] Bind( + ImmutableArray explicitAssemblies, MetadataReferenceResolver resolverOpt, MetadataImportOptions importOptions, - ArrayBuilder implicitlyResolvedReferences, + out ImmutableArray allAssemblies, + out ImmutableArray implicitlyResolvedReferences, + out ImmutableArray implicitlyResolvedReferenceMap, DiagnosticBag resolutionDiagnostics, out bool hasCircularReference, out int corLibraryIndex) { - Debug.Assert(!assemblies.IsDefault); + Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt); var referenceBindings = ArrayBuilder.GetInstance(); try { // Based on assembly identity, for each assembly, // bind its references against the other assemblies we have. - for (int i = 0; i < assemblies.Length; i++) + for (int i = 0; i < explicitAssemblies.Length; i++) { - referenceBindings.Add(assemblies[i].BindAssemblyReferences(assemblies, IdentityComparer)); + referenceBindings.Add(explicitAssemblies[i].BindAssemblyReferences(explicitAssemblies, IdentityComparer)); } if (resolverOpt?.ResolveMissingAssemblies == true) { - ResolveAndBindMissingAssemblies(assemblies, referenceBindings, implicitlyResolvedReferences, resolverOpt, importOptions, resolutionDiagnostics, out assemblies); + ResolveAndBindMissingAssemblies(explicitAssemblies, resolverOpt, importOptions, referenceBindings, out allAssemblies, out implicitlyResolvedReferences, out implicitlyResolvedReferenceMap, resolutionDiagnostics); + } + else + { + allAssemblies = explicitAssemblies; + implicitlyResolvedReferences = ImmutableArray.Empty; + implicitlyResolvedReferenceMap = ImmutableArray.Empty; } - hasCircularReference = CheckCircularReference(referenceBindings); + // implicitly resolved missing assemblies were added to both referenceBindings and assemblies: + Debug.Assert(referenceBindings.Count == allAssemblies.Length); - corLibraryIndex = IndexOfCorLibrary(assemblies); + hasCircularReference = CheckCircularReference(referenceBindings); + corLibraryIndex = IndexOfCorLibrary(allAssemblies); // For each assembly, locate AssemblySymbol with similar reference resolution // What does similar mean? // Similar means: - // 1) The same references are resolved against the assemblies that we are given. + // 1) The same references are resolved against the assemblies that we are given + // (or were found duiring implicit assembly resolution). // 2) The same assembly is used as the COR library. var boundInputs = new BoundInputAssembly[referenceBindings.Count]; @@ -100,7 +117,7 @@ internal partial class CommonReferenceManager boundInputs[i].ReferenceBinding = referenceBindings[i]; } - var candidateInputAssemblySymbols = new TAssemblySymbol[assemblies.Length]; + var candidateInputAssemblySymbols = new TAssemblySymbol[allAssemblies.Length]; // If any assembly from assemblies array refers back to assemblyBeingBuilt, // we know that we cannot reuse symbols for any assemblies containing NoPia @@ -109,7 +126,7 @@ internal partial class CommonReferenceManager if (!hasCircularReference) { // Deal with assemblies containing NoPia local types. - if (ReuseAssemblySymbolsWithNoPiaLocalTypes(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex)) + if (ReuseAssemblySymbolsWithNoPiaLocalTypes(boundInputs, candidateInputAssemblySymbols, allAssemblies, corLibraryIndex)) { return boundInputs; } @@ -118,7 +135,7 @@ internal partial class CommonReferenceManager // NoPia shortcut either didn't apply or failed, go through general process // of matching candidates. - ReuseAssemblySymbols(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex); + ReuseAssemblySymbols(boundInputs, candidateInputAssemblySymbols, allAssemblies, corLibraryIndex); return boundInputs; } @@ -130,21 +147,29 @@ internal partial class CommonReferenceManager private void ResolveAndBindMissingAssemblies( ImmutableArray explicitAssemblies, - ArrayBuilder referenceBindings, - ArrayBuilder resolvedReferences, MetadataReferenceResolver resolver, MetadataImportOptions importOptions, - DiagnosticBag resolutionDiagnostics, - out ImmutableArray allAssemblies) + [In, Out] ArrayBuilder referenceBindings, + out ImmutableArray allAssemblies, + out ImmutableArray metadataReferences, + out ImmutableArray resolvedReferences, + DiagnosticBag resolutionDiagnostics) { + Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt); + var implicitAssemblies = ArrayBuilder.GetInstance(); // tracks identities we already asked the resolver to resolve: - var resolvedMissingIdentities = PooledHashSet.GetInstance(); + var requestedIdentities = PooledHashSet.GetInstance(); - // reference bindings of implicit assemblies, use to calculate a fixed point: + // reference bindings of implicit assemblies, used to calculate a fixed point: var referenceBindingsToProcess = ArrayBuilder.GetInstance(); + var metadataReferencesBuilder = ArrayBuilder.GetInstance(); + + Dictionary> lazyResolvedReferencesBySimpleName = null; + Dictionary> lazyAliasMap = null; + try { // collect all missing identities, resolve the assemblies and bind their references against explicit definitions: @@ -154,9 +179,13 @@ internal partial class CommonReferenceManager { foreach (var binding in referenceBindingsToProcess.Pop()) { - // try resolve a reference if eitehr the binding failed or didn't find an exact match: - if (binding.IsBound && binding.VersionDifference == 0 || - resolvedMissingIdentities.Contains(binding.ReferenceIdentity)) + // only attempt to resolve unbound references (regardless of version difference of the bound ones) + if (binding.IsBound) + { + continue; + } + + if (!requestedIdentities.Add(binding.ReferenceIdentity)) { continue; } @@ -173,8 +202,19 @@ internal partial class CommonReferenceManager continue; } - resolvedReferences.Add(peReference); - resolvedMissingIdentities.Add(binding.ReferenceIdentity); + // The resolver may return different version than we asked for, so it may also happen that + // it returns the same identity for two different input identities (e.g. if a higher version + // of an assembly is available than what the assemblies reference: "A, v1" -> "A, v3" and "A, v2" -> "A, v3"). + // If such case occurs merge the properties (aliases) of the resulting references in the same way we do + + var existingReference = TryAddAssembly(data.Identity, peReference, resolutionDiagnostics, Location.None, ref lazyResolvedReferencesBySimpleName); + if (existingReference != null) + { + MergeReferenceProperties(existingReference, peReference, resolutionDiagnostics, ref lazyAliasMap); + continue; + } + + metadataReferencesBuilder.Add(peReference); implicitAssemblies.Add(data); var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer); @@ -183,72 +223,102 @@ internal partial class CommonReferenceManager } } + if (implicitAssemblies.Count == 0) + { + Debug.Assert(lazyAliasMap == null); + Debug.Assert(lazyResolvedReferencesBySimpleName == null); + + resolvedReferences = ImmutableArray.Empty; + metadataReferences = ImmutableArray.Empty; + allAssemblies = explicitAssemblies; + return; + } + + // Rebind assembly references that were initially missing. All bindings established above + // are against explicitly specified references. + // NB: includes the assembly being built: int explicitAssemblyCount = explicitAssemblies.Length; allAssemblies = explicitAssemblies.AddRange(implicitAssemblies); - // Rebind assembly referenced that were initially missing or were bound to a definitions with a different version - // (with more assembly identities available we may have a better match now). - - if (implicitAssemblies.Count > 0) + for (int bindingsIndex = 0; bindingsIndex < referenceBindings.Count; bindingsIndex++) { - foreach (var referenceBinding in referenceBindings) + var referenceBinding = referenceBindings[bindingsIndex]; + + for (int i = 0; i < referenceBinding.Length; i++) { - for (int i = 0; i < referenceBinding.Length; i++) - { - var binding = referenceBinding[i]; + var binding = referenceBinding[i]; - if (!binding.IsBound || binding.VersionDifference != 0) - { - // We only need to resolve against implicitly resolved assemblies, - // since we already resolved against explicitly specified ones. - var newBinding = ResolveReferencedAssembly( - binding.ReferenceIdentity, - allAssemblies, - explicitAssemblyCount, - IdentityComparer); - - if (IsBetterVersionMatch(binding.ReferenceIdentity, allAssemblies, binding, newBinding)) - { - referenceBinding[i] = newBinding; - } - } + // We don't rebind references bound to a non-matching version of a reference that was explicitly + // specified, even if we have a better version now. + if (binding.IsBound) + { + continue; } - } - UpdateBindingsOfAssemblyBeingBuilt(referenceBindings, explicitAssemblies, implicitAssemblies); + // We only need to resolve against implicitly resolved assemblies, + // since we already resolved against explicitly specified ones. + referenceBinding[i] = ResolveReferencedAssembly( + binding.ReferenceIdentity, + allAssemblies, + explicitAssemblyCount, + IdentityComparer); + } } + + UpdateBindingsOfAssemblyBeingBuilt(referenceBindings, explicitAssemblyCount, implicitAssemblies); + + metadataReferences = metadataReferencesBuilder.ToImmutable(); + resolvedReferences = ToResolvedAssemblyReferences(metadataReferences, lazyAliasMap, explicitAssemblyCount); } finally { implicitAssemblies.Free(); - resolvedMissingIdentities.Free(); + requestedIdentities.Free(); referenceBindingsToProcess.Free(); + metadataReferencesBuilder.Free(); } } - private static void UpdateBindingsOfAssemblyBeingBuilt(ArrayBuilder referenceBindings, ImmutableArray explicitAssemblies, ArrayBuilder implicitAssemblies) + private static ImmutableArray ToResolvedAssemblyReferences( + ImmutableArray references, + Dictionary> aliasMapOpt, + int explicitAssemblyCount) { + var result = ArrayBuilder.GetInstance(references.Length); + for (int i = 0; i < references.Length; i++) + { + // -1 for assembly being built + result.Add(new ResolvedReference(explicitAssemblyCount - 1 + i, MetadataImageKind.Assembly, GetAndFreeAliases(references[i], aliasMapOpt))); + } + + return result.ToImmutableAndFree(); + } + + private static void UpdateBindingsOfAssemblyBeingBuilt(ArrayBuilder referenceBindings, int explicitAssemblyCount, ArrayBuilder implicitAssemblies) + { + var referenceBindingsOfAssemblyBeingBuilt = referenceBindings[0]; + // add implicitly resolved assemblies to the bindings of the assembly being built: - var bindingsOfAssemblyBeingBuilt = ArrayBuilder.GetInstance(referenceBindings[0].Length + implicitAssemblies.Count); + var bindingsOfAssemblyBeingBuilt = ArrayBuilder.GetInstance(referenceBindingsOfAssemblyBeingBuilt.Length + implicitAssemblies.Count); // add bindings for explicitly specified assemblies (-1 for the assembly being built): - bindingsOfAssemblyBeingBuilt.AddRange(referenceBindings[0], explicitAssemblies.Length - 1); + bindingsOfAssemblyBeingBuilt.AddRange(referenceBindingsOfAssemblyBeingBuilt, explicitAssemblyCount - 1); // add bindings for implicitly resolved assemblies: for (int i = 0; i < implicitAssemblies.Count; i++) { - bindingsOfAssemblyBeingBuilt.Add(new AssemblyReferenceBinding(implicitAssemblies[i].Identity, explicitAssemblies.Length + i)); + bindingsOfAssemblyBeingBuilt.Add(new AssemblyReferenceBinding(implicitAssemblies[i].Identity, explicitAssemblyCount + i)); } // add bindings for assemblies referenced by modules added to the compilation: - bindingsOfAssemblyBeingBuilt.AddRange(referenceBindings[0], explicitAssemblies.Length - 1, referenceBindings[0].Length - explicitAssemblies.Length + 1); + bindingsOfAssemblyBeingBuilt.AddRange(referenceBindingsOfAssemblyBeingBuilt, explicitAssemblyCount - 1, referenceBindingsOfAssemblyBeingBuilt.Length - explicitAssemblyCount + 1); referenceBindings[0] = bindingsOfAssemblyBeingBuilt.ToArrayAndFree(); } internal AssemblyData ResolveMissingAssembly( - AssemblyIdentity identity, + AssemblyIdentity referenceIdentity, PortableExecutableReference peReference, MetadataImportOptions importOptions, DiagnosticBag diagnostics) @@ -268,8 +338,16 @@ private static void UpdateBindingsOfAssemblyBeingBuilt(ArrayBuilder" : (_kind == MetadataImageKind.Assembly ? "A[" : "M[") + Index + "]: aliases=" + _aliases.ToString(); + return IsSkipped ? "" : $"{(_kind == MetadataImageKind.Assembly ? "A" : "M")}[{Index}]: aliases='{string.Join("','", _aliases)}'"; } } @@ -344,7 +344,7 @@ public ReferencedAssemblyIdentity(AssemblyIdentity identity, MetadataReference m : ((object)modulesBuilder == null ? 0 : modulesBuilder.Count); int reversedIndex = count - 1 - referenceMap[i].Index; - referenceMap[i] = new ResolvedReference(reversedIndex, referenceMap[i].Kind, GetAliases(references[i], aliasMap)); + referenceMap[i] = new ResolvedReference(reversedIndex, referenceMap[i].Kind, GetAndFreeAliases(references[i], aliasMap)); } } @@ -371,10 +371,10 @@ public ReferencedAssemblyIdentity(AssemblyIdentity identity, MetadataReference m return ImmutableArray.CreateRange(referenceMap); } - private static ImmutableArray GetAliases(MetadataReference reference, Dictionary> aliasMap) + private static ImmutableArray GetAndFreeAliases(MetadataReference reference, Dictionary> aliasMapOpt) { ArrayBuilder aliases; - if (aliasMap != null && aliasMap.TryGetValue(reference, out aliases)) + if (aliasMapOpt != null && aliasMapOpt.TryGetValue(reference, out aliases)) { return aliases.ToImmutableAndFree(); } @@ -576,7 +576,7 @@ private static void AddModule(PEModule module, int referenceIndex, ResolvedRefer // Returns null if an assembly of an equivalent identity has not been added previously, otherwise returns the reference that added it. // - Both assembly names are strong (have keys) and are either equal or FX unified // - Both assembly names are weak (no keys) and have the same simple name. - private MetadataReference TryAddAssembly(AssemblyIdentity identity, MetadataReference boundReference, DiagnosticBag diagnostics, Location location, + private MetadataReference TryAddAssembly(AssemblyIdentity identity, MetadataReference boundReference, DiagnosticBag diagnosticsOpt, Location location, ref Dictionary> referencesBySimpleName) { if (referencesBySimpleName == null) @@ -643,7 +643,7 @@ private static void AddModule(PEModule module, int referenceIndex, ResolvedRefer // BREAKING CHANGE in VB: we report an error for both languages // Multiple assemblies with equivalent identity have been imported: '{0}' and '{1}'. Remove one of the duplicate references. - MessageProvider.ReportDuplicateMetadataReferenceStrong(diagnostics, location, boundReference, identity, equivalent.MetadataReference, equivalent.Identity); + MessageProvider.ReportDuplicateMetadataReferenceStrong(diagnosticsOpt, location, boundReference, identity, equivalent.MetadataReference, equivalent.Identity); } // If the versions match exactly we ignore duplicates w/o reporting errors while // Dev12 C# reports: @@ -664,7 +664,7 @@ private static void AddModule(PEModule module, int referenceIndex, ResolvedRefer if (identity != equivalent.Identity) { - MessageProvider.ReportDuplicateMetadataReferenceWeak(diagnostics, location, boundReference, identity, equivalent.MetadataReference, equivalent.Identity); + MessageProvider.ReportDuplicateMetadataReferenceWeak(diagnosticsOpt, location, boundReference, identity, equivalent.MetadataReference, equivalent.Identity); } } @@ -840,6 +840,9 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe } continue; + + default: + throw ExceptionUtilities.Unreachable; } } @@ -909,28 +912,5 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe return new AssemblyReferenceBinding(reference); } - - private static bool IsBetterVersionMatch(AssemblyIdentity reference, ImmutableArray definitions, AssemblyReferenceBinding previousBinding, AssemblyReferenceBinding newBinding) - { - Debug.Assert(newBinding.IsBound); - - // Previous binding failed or new binding is an exact match - if (!previousBinding.IsBound || newBinding.VersionDifference == 0) - { - return true; - } - - if (previousBinding.VersionDifference != newBinding.VersionDifference) - { - // lesser than reference version is worst than higher than reference version - return previousBinding.VersionDifference < 0; - } - - var previousVersion = definitions[previousBinding.DefinitionIndex].Identity.Version; - var newVersion = definitions[newBinding.DefinitionIndex].Identity.Version; - - // closer version is better: - return (newBinding.VersionDifference > 0) ? newVersion < previousVersion : newVersion > previousVersion; - } } } diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs index cfbcb9a71ceb820d46a06e9fb93622dc0247a5d7..af5b83cfaa934551bb40daf315d10882da5a3b83 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs @@ -383,16 +383,14 @@ internal bool IsBound protected static void BuildReferencedAssembliesAndModulesMaps( ImmutableArray references, ImmutableArray referenceMap, - IReadOnlyList implicitlyResolvedReferences, - int referencedAssemblyCount, int referencedModuleCount, out Dictionary referencedAssembliesMap, out Dictionary referencedModulesMap, out ImmutableArray> aliasesOfReferencedAssemblies) { - referencedAssembliesMap = new Dictionary(referenceMap.Length + implicitlyResolvedReferences.Count); + referencedAssembliesMap = new Dictionary(referenceMap.Length); referencedModulesMap = new Dictionary(referencedModuleCount); - var aliasesOfReferencedAssembliesBuilder = ArrayBuilder>.GetInstance(referencedAssemblyCount); + var aliasesOfReferencedAssembliesBuilder = ArrayBuilder>.GetInstance(referenceMap.Length - referencedModuleCount); for (int i = 0; i < referenceMap.Length; i++) { @@ -418,22 +416,6 @@ internal bool IsBound } } - for (int i = 0; i < implicitlyResolvedReferences.Count; i++) - { - var assemblyIndex = referencedAssemblyCount + i; - Debug.Assert(aliasesOfReferencedAssembliesBuilder.Count == assemblyIndex); - - referencedAssembliesMap.Add(implicitlyResolvedReferences[i], assemblyIndex); - - // Use aliases specified on the reference returned by the missing assembly resolver. - // Unlike explicitly given references, we don't apply de-duplication logic on references - // returned by the resolver. This is because we already have an assembly identity - // (rather than an opaque reference string or a preexisting metadata reference) - // and we don't ask the resolver to resolve an identity that matches identities - // we have already resolved. Hence there are no opportunities for reference duplication. - aliasesOfReferencedAssembliesBuilder.Add(implicitlyResolvedReferences[i].Properties.Aliases); - } - aliasesOfReferencedAssemblies = aliasesOfReferencedAssembliesBuilder.ToImmutableAndFree(); } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb b/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb index f67b7c17d9621bcf6f68f7dbbec11fc14aaeac05..85651f24145d6ab2cada0c7aa001f151bc84c2cc 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb @@ -256,7 +256,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Function CreateAndSetSourceAssemblyFullBind(compilation As VisualBasicCompilation) As Boolean Dim resolutionDiagnostics = DiagnosticBag.GetInstance() - Dim implicitlyResolvedReferences = ArrayBuilder(Of MetadataReference).GetInstance() Try Dim boundReferenceDirectiveMap As IDictionary(Of String, MetadataReference) = Nothing @@ -275,21 +274,29 @@ Namespace Microsoft.CodeAnalysis.VisualBasic resolutionDiagnostics) Dim assemblyBeingBuiltData As New AssemblyDataForAssemblyBeingBuilt(New AssemblyIdentity(name:=SimpleAssemblyName), referencedAssemblies, modules) - Dim allAssemblyData = referencedAssemblies.Insert(0, assemblyBeingBuiltData) + Dim explicitAssemblyData = referencedAssemblies.Insert(0, assemblyBeingBuiltData) ' Let's bind all the references and resolve missing one (if resolver is available) Dim corLibraryIndex As Integer Dim hasCircularReference As Boolean - Dim bindingResult() As BoundInputAssembly = Bind(allAssemblyData, + Dim implicitlyResolvedReferences As ImmutableArray(Of MetadataReference) = Nothing + Dim implicitlyResolvedReferenceMap As ImmutableArray(Of ResolvedReference) = Nothing + Dim allAssemblyData As ImmutableArray(Of AssemblyData) = Nothing + + Dim bindingResult() As BoundInputAssembly = Bind(explicitAssemblyData, compilation.Options.MetadataReferenceResolver, compilation.Options.MetadataImportOptions, + allAssemblyData, implicitlyResolvedReferences, + implicitlyResolvedReferenceMap, resolutionDiagnostics, hasCircularReference, corLibraryIndex) Debug.Assert(bindingResult.Length = allAssemblyData.Length) + referenceMap = referenceMap.AddRange(implicitlyResolvedReferenceMap) + Dim referencedAssembliesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim referencedModulesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim aliasesOfReferencedAssemblies As ImmutableArray(Of ImmutableArray(Of String)) = Nothing @@ -297,8 +304,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic BuildReferencedAssembliesAndModulesMaps( references, referenceMap, - implicitlyResolvedReferences, - referencedAssemblies.Length, modules.Length, referencedAssembliesMap, referencedModulesMap,