diff --git a/src/Compiler/Facilities/BuildGraph.fs b/src/Compiler/Facilities/BuildGraph.fs index b8ee50564c1..5f9f80b7f14 100644 --- a/src/Compiler/Facilities/BuildGraph.fs +++ b/src/Compiler/Facilities/BuildGraph.fs @@ -228,14 +228,14 @@ module GraphNode = | None -> () [] -type GraphNode<'T>(retryCompute: bool, computation: NodeCode<'T>) = +type GraphNode<'T> private (retryCompute: bool, computation: NodeCode<'T>, cachedResult: Task<'T>, cachedResultNode: NodeCode<'T>) = let gate = obj () let mutable computation = computation let mutable requestCount = 0 - let mutable cachedResult: Task<'T> = Unchecked.defaultof<_> - let mutable cachedResultNode: NodeCode<'T> = Unchecked.defaultof<_> + let mutable cachedResult: Task<'T> = cachedResult + let mutable cachedResultNode: NodeCode<'T> = cachedResultNode let isCachedResultNodeNotNull () = not (obj.ReferenceEquals(cachedResultNode, null)) @@ -429,4 +429,9 @@ type GraphNode<'T>(retryCompute: bool, computation: NodeCode<'T>) = member _.IsComputing = requestCount > 0 + static member FromResult(result: 'T) = + let nodeResult = node.Return result + GraphNode(true, nodeResult, Task.FromResult(result), nodeResult) + + new(retryCompute: bool, computation) = GraphNode(retryCompute, computation, Unchecked.defaultof<_>, Unchecked.defaultof<_>) new(computation) = GraphNode(retryCompute = true, computation = computation) diff --git a/src/Compiler/Facilities/BuildGraph.fsi b/src/Compiler/Facilities/BuildGraph.fsi index b94c6e30b26..5afb43e5f08 100644 --- a/src/Compiler/Facilities/BuildGraph.fsi +++ b/src/Compiler/Facilities/BuildGraph.fsi @@ -102,6 +102,9 @@ type internal GraphNode<'T> = /// By default, 'retryCompute' is 'true'. new: computation: NodeCode<'T> -> GraphNode<'T> + /// Creates a GraphNode with given result already cached. + static member FromResult: 'T -> GraphNode<'T> + /// Return NodeCode which, when executed, will get the value of the computation if already computed, or /// await an existing in-progress computation for the node if one exists, or else will synchronously /// start the computation on the current thread. diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index bfc2d69c759..be8c4fae0ce 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -275,7 +275,7 @@ type TcInfoNode = static member FromState(state: TcInfoState) = let tcInfo = state.TcInfo let tcInfoExtras = state.TcInfoExtras - TcInfoNode(GraphNode(node.Return tcInfo), GraphNode(node.Return (tcInfo, defaultArg tcInfoExtras emptyTcInfoExtras))) + TcInfoNode(GraphNode.FromResult tcInfo, GraphNode.FromResult (tcInfo, defaultArg tcInfoExtras emptyTcInfoExtras)) /// Bound model of an underlying syntax and typed tree. [] @@ -1098,7 +1098,7 @@ module IncrementalBuilderStateHelpers = | ValueSome(boundModel) when initialState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> let newBoundModel = boundModel.ClearTcInfoExtras() { state with - boundModels = state.boundModels.SetItem(slot, GraphNode(node.Return newBoundModel)) + boundModels = state.boundModels.SetItem(slot, GraphNode.FromResult newBoundModel) stampedFileNames = state.stampedFileNames.SetItem(slot, stamp) } | _ -> @@ -1165,7 +1165,7 @@ type IncrementalBuilderState with let referencedAssemblies = initialState.referencedAssemblies let cache = TimeStampCache(defaultTimeStamp) - let initialBoundModel = GraphNode(node.Return initialBoundModel) + let initialBoundModel = GraphNode.FromResult initialBoundModel let boundModels = ImmutableArrayBuilder.create fileNames.Length for slot = 0 to fileNames.Length - 1 do diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 8b68495389b..cf5487e25df 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -410,7 +410,7 @@ type BackgroundCompiler let createBuilderNode (options, userOpName, ct: CancellationToken) = lock gate (fun () -> if ct.IsCancellationRequested then - GraphNode(node.Return(None, [||])) + GraphNode.FromResult(None, [||]) else let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) diff --git a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs index ae2aeff2071..a33f9020897 100644 --- a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs +++ b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs @@ -272,3 +272,10 @@ module BuildGraphTests = tasks |> Seq.iter (fun x -> try x.Wait(1000) |> ignore with | :? TimeoutException -> reraise() | _ -> ()) + + [] + let ``GraphNode created from an already computed result will return it in tryPeekValue`` () = + let graphNode = GraphNode.FromResult 1 + + Assert.shouldBeTrue graphNode.HasValue + Assert.shouldBe (ValueSome 1) (graphNode.TryPeekValue())