Skip to content

Fix mixed emit-multi+/- sessions #18465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* Make `[<CallerMemberName; Struct>]` combination work([PR #18444](https://github.com/dotnet/fsharp/pull/18444/))
* Fix code completion considers types from own namespace non-imported ([PR #18518](https://github.com/dotnet/fsharp/issues/18518))
* Code completion: fix getting qualifier expression in do statements in type decls ([PR #18524](https://github.com/dotnet/fsharp/pull/18524))
* Fixed: [#18441](https://github.com/dotnet/fsharp/issues/18441) FSI multi-emit unstable. ([PR #18465](https://github.com/dotnet/fsharp/pull/18465))

### Added
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))
Expand Down
25 changes: 16 additions & 9 deletions src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,8 @@ let internal mkBoundValueTypedImpl tcGlobals m moduleName name ty =
let qname = QualifiedNameOfFile.QualifiedNameOfFile(Ident(moduleName, m))
entity, v, CheckedImplFile.CheckedImplFile(qname, [], mty, contents, false, false, StampMap.Empty, Map.empty)

let dynamicCcuName = "FSI-ASSEMBLY"
let dynamicCcuName (isEmitMulti) =
$"""FSI-ASSEMBLY{if isEmitMulti then "-MULTI" else ""}"""

/// Encapsulates the coordination of the typechecking, optimization and code generation
/// components of the F# compiler for interactively executed fragments of code.
Expand Down Expand Up @@ -1723,7 +1724,11 @@ type internal FsiDynamicCompiler
None
else
let assemBuilder, moduleBuilder =
mkDynamicAssemblyAndModule (dynamicCcuName, tcConfigB.optSettings.LocalOptimizationsEnabled, fsiCollectible)
mkDynamicAssemblyAndModule (
dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit),
tcConfigB.optSettings.LocalOptimizationsEnabled,
fsiCollectible
)

dynamicAssemblies.Add(assemBuilder)
Some(assemBuilder, moduleBuilder)
Expand Down Expand Up @@ -1813,7 +1818,7 @@ type internal FsiDynamicCompiler

let attrs =
[
tcGlobals.MakeInternalsVisibleToAttribute(dynamicCcuName)
tcGlobals.MakeInternalsVisibleToAttribute(dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit))
yield! manifest.CustomAttrs.AsList()
]

Expand Down Expand Up @@ -1960,7 +1965,9 @@ type internal FsiDynamicCompiler
diagnosticsLogger.AbortOnError(fsiConsoleOutput)

ReportTime tcConfig "Linking"
let ilxMainModule = CreateModuleFragment(tcConfigB, dynamicCcuName, codegenResults)

let ilxMainModule =
CreateModuleFragment(tcConfigB, dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit), codegenResults)

diagnosticsLogger.AbortOnError(fsiConsoleOutput)

Expand Down Expand Up @@ -2652,7 +2659,7 @@ type internal FsiDynamicCompiler

let tcEnv, asms =
try
RequireReferences(ctok, tcImports, tcState.TcEnvFromImpls, dynamicCcuName, resolutions)
RequireReferences(ctok, tcImports, tcState.TcEnvFromImpls, dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit), resolutions)
with _ ->
for (path, _, _, m) in refs do
tcConfigB.RemoveReferencedAssemblyByPath(m, path)
Expand Down Expand Up @@ -3025,7 +3032,7 @@ type internal FsiDynamicCompiler
let emEnv0 =
if tcConfigB.fsiMultiAssemblyEmit then
let emEnv =
ILMultiInMemoryAssemblyEmitEnv(ilGlobals, resolveAssemblyRef, dynamicCcuName)
ILMultiInMemoryAssemblyEmitEnv(ilGlobals, resolveAssemblyRef, dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit))

MultipleInMemoryAssemblies emEnv
else
Expand All @@ -3041,10 +3048,10 @@ type internal FsiDynamicCompiler
let emEnv = ILDynamicAssemblyWriter.emEnv0
SingleRefEmitAssembly(cenv, emEnv)

let tcEnv, openDecls0 =
GetInitialTcEnv(dynamicCcuName, rangeStdin0, tcConfig, tcImports, tcGlobals)
let ccuName = dynamicCcuName (tcConfigB.fsiMultiAssemblyEmit)

let ccuName = dynamicCcuName
let tcEnv, openDecls0 =
GetInitialTcEnv(ccuName, rangeStdin0, tcConfig, tcImports, tcGlobals)

let tcState =
GetInitialTcState(rangeStdin0, ccuName, tcConfig, tcGlobals, tcImports, tcEnv, openDecls0)
Expand Down
47 changes: 32 additions & 15 deletions tests/FSharp.Compiler.ComponentTests/Scripting/Interactive.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ open Xunit
open System
open FSharp.Test.Compiler
open FSharp.Test.ScriptHelpers
open FSharp.Compiler.Interactive.Shell
open FSharp.Compiler.Diagnostics
open FSharp.Test.CompilerAssertHelpers

module ``Interactive tests`` =

Expand Down Expand Up @@ -35,34 +38,48 @@ module ``Interactive tests`` =
]

[<Theory>]
[<InlineData(true)>]
[<InlineData(false)>]
let ``Evaluation of multiple sessions should succeed`` (useMultiEmit) =
[<InlineData(true, true)>]
[<InlineData(false, false)>]
[<InlineData(false, true)>]
[<InlineData(true, false)>]
let ``Evaluation of multiple sessions should succeed`` (useMultiEmit1, useMultiEmit2) =

let args : string array = [| if useMultiEmit then "--multiemit+" else "--multiemit-"|]
use sessionOne = new FSharpScript(additionalArgs=args)
use sessionTwo = new FSharpScript(additionalArgs=args)
// This test is to check that we can have multiple sessions and that they can access each other

sessionOne.Eval("""
module Test1 =
let AssertCanWeAccessTheOtherSession (session: FSharpScript) (script: string) (moduleName: string) (errorChoice: string) =

let test1 obj = sprintf "Execute - Test1.test1 - %A" obj""") |> ignore
let result: Result<FsiValue option, exn> * FSharpDiagnostic[] = session.Eval(script)
let expected =
[ (Error 39, Line 1, Col 1, Line 1, Col 6, $"The value, namespace, type or module '{moduleName}' is not defined. Maybe you want one of the following:{errorChoice}") ]

let result1 = sessionOne.Eval("""Test1.test1 18""") |> getValue
match result with
| Result.Error _, diagnostics -> Assert.WithDiagnostics(0, diagnostics, expected)
| _ -> ()

// Initialize
let args useMultiEmit : string array = [| if useMultiEmit then "--multiemit+" else "--multiemit-"|]
use sessionOne = new FSharpScript(additionalArgs = args useMultiEmit1)
use sessionTwo = new FSharpScript(additionalArgs = args useMultiEmit2)
use sessionThree = new FSharpScript(additionalArgs = args useMultiEmit2)

// First session
sessionOne.Eval("""module Test1 = let test1 obj = sprintf "Execute - Test1.test1 - %A" obj""") |> ignore
let result1 = sessionOne.Eval("Test1.test1 18") |> getValue
let value1 = result1.Value
Assert.Equal(typeof<string>, value1.ReflectionType)
Assert.Equal("Execute - Test1.test1 - 18", value1.ReflectionValue :?> string)

sessionTwo.Eval("""
module Test2 =

let test2 obj = sprintf "Execute - Test2.test2 - %A" obj""") |> ignore

// Second session
sessionTwo.Eval("""module Test2 = let test2 obj = sprintf "Execute - Test2.test2 - %A" obj""") |> ignore
let result2 = sessionTwo.Eval("""Test2.test2 27""") |> getValue
let value2 = result2.Value
Assert.Equal(typeof<string>, value2.ReflectionType)
Assert.Equal("Execute - Test2.test2 - 27", value2.ReflectionValue :?> string)

// Can I access the other session
AssertCanWeAccessTheOtherSession sessionOne "Test2.test2 19" "Test2" "\nTest1\nText" // Session 1 can't access values from session 2
AssertCanWeAccessTheOtherSession sessionTwo "Test1.test1 13" "Test1" "\nTest2\nText" // Session 2 can't access values from session 1

module ``External FSI tests`` =
[<Fact>]
let ``Eval object value``() =
Expand Down
59 changes: 45 additions & 14 deletions tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1671,20 +1671,47 @@ Actual:
let private withResultIgnoreNativeRange (expectedResult: ErrorInfo ) (result: CompilationResult) : CompilationResult =
withResultsIgnoreNativeRange [expectedResult] result

let withDiagnostics (expected: (ErrorType * Line * Col * Line * Col * string) list) (result: CompilationResult) : CompilationResult =
let expectedResults: ErrorInfo list =
[ for e in expected do
let (error, Line startLine, Col startCol, Line endLine, Col endCol, message) = e
{ Error = error
Range =
{ StartLine = startLine
StartColumn = startCol
EndLine = endLine
EndColumn = endCol }
NativeRange = Unchecked.defaultof<_>
SubCategory = ""
Message = message } ]
withResultsIgnoreNativeRange expectedResults result
let private convertExpectedsToErrorInfos(expected: (ErrorType * Line * Col * Line * Col * string) list): ErrorInfo list = [
for e in expected do
let (error, Line startLine, Col startCol, Line endLine, Col endCol, message) = e
{
Error = error
Range = {
StartLine = startLine
StartColumn = startCol
EndLine = endLine
EndColumn = endCol
}
NativeRange = Unchecked.defaultof<_>
SubCategory = ""
Message = message
}
]

let private convertDiagnosticsToErrorInfos (diagnostics: FSharpDiagnostic[]) : ErrorInfo list =
diagnostics
|> Array.map (fun diagnostic ->
let errorType =
match diagnostic.Severity with
| FSharpDiagnosticSeverity.Error -> Error diagnostic.ErrorNumber
| FSharpDiagnosticSeverity.Warning -> Warning diagnostic.ErrorNumber
| FSharpDiagnosticSeverity.Info -> Information diagnostic.ErrorNumber
| FSharpDiagnosticSeverity.Hidden -> Hidden diagnostic.ErrorNumber
{
Error = errorType
Range = {
StartLine = diagnostic.StartLine
StartColumn = diagnostic.StartColumn
EndLine = diagnostic.EndLine
EndColumn = diagnostic.EndColumn
}
NativeRange = diagnostic.Range
Message = diagnostic.Message
SubCategory = diagnostic.Subcategory
}) |> Array.toList

let withDiagnostics expected (result: CompilationResult) : CompilationResult =
withResultsIgnoreNativeRange (convertExpectedsToErrorInfos expected) result

let withSingleDiagnostic (expected: (ErrorType * Line * Col * Line * Col * string)) (result: CompilationResult) : CompilationResult =
withDiagnostics [expected] result
Expand All @@ -1695,6 +1722,10 @@ Actual:
let withError (expectedError: ErrorInfo) (result: CompilationResult) : CompilationResult =
withErrors [expectedError] result

type Assert =
static member WithDiagnostics(libAdjust, result, expected) =
assertErrors "Results" libAdjust (convertDiagnosticsToErrorInfos result) (convertExpectedsToErrorInfos expected)

module StructuredResultsAsserts =
type SimpleErrorInfo =
{ Error: ErrorType
Expand Down
5 changes: 4 additions & 1 deletion tests/FSharp.Test.Utilities/CompilerAssert.fs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ module CompilerAssertHelpers =
yield source.WithFileName(destFileName)
]
rawCompile outputFilePath isExe options targetFramework sources

let assertErrors libAdjust ignoreWarnings (errors: FSharpDiagnostic []) expectedErrors =
let errorMessage (error: FSharpDiagnostic) =
let errN, range, message = error.ErrorNumber, error.Range, error.Message
Expand Down Expand Up @@ -694,6 +694,9 @@ Updated automatically, please check diffs in your pull request, changes must be
compileCompilation ignoreWarnings cmpl (fun ((errors, _, _), _) ->
assertErrors 0 ignoreWarnings errors expectedErrors)

static member assertWithErrors(libAdjust, ignoreWarnings, errors, expectedErrors) =
assertErrors libAdjust, ignoreWarnings, errors, expectedErrors

static member Compile(cmpl: Compilation, ?ignoreWarnings) =
CompilerAssert.CompileWithErrors(cmpl, [||], defaultArg ignoreWarnings false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3509-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3509-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack.
[IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001C7][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3509-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001C7][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack.
Expand Down
Loading