Skip to content

Commit

Permalink
Fix #13197 (#13870)
Browse files Browse the repository at this point in the history
* Fix #13197

* format code
  • Loading branch information
dsyme authored Sep 13, 2022
1 parent 38e7e57 commit 9d9c6c1
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 46 deletions.
33 changes: 19 additions & 14 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// --------------------------------------------------------------------
Expand Down Expand Up @@ -2803,12 +2811,16 @@ and [<Sealed>] 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 [<NoEquality; NoComparison>] ILPreTypeDef =
abstract Namespace: string list
abstract Name: string
Expand Down Expand Up @@ -3331,15 +3343,9 @@ let tname_UIntPtr = "System.UIntPtr"
let tname_TypedReference = "System.TypedReference"

[<NoEquality; NoComparison; StructuredFormatDisplay("{DebugText}")>]
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)

Expand Down Expand Up @@ -3394,17 +3400,16 @@ type ILGlobals

member x.IsPossiblePrimaryAssemblyRef(aref: ILAssemblyRef) =
aref.EqualsIgnoringVersion x.primaryAssemblyRef
|| assembliesThatForwardToPrimaryAssembly
|> Array.exists aref.EqualsIgnoringVersion
|| equivPrimaryAssemblyRefs |> Array.exists aref.EqualsIgnoringVersion

/// For debugging
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
member x.DebugText = x.ToString()

override x.ToString() = "<ILGlobals>"

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)

Expand Down
21 changes: 14 additions & 7 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open FSharp.Compiler.IO
open System.Collections.Generic
open System.Reflection

/// Represents the target primary assembly
[<RequireQualifiedAccess>]
type internal PrimaryAssembly =
| Mscorlib
Expand All @@ -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[]

Expand Down Expand Up @@ -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 <c>FindByName</c> 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 <c>FindByName</c> will result in all the ILPreTypeDefs being read.
member internal FindByName: string -> ILTypeDef

/// Calls to <c>ExistsByName</c> will result in all the ILPreTypeDefs being read.
member internal ExistsByName: string -> bool

/// Represents IL Type Definitions.
[<NoComparison; NoEquality>]
type ILTypeDef =
Expand Down Expand Up @@ -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
Expand Down
50 changes: 25 additions & 25 deletions src/Compiler/Driver/CompilerImports.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2321,13 +2321,13 @@ and [<Sealed>] 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
Expand All @@ -2336,28 +2336,28 @@ and [<Sealed>] 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 {
Expand Down Expand Up @@ -2406,7 +2406,7 @@ and [<Sealed>] 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 =
Expand Down
42 changes: 42 additions & 0 deletions tests/FSharp.Compiler.UnitTests/FsiTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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) = ()

[<Collection("SingleThreaded")>]
module FsiTests =
Expand Down Expand Up @@ -636,3 +643,38 @@ module FsiTests =
let boundValue = fsiSession.GetBoundValues() |> List.exactlyOne
Assert.shouldBe typeof<CustomType2[,]> boundValue.Value.ReflectionType
boundValue.Value.ReflectionValue.Should().Be(mdArr, "") |> ignore

#if NETCOREAPP
[<Fact>]
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<Sentinel>.Assembly.Location
"--noninteractive"
"--targetprofile:netcore"
"--langversion:preview"
multiemitFlag
useSdkRefsFlag
|]
let fsi = FsiEvaluationSession.Create(config, argv, TextReader.Null, TextWriter.Null, TextWriter.Null)
let assemblyPath = typeof<Sentinel>.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

0 comments on commit 9d9c6c1

Please sign in to comment.