Skip to content

Running Compile tutorial code produces StackOverflowException in ImplicitLoadIfAllowed #258

@adamkennedy

Description

@adamkennedy

I have been experimenting with dynamic assembly compilation and the results of my tests have been going really well, while I was launching the compiler from F# Interactive sessions.

However, when switching to a Visual Studio Command Line F# project with the FSharp.Compiler.Service 0.76 NuGet package (so I can move to building a production version) I am getting StackOverflowExceptions from infinite loops in the compiler service.

This occurs regardless of the code being compiled.

For example, creating a project with the Hosted Compiler example code results in the same StackOverflowException as my actual project code.

// Learn more about F# at http://fsharp.net
// See the 'F# Tutorial' project for more help.

open Microsoft.FSharp.Compiler.SimpleSourceCodeServices
open System.IO

let scs = SimpleSourceCodeServices()

[<EntryPoint>]
let main argv = 
    let fn = Path.GetTempFileName()
    let fn2 = Path.ChangeExtension(fn, ".fs")
    let fn3 = Path.ChangeExtension(fn, ".dll")

    File.WriteAllText(fn2, """
    module M

    type C() = 
       member x.P = 1

    let x = 3 + 4
    """)

    let errors2, exitCode2, dynAssembly2 = 
        scs.CompileToDynamicAssembly([| "-o"; fn3; "-a"; fn2 |], execute=None)

    // return an integer exit code
    0

I have traced the infinite loop to the following set of stack entries.

FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.TcImports.RegisterAndImportReferencedAssemblies(Microsoft.FSharp.Core.FSharpOption<Microsoft.FSharp.Core.FSharpFunc<string,Microsoft.FSharp.Core.Unit>> displayPSTypeProviderSecurityDialogBlockingUI, Microsoft.FSharp.Collections.FSharpList<Microsoft.FSharp.Compiler.Build.AssemblyResolution> nms) Line 4408   F#
    FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.tryFile@4420(Microsoft.FSharp.Compiler.Build.TcImports tcImports, Microsoft.FSharp.Compiler.Range.range m, string speculativeFileName) Line 4425    F#
    FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.TcImports.ImplicitLoadIfAllowed(Microsoft.FSharp.Compiler.Range.range m, string assemblyName, bool lookupOnly) Line 4431    F#
    FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.TcImports.FindCcuInfo(Microsoft.FSharp.Compiler.Range.range m, string assemblyName, bool lookupOnly) Line 3806  F#
    FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.RegisterAndPrepareToImportReferencedDll@4363.Invoke(Microsoft.FSharp.Core.Unit unitVar0) Line 4363  F#
    FSharp.Compiler.Service.dll!Microsoft.FSharp.Compiler.Build.TcImports.RegisterAndImportReferencedAssemblies(Microsoft.FSharp.Core.FSharpOption<Microsoft.FSharp.Core.FSharpFunc<string,Microsoft.FSharp.Core.Unit>> displayPSTypeProviderSecurityDialogBlockingUI, Microsoft.FSharp.Collections.FSharpList<Microsoft.FSharp.Compiler.Build.AssemblyResolution> nms) Line 4408   F#

The relevant code is this, from build.fs around line 4414. assemblyName is "FSharp.Core"

    member tcImports.ImplicitLoadIfAllowed (m, assemblyName, lookupOnly) = 
        CheckDisposed()
        // If the user is asking for the default framework then also try to resolve other implicit assemblies as they are discovered.
        // Using this flag to mean 'allow implicit discover of assemblies'.
        let tcConfig = tcConfigP.Get()
        if not lookupOnly && tcConfig.implicitlyResolveAssemblies then 
            let tryFile speculativeFileName = 
                let foundFile = tcImports.TryResolveAssemblyReference (AssemblyReference (m, speculativeFileName, None), ResolveAssemblyReferenceMode.Speculative)
                match foundFile with 
                | OkResult (warns, res) -> 
                     ReportWarnings warns
                     tcImports.DoRegisterAndImportReferencedAssemblies(None,res)
                     true
                | ErrorResult (_warns, _err) -> 
                    // Throw away warnings and errors - this is speculative loading
                    false

            if tryFile (assemblyName + ".dll") then ()
            else tryFile (assemblyName + ".exe")  |> ignore

If there is something that I'm doing wrong here, it's not obvious to me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions