From 9d9c6c114efc3ceda8b33edb2ecf7c66e51100bb Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 13 Sep 2022 13:04:13 +0100 Subject: [PATCH] Fix #13197 (#13870) * Fix https://github.com/dotnet/fsharp/issues/13197 * format code --- src/Compiler/AbstractIL/il.fs | 33 ++++++++------ src/Compiler/AbstractIL/il.fsi | 21 ++++++--- src/Compiler/Driver/CompilerImports.fs | 50 ++++++++++----------- tests/FSharp.Compiler.UnitTests/FsiTests.fs | 42 +++++++++++++++++ 4 files changed, 100 insertions(+), 46 deletions(-) diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index c0104807dcc..bbd5183842a 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -54,6 +54,14 @@ type PrimaryAssembly = | System_Runtime -> "System.Runtime" | NetStandard -> "netstandard" + static member IsPossiblePrimaryAssembly(fileName: string) = + let name = System.IO.Path.GetFileNameWithoutExtension(fileName) + + String.Compare(name, "mscorlib", true) <> 0 + || String.Compare(name, "System.Runtime", true) <> 0 + || String.Compare(name, "netstandard", true) <> 0 + || String.Compare(name, "System.Private.CoreLib", true) <> 0 + // -------------------------------------------------------------------- // Utilities: type names // -------------------------------------------------------------------- @@ -2803,12 +2811,16 @@ and [] ILTypeDefs(f: unit -> ILPreTypeDef[]) = member x.GetEnumerator() = (seq { for pre in array.Value -> pre.GetTypeDef() }).GetEnumerator() - member x.AsArrayOfPreTypeDefs() = array.Value + member _.AsArrayOfPreTypeDefs() = array.Value - member x.FindByName nm = + member _.FindByName nm = let ns, n = splitILTypeName nm dict.Value[ (ns, n) ].GetTypeDef() + member _.ExistsByName nm = + let ns, n = splitILTypeName nm + dict.Value.ContainsKey((ns, n)) + and [] ILPreTypeDef = abstract Namespace: string list abstract Name: string @@ -3331,15 +3343,9 @@ let tname_UIntPtr = "System.UIntPtr" let tname_TypedReference = "System.TypedReference" [] -type ILGlobals - ( - primaryScopeRef: ILScopeRef, - assembliesThatForwardToPrimaryAssembly: ILAssemblyRef list, - fsharpCoreAssemblyScopeRef: ILScopeRef - ) = +type ILGlobals(primaryScopeRef: ILScopeRef, equivPrimaryAssemblyRefs: ILAssemblyRef list, fsharpCoreAssemblyScopeRef: ILScopeRef) = - let assembliesThatForwardToPrimaryAssembly = - Array.ofList assembliesThatForwardToPrimaryAssembly + let equivPrimaryAssemblyRefs = Array.ofList equivPrimaryAssemblyRefs let mkSysILTypeRef nm = mkILTyRef (primaryScopeRef, nm) @@ -3394,8 +3400,7 @@ type ILGlobals member x.IsPossiblePrimaryAssemblyRef(aref: ILAssemblyRef) = aref.EqualsIgnoringVersion x.primaryAssemblyRef - || assembliesThatForwardToPrimaryAssembly - |> Array.exists aref.EqualsIgnoringVersion + || equivPrimaryAssemblyRefs |> Array.exists aref.EqualsIgnoringVersion /// For debugging [] @@ -3403,8 +3408,8 @@ type ILGlobals override x.ToString() = "" -let mkILGlobals (primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef) = - ILGlobals(primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef) +let mkILGlobals (primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef) = + ILGlobals(primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef) let mkNormalCall mspec = I_call(Normalcall, mspec, None) diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 32528348907..3ea66ef5bf2 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -8,6 +8,7 @@ open FSharp.Compiler.IO open System.Collections.Generic open System.Reflection +/// Represents the target primary assembly [] type internal PrimaryAssembly = | Mscorlib @@ -16,6 +17,11 @@ type internal PrimaryAssembly = member Name: string + /// Checks if an assembly resolution may represent a primary assembly that actually contains the + /// definition of Sytem.Object. Note that the chosen target primary assembly may not actually be the one + /// that contains the definition of System.Object - it is just the one we are choosing to emit for. + static member IsPossiblePrimaryAssembly: fileName: string -> bool + /// Represents guids type ILGuid = byte[] @@ -1407,12 +1413,12 @@ type ILTypeDefs = /// Get some information about the type defs, but do not force the read of the type defs themselves. member internal AsArrayOfPreTypeDefs: unit -> ILPreTypeDef[] - /// Calls to FindByName will result in any laziness in the overall - /// set of ILTypeDefs being read in in addition - /// to the details for the type found, but the remaining individual - /// type definitions will not be read. + /// Calls to FindByName will result in all the ILPreTypeDefs being read. member internal FindByName: string -> ILTypeDef + /// Calls to ExistsByName will result in all the ILPreTypeDefs being read. + member internal ExistsByName: string -> bool + /// Represents IL Type Definitions. [] type ILTypeDef = @@ -1841,10 +1847,11 @@ type internal ILGlobals = member IsPossiblePrimaryAssemblyRef: ILAssemblyRef -> bool /// Build the table of commonly used references given functions to find types in system assemblies +/// +/// primaryScopeRef is the primary assembly we are emitting +/// equivPrimaryAssemblyRefs are ones regarded as equivalent val internal mkILGlobals: - primaryScopeRef: ILScopeRef * - assembliesThatForwardToPrimaryAssembly: ILAssemblyRef list * - fsharpCoreAssemblyScopeRef: ILScopeRef -> + primaryScopeRef: ILScopeRef * equivPrimaryAssemblyRefs: ILAssemblyRef list * fsharpCoreAssemblyScopeRef: ILScopeRef -> ILGlobals val internal PrimaryAssemblyILGlobals: ILGlobals diff --git a/src/Compiler/Driver/CompilerImports.fs b/src/Compiler/Driver/CompilerImports.fs index b7738705788..0958224400d 100644 --- a/src/Compiler/Driver/CompilerImports.fs +++ b/src/Compiler/Driver/CompilerImports.fs @@ -2321,13 +2321,13 @@ and [] TcImports | _, [ ResolvedImportedAssembly ccu ] -> ccu.FSharpViewOfMetadata.ILScopeRef | _ -> failwith "primaryScopeRef - unexpected" + let resolvedAssemblies = tcResolutions.GetAssemblyResolutions() + let primaryAssemblyResolvedPath = match primaryAssemblyResolution with | [ primaryAssemblyResolution ] -> primaryAssemblyResolution.resolvedPath | _ -> failwith "primaryAssemblyResolvedPath - unexpected" - let resolvedAssemblies = tcResolutions.GetAssemblyResolutions() - let readerSettings: ILReaderOptions = { pdbDirPath = None @@ -2336,28 +2336,28 @@ and [] TcImports tryGetMetadataSnapshot = tcConfig.tryGetMetadataSnapshot } - let tryFindAssemblyByExportedType manifest (exportedType: ILExportedTypeOrForwarder) = - match exportedType.ScopeRef, primaryScopeRef with - | ILScopeRef.Assembly aref1, ILScopeRef.Assembly aref2 when aref1.EqualsIgnoringVersion aref2 -> - mkRefToILAssembly manifest |> Some - | _ -> None - - let tryFindAssemblyThatForwardsToPrimaryAssembly manifest = - manifest.ExportedTypes.TryFindByName "System.Object" - |> Option.bind (tryFindAssemblyByExportedType manifest) - - // Determine what other assemblies could have been the primary assembly - // by checking to see if "System.Object" is an exported type. - let assembliesThatForwardToPrimaryAssembly = - resolvedAssemblies - |> List.choose (fun resolvedAssembly -> - if primaryAssemblyResolvedPath <> resolvedAssembly.resolvedPath then - let reader = OpenILModuleReader resolvedAssembly.resolvedPath readerSettings - - reader.ILModuleDef.Manifest - |> Option.bind tryFindAssemblyThatForwardsToPrimaryAssembly - else - None) + let tryFindEquivPrimaryAssembly (resolvedAssembly: AssemblyResolution) = + if primaryAssemblyResolvedPath = resolvedAssembly.resolvedPath then + None + else + let reader = OpenILModuleReader resolvedAssembly.resolvedPath readerSettings + let mdef = reader.ILModuleDef + + // We check the exported types of all assemblies, since many may forward System.Object, + // but only check the actual type definitions for specific assemblies that we know + // might actually declare System.Object. + match mdef.Manifest with + | Some manifest when + manifest.ExportedTypes.TryFindByName "System.Object" |> Option.isSome + || PrimaryAssembly.IsPossiblePrimaryAssembly resolvedAssembly.resolvedPath + && mdef.TypeDefs.ExistsByName "System.Object" + -> + mkRefToILAssembly manifest |> Some + | _ -> None + + // Find assemblies which also declare System.Object + let equivPrimaryAssemblyRefs = + resolvedAssemblies |> List.choose tryFindEquivPrimaryAssembly let! fslibCcu, fsharpCoreAssemblyScopeRef = node { @@ -2406,7 +2406,7 @@ and [] TcImports sysCcus |> Array.tryFind (fun ccu -> ccuHasType ccu path typeName) let ilGlobals = - mkILGlobals (primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef) + mkILGlobals (primaryScopeRef, equivPrimaryAssemblyRefs, fsharpCoreAssemblyScopeRef) // OK, now we have both mscorlib.dll and FSharp.Core.dll we can create TcGlobals let tcGlobals = diff --git a/tests/FSharp.Compiler.UnitTests/FsiTests.fs b/tests/FSharp.Compiler.UnitTests/FsiTests.fs index 0419ee4c159..36b36a1c49f 100644 --- a/tests/FSharp.Compiler.UnitTests/FsiTests.fs +++ b/tests/FSharp.Compiler.UnitTests/FsiTests.fs @@ -6,6 +6,13 @@ open FluentAssertions open FSharp.Compiler.Interactive.Shell open FSharp.Test open Xunit +open System.Threading + +type Sentinel () = + let x = () + +module MyModule = + let test(x: int) = () [] module FsiTests = @@ -636,3 +643,38 @@ module FsiTests = let boundValue = fsiSession.GetBoundValues() |> List.exactlyOne Assert.shouldBe typeof boundValue.Value.ReflectionType boundValue.Value.ReflectionValue.Should().Be(mdArr, "") |> ignore + +#if NETCOREAPP + [] + let ``Evaluating simple reference and code succeeds under permutations``() = + + for useSdkRefsFlag in ["/usesdkrefs"; "/usesdkrefs-"] do + for multiemitFlag in ["/multiemit"; "/multiemit-"] do + let config = FsiEvaluationSession.GetDefaultConfiguration() + let argv = [| + typeof.Assembly.Location + "--noninteractive" + "--targetprofile:netcore" + "--langversion:preview" + multiemitFlag + useSdkRefsFlag + |] + let fsi = FsiEvaluationSession.Create(config, argv, TextReader.Null, TextWriter.Null, TextWriter.Null) + let assemblyPath = typeof.Assembly.Location.Replace("\\", "/") + let code = $@" + #r ""{assemblyPath}"" + FSharp.Compiler.UnitTests.MyModule.test(3)" + let ch, errors = fsi.EvalInteractionNonThrowing(code, CancellationToken.None) + errors + |> Array.iter (fun e -> printfn "error: %A" e) + match ch with + | Choice1Of2 v -> + let v = + match v with + | Some v -> sprintf "%A" v.ReflectionValue + | None -> "(none)" + printfn "value: %A" v + | Choice2Of2 e -> + printfn "exception: %A" e + raise e +#endif