From c0637d94bf7bb6ffc55d0145e9ddcaa807c96b33 Mon Sep 17 00:00:00 2001 From: majocha <1760221+majocha@users.noreply.github.com> Date: Sat, 11 Oct 2025 15:07:06 +0200 Subject: [PATCH 1/5] enable graph checking --- src/Compiler/Driver/ParseAndCheckInputs.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index c6de5e776a..c53d716e7a 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1786,7 +1786,7 @@ let CheckMultipleInputsUsingGraphMode (idx, friendlyFileName)) |> Graph.writeMermaidToFile graphFile) - let _ = ctok // TODO Use it + ignore ctok // TODO Use it let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger // In the first linear part of parallel checking, we use a 'checkForErrors' that checks either for errors @@ -1880,8 +1880,7 @@ let CheckMultipleInputsUsingGraphMode let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) = // tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions let results, tcState = - match tcConfig.typeCheckingConfig.Mode with - | TypeCheckingMode.Graph when (not tcConfig.isInteractive && not tcConfig.compilingFSharpCore) -> + if not tcConfig.isInteractive && not tcConfig.compilingFSharpCore then CheckMultipleInputsUsingGraphMode( ctok, checkForErrors, @@ -1893,7 +1892,8 @@ let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tc eagerFormat, inputs ) - | _ -> CheckMultipleInputsSequential(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, inputs) + else + CheckMultipleInputsSequential(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, inputs) let (tcEnvAtEndOfLastFile, topAttrs, implFiles, _), tcState = CheckMultipleInputsFinish(results, tcState) From 10876edca4fd50d792f732ad2fc1138db7ca0e98 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 13 Oct 2025 21:32:41 +0200 Subject: [PATCH 2/5] update neg14, neg56_a --- .../Miscellaneous/MigratedTypeCheckTests.fs | 2 +- tests/fsharp/tests.fs | 3 --- tests/fsharp/typecheck/sigs/neg14.bsl | 8 ++++++++ tests/fsharp/typecheck/sigs/neg56_a.bsl | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs index b52d62858f..eaed687d4a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs @@ -191,7 +191,7 @@ let ``type check neg55`` () = singleNegTest ( "typecheck/sigs") "neg55" [] let ``type check neg56`` () = singleNegTest ( "typecheck/sigs") "neg56" -[] +[] let ``type check neg56_a`` () = singleNegTest ( "typecheck/sigs") "neg56_a" [] diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index c8cbfdab25..764481fc46 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -2282,9 +2282,6 @@ module TypecheckTests = [] let ``type check neg49`` () = singleNegTest (testConfig "typecheck/sigs") "neg49" - [] - let ``type check neg56_a`` () = singleNegTest (testConfig "typecheck/sigs") "neg56_a" - [] let ``type check neg94`` () = singleNegTest (testConfig "typecheck/sigs") "neg94" diff --git a/tests/fsharp/typecheck/sigs/neg14.bsl b/tests/fsharp/typecheck/sigs/neg14.bsl index 28f4763d71..41e52bbdc2 100644 --- a/tests/fsharp/typecheck/sigs/neg14.bsl +++ b/tests/fsharp/typecheck/sigs/neg14.bsl @@ -1,4 +1,12 @@ neg14a.fs(9,6,9,33): typecheck error FS0343: The type 'missingInterfaceInSignature' implements 'System.IComparable' explicitly but provides no corresponding override for 'Object.Equals'. An implementation of 'Object.Equals' has been automatically provided, implemented via 'System.IComparable'. Consider implementing the override 'Object.Equals' explicitly +neg14a.fs(2,8,2,11): typecheck error FS0193: Module 'Lib' requires a type 'z' + +neg14a.fs(2,8,2,11): typecheck error FS0193: Module 'Lib' requires a type 'missingTypeVariableInSignature' + +neg14a.fs(2,8,2,11): typecheck error FS0193: Module 'Lib' requires a type 'missingTypeInImplementation' + +neg14a.fs(2,8,2,11): typecheck error FS0193: Module 'Lib' requires a type 'fieldsInWrongOrder' + neg14b.fs(2,13,2,14): typecheck error FS0039: The value, constructor, namespace or type 'X' is not defined. diff --git a/tests/fsharp/typecheck/sigs/neg56_a.bsl b/tests/fsharp/typecheck/sigs/neg56_a.bsl index ca83218a1b..7d477679f7 100644 --- a/tests/fsharp/typecheck/sigs/neg56_a.bsl +++ b/tests/fsharp/typecheck/sigs/neg56_a.bsl @@ -1,2 +1,4 @@ neg56_a.fs(11,35,11,47): typecheck error FS1125: The instantiation of the generic type 'list1' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'list1<_>'. + +neg56_a.fs(15,18,15,33): typecheck error FS3068: The function or member 'toList' is used in a way that requires further type annotations at its definition to ensure consistency of inferred types. The inferred signature is 'static member private list1.toList: ('a list1 -> 'a list)'. From fdeb4a9a177389e2e441d09cfbaf63f63b4ab730 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:32:46 +0200 Subject: [PATCH 3/5] fixes --- .../Driver/GraphChecking/DependencyResolution.fs | 14 ++++++++++++++ src/Compiler/Driver/GraphChecking/Types.fs | 8 ++++++++ src/Compiler/Driver/GraphChecking/Types.fsi | 3 +++ src/Compiler/Driver/ParseAndCheckInputs.fs | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index 1425ec0f9e..024c89dbf3 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -206,6 +206,12 @@ let mkGraph (filePairs: FilePairMap) (files: FileInProject array) : Graph Array.empty | Some sigIdx -> Array.singleton sigIdx + let wrongOrderSignature = + if file.ParsedInput.IsSigFile then + match filePairs.TryGetWrongOrderSignatureToImplementationIndex file.Idx with + | Some idx -> Array.singleton idx + | None -> Array.empty + else Array.empty + let allDependencies = [| yield! depsResult.FoundDependencies yield! ghostDependencies yield! signatureDependency + yield! wrongOrderSignature |] |> Array.distinct diff --git a/src/Compiler/Driver/GraphChecking/Types.fs b/src/Compiler/Driver/GraphChecking/Types.fs index 7443df5820..493f70cf2d 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fs +++ b/src/Compiler/Driver/GraphChecking/Types.fs @@ -25,6 +25,8 @@ type internal FileInProject = ParsedInput: ParsedInput } + member x.IsScript = match x.ParsedInput with ParsedInput.ImplFile impl -> impl.IsScript | _ -> false + /// There is a subtle difference between a module and namespace. /// A namespace does not necessarily expose a set of dependent files. /// Only when the namespace exposes types that could later be inferred. @@ -175,5 +177,11 @@ type internal FilePairMap(files: FileInProject array) = member x.IsSignature(index: FileIndex) = Map.containsKey index sigToImpl + member x.TryGetWrongOrderSignatureToImplementationIndex(index: FileIndex) = + let input = files[index].ParsedInput + files |> Array.truncate index + |> Array.tryFindIndex (fun f -> f.ParsedInput.IsImplFile && f.ParsedInput.QualifiedName.Text = input.QualifiedName.Text) + + /// Callback that returns a previously calculated 'Result and updates 'State accordingly. type internal Finisher<'Node, 'State, 'Result> = Finisher of node: 'Node * finisher: ('State -> 'Result * 'State) diff --git a/src/Compiler/Driver/GraphChecking/Types.fsi b/src/Compiler/Driver/GraphChecking/Types.fsi index 5f075a1bad..6a529b104a 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fsi +++ b/src/Compiler/Driver/GraphChecking/Types.fsi @@ -23,6 +23,8 @@ type internal FileInProject = FileName: FileName ParsedInput: ParsedInput } + member IsScript: bool + /// There is a subtle difference between a module and namespace. /// A namespace does not necessarily expose a set of dependent files. /// Only when the namespace exposes types that could later be inferred. @@ -115,6 +117,7 @@ type internal FilePairMap = member HasSignature: implementationIndex: FileIndex -> bool member TryGetSignatureIndex: implementationIndex: FileIndex -> FileIndex option member IsSignature: index: FileIndex -> bool + member TryGetWrongOrderSignatureToImplementationIndex: index: FileIndex -> FileIndex option /// Callback that returns a previously calculated 'Result and updates 'State accordingly. type internal Finisher<'Node, 'State, 'Result> = Finisher of node: 'Node * finisher: ('State -> 'Result * 'State) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index c53d716e7a..087a25cbff 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1880,7 +1880,7 @@ let CheckMultipleInputsUsingGraphMode let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) = // tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions let results, tcState = - if not tcConfig.isInteractive && not tcConfig.compilingFSharpCore then + if not tcConfig.deterministic && not tcConfig.isInteractive && not tcConfig.compilingFSharpCore then CheckMultipleInputsUsingGraphMode( ctok, checkForErrors, From 8304a2b7ef8b1107cb45704785f2f8d5a07b52d7 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:01:28 +0200 Subject: [PATCH 4/5] fantomas --- .../Driver/GraphChecking/DependencyResolution.fs | 3 ++- src/Compiler/Driver/GraphChecking/Types.fs | 12 +++++++++--- src/Compiler/Driver/ParseAndCheckInputs.fs | 6 +++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index 024c89dbf3..817588e700 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -246,7 +246,8 @@ let mkGraph (filePairs: FilePairMap) (files: FileInProject array) : Graph Array.singleton idx | None -> Array.empty - else Array.empty + else + Array.empty let allDependencies = [| diff --git a/src/Compiler/Driver/GraphChecking/Types.fs b/src/Compiler/Driver/GraphChecking/Types.fs index 493f70cf2d..7739bd531f 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fs +++ b/src/Compiler/Driver/GraphChecking/Types.fs @@ -25,7 +25,10 @@ type internal FileInProject = ParsedInput: ParsedInput } - member x.IsScript = match x.ParsedInput with ParsedInput.ImplFile impl -> impl.IsScript | _ -> false + member x.IsScript = + match x.ParsedInput with + | ParsedInput.ImplFile impl -> impl.IsScript + | _ -> false /// There is a subtle difference between a module and namespace. /// A namespace does not necessarily expose a set of dependent files. @@ -179,9 +182,12 @@ type internal FilePairMap(files: FileInProject array) = member x.TryGetWrongOrderSignatureToImplementationIndex(index: FileIndex) = let input = files[index].ParsedInput - files |> Array.truncate index - |> Array.tryFindIndex (fun f -> f.ParsedInput.IsImplFile && f.ParsedInput.QualifiedName.Text = input.QualifiedName.Text) + files + |> Array.truncate index + |> Array.tryFindIndex (fun f -> + f.ParsedInput.IsImplFile + && f.ParsedInput.QualifiedName.Text = input.QualifiedName.Text) /// Callback that returns a previously calculated 'Result and updates 'State accordingly. type internal Finisher<'Node, 'State, 'Result> = Finisher of node: 'Node * finisher: ('State -> 'Result * 'State) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 087a25cbff..5db05aa6e1 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1880,7 +1880,11 @@ let CheckMultipleInputsUsingGraphMode let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) = // tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions let results, tcState = - if not tcConfig.deterministic && not tcConfig.isInteractive && not tcConfig.compilingFSharpCore then + if + not tcConfig.deterministic + && not tcConfig.isInteractive + && not tcConfig.compilingFSharpCore + then CheckMultipleInputsUsingGraphMode( ctok, checkForErrors, From 4de861516849fc4133304763017507a6513d0762 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:33:25 +0200 Subject: [PATCH 5/5] handle script compilation --- .../GraphChecking/DependencyResolution.fs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index 817588e700..91292a8245 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -207,11 +207,6 @@ let mkGraph (filePairs: FilePairMap) (files: FileInProject array) : Graph Array.tryFindIndexBack (fun f -> f.IsScript) |> Option.map (fun idx -> idx + 1) |> Option.defaultValue 0 + + let sequentialPartForScriptCompilation = files + |> Array.take scriptCompilationLength + |> Array.map (fun file -> file.Idx, [| if file.Idx > 0 then file.Idx - 1 |]) + + let normalPart = + files + |> Array.skip scriptCompilationLength |> Array.Parallel.map (fun file -> file.Idx, findDependencies file) - |> readOnlyDict + + let graph = Array.append sequentialPartForScriptCompilation normalPart |> readOnlyDict let trie = trie |> Array.last |> snd