From e44ae1daecf51d423f0ee187ef97ef6cf6c6dd9a Mon Sep 17 00:00:00 2001 From: Ella Hathaway <67609881+ellahathaway@users.noreply.github.com> Date: Tue, 19 Nov 2024 05:36:56 -0800 Subject: [PATCH 01/13] Add -sign to build.sh (#18024) --- eng/build.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eng/build.sh b/eng/build.sh index c4abb23f6f1..e6e27732c8f 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -20,6 +20,7 @@ usage() echo " --rebuild Rebuild all projects" echo " --pack Build nuget packages" echo " --publish Publish build artifacts" + echo " --sign Sign build artifacts" echo " --help Print help and exit" echo "" echo "Test actions:" @@ -58,6 +59,7 @@ build=false rebuild=false pack=false publish=false +sign=false test_core_clr=false test_compilercomponent_tests=false test_benchmarks=false @@ -129,6 +131,9 @@ while [[ $# > 0 ]]; do --publish) publish=true ;; + --sign) + sign=true + ;; --testcoreclr|--test|-t) test_core_clr=true ;; @@ -297,6 +302,7 @@ function BuildSolution { /p:Rebuild=$rebuild \ /p:Pack=$pack \ /p:Publish=$publish \ + /p:Sign=$sign \ /p:UseRoslynAnalyzers=$enable_analyzers \ /p:ContinuousIntegrationBuild=$ci \ /p:QuietRestore=$quiet_restore \ From 373fb23c5a6a7a00ddee9d142150cb18b2bd51d5 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 21 Nov 2024 15:36:02 +0100 Subject: [PATCH 02/13] Fix singing problems (#18040) --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 6d23934c7ed..b866e3068b4 100644 --- a/global.json +++ b/global.json @@ -11,7 +11,7 @@ "Microsoft.VisualStudio.Component.FSharp" ] }, - "xcopy-msbuild": "17.8.5" + "xcopy-msbuild": "17.12.0" }, "native-tools": { "perl": "5.38.2.2" From 95cff6b6ee463f21e03a5a4c60fd2fb2e992fdf7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 22 Nov 2024 13:51:01 +0100 Subject: [PATCH 03/13] Skip flaky test (#18051) --- .../FSharpChecker/TransparentCompiler.fs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index b05096303d4..6dcd6463cd5 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -51,7 +51,7 @@ let internal recordAllEvents groupBy = let getFileNameKey (_l, (f: string, _p), _) = Path.GetFileName f // TODO: currently the label for DependecyGraph cache is $"%d{fileSnapshots.Length} files ending with {lastFile}" -let getDependecyGraphKey (_l, _, _) = failwith "not implemented" +let getDependecyGraphKey (_l, _, _) = failwith "not implemented" let internal recordEvents groupBy = let observe, getEvents = recordAllEvents groupBy @@ -293,7 +293,7 @@ let ``We don't check files that are not depended on`` () = let observe, check = recordEvents getFileNameKey - ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { withChecker (observe _.TcIntermediate) updateFile "First" updatePublicSurface checkFile "Last" expectOk @@ -448,7 +448,7 @@ let fuzzingTest seed (project: SyntheticProject) = task { let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) let checker = builder.Checker - + // Force creation and caching of options do! SaveAndCheckProject project checker false |> Async.Ignore @@ -600,7 +600,7 @@ let fuzzingTest seed (project: SyntheticProject) = task { builder.DeleteProjectDir() } -[] +[] [] [] [] @@ -712,7 +712,7 @@ let ``What happens if bootstrapInfoStatic needs to be recomputed`` _ = giraffeProject.Workflow { updateFile "Helpers" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) checkFile "EndpointRouting" expectOk - withChecker (fun checker -> + withChecker (fun checker -> async { checker.Caches.BootstrapInfoStatic.Clear() checker.Caches.BootstrapInfo.Clear() @@ -722,7 +722,7 @@ let ``What happens if bootstrapInfoStatic needs to be recomputed`` _ = }) updateFile "Core" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) checkFile "EndpointRouting" expectOk - } + } module ParsedInputHashing = @@ -776,7 +776,7 @@ let ``TypeCheck last file in project with transparent compiler`` useTransparentC let responseFile = FileInfo responseFile let syntheticProject = mkSyntheticProjectForResponseFile responseFile - let workflow = + let workflow = ProjectWorkflowBuilder( syntheticProject, isExistingProject = true, @@ -793,7 +793,7 @@ let ``TypeCheck last file in project with transparent compiler`` useTransparentC | Some lastFile -> workflow { - clearCache + clearCache checkFile lastFile expectOk } @@ -803,13 +803,13 @@ let ``LoadClosure for script is computed once`` () = sourceFile "First" []) let observe, getEvents = recordAllEvents getFileNameKey - + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { withChecker (observe _.ScriptClosure) checkFile "First" expectOk } |> ignore - + Assert.Empty(getEvents()) [] @@ -819,7 +819,7 @@ let ``LoadClosure for script is recomputed after changes`` () = sourceFile "First" []) let observe, check = recordEvents getFileNameKey - + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { withChecker (observe _.ScriptClosure) checkFile "First" expectOk @@ -830,12 +830,12 @@ let ``LoadClosure for script is recomputed after changes`` () = } |> ignore check (fileName "First") [Weakened; Requested; Started; Finished; Weakened; Requested; Started; Finished] - + [] let ``TryGetRecentCheckResultsForFile returns None before first call to ParseAndCheckFileInProject`` () = let project = SyntheticProject.Create( sourceFile "First" []) - + ProjectWorkflowBuilder(project) { clearCache tryGetRecentCheckResults "First" expectNone @@ -845,7 +845,7 @@ let ``TryGetRecentCheckResultsForFile returns None before first call to ParseAnd let ``TryGetRecentCheckResultsForFile returns result after first call to ParseAndCheckFileInProject`` () = let project = SyntheticProject.Create( sourceFile "First" [] ) - + ProjectWorkflowBuilder(project) { tryGetRecentCheckResults "First" expectSome } |> ignore @@ -854,7 +854,7 @@ let ``TryGetRecentCheckResultsForFile returns result after first call to ParseAn let ``TryGetRecentCheckResultsForFile returns no result after edit`` () = let project = SyntheticProject.Create( sourceFile "First" []) - + ProjectWorkflowBuilder(project) { tryGetRecentCheckResults "First" expectSome updateFile "First" updatePublicSurface @@ -862,13 +862,13 @@ let ``TryGetRecentCheckResultsForFile returns no result after edit`` () = checkFile "First" expectOk tryGetRecentCheckResults "First" expectSome } |> ignore - + [] let ``TryGetRecentCheckResultsForFile returns result after edit of other file`` () = let project = SyntheticProject.Create( sourceFile "First" [], sourceFile "Second" ["First"]) - + ProjectWorkflowBuilder(project) { tryGetRecentCheckResults "First" expectSome tryGetRecentCheckResults "Second" expectSome @@ -901,7 +901,7 @@ let ``Unused warning should still produce after parse warning`` useTransparentCo // There should be parse warning because of the space in the file name: // warning FS0221: The declarations in this file will be placed in an implicit module 'As 01' based on the file name 'As 01.fs'. // However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file. - + let project = { SyntheticProject.Create( { sourceFile "As 01" [] with @@ -914,7 +914,7 @@ do printfn "Hello from F#" """ SignatureFile = No - + }) with AutoAddModules = false OtherOptions = [ @@ -939,7 +939,7 @@ printfn "Hello from F#" checkResults.Diagnostics |> Array.exists (fun diag -> diag.Severity = FSharpDiagnosticSeverity.Warning && diag.ErrorNumber = 1182) Assert.True(hasCheckWarning, "Expected post inference warning FS1182") - + ProjectWorkflowBuilder(project, useTransparentCompiler = useTransparentCompiler) { checkFile "As 01" expectTwoWarnings } @@ -959,12 +959,12 @@ type private LoadClosureTestShim(currentFileSystem: IFileSystem) = let mutable bDidUpdate = false let asStream (v:string) = new MemoryStream(System.Text.Encoding.UTF8.GetBytes v) let knownFiles = set [ "a.fsx"; "b.fsx"; "c.fsx" ] - + member val aFsx = "#load \"b.fsx\"" member val bFsxInitial = "" member val bFsxUpdate = "#load \"c.fsx\"" member val cFsx = "" - + member x.DocumentSource (fileName: string) = async { if not (knownFiles.Contains fileName) then @@ -978,7 +978,7 @@ type private LoadClosureTestShim(currentFileSystem: IFileSystem) = } member x.UpdateB () = bDidUpdate <- true - + override _.FileExistsShim(path) = if knownFiles.Contains path then true else currentFileSystem.FileExistsShim(path) override _.GetFullPathShim(fileName) = @@ -996,7 +996,7 @@ type private LoadClosureTestShim(currentFileSystem: IFileSystem) = ) module TestsMutatingFileSystem = - + [] [] [] @@ -1010,7 +1010,7 @@ module TestsMutatingFileSystem = if System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") then None else - Some false + Some false try let checker = FSharpChecker.Create(useTransparentCompiler = useTransparentCompiler) @@ -1034,7 +1034,7 @@ module TestsMutatingFileSystem = match snd checkResults with | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) - + // Update b.fsx, it should now load c.fsx fileSystemShim.UpdateB() @@ -1081,7 +1081,7 @@ let ``Parsing with cache and without project snapshot`` () = async { let checker = FSharpChecker.Create(useTransparentCompiler = true) let fileName = "B.fs" - let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| "A.fs"; fileName; "C.fs" |] } + let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| "A.fs"; fileName; "C.fs" |] } let sourceText = SourceText.ofString """ module B @@ -1091,7 +1091,7 @@ let b : int = ExtraIdentUserNeverWroteRulezzz let! parseResult = checker.ParseFile(fileName, sourceText, parsingOptions, cache = true) Assert.False(parseResult.ParseHadErrors) Assert.True(Array.isEmpty parseResult.Diagnostics) - + let! parseAgainResult = checker.ParseFile(fileName, sourceText, parsingOptions, cache = true) Assert.False(parseAgainResult.ParseHadErrors) Assert.True(Array.isEmpty parseAgainResult.Diagnostics) From 1b15b6ce69d6a40799c368e6806d657909551223 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis Date: Fri, 22 Nov 2024 17:44:45 +0200 Subject: [PATCH 04/13] Remove support for `System.AggressiveAttributeTrimming` feature switch. (#18039) Co-authored-by: Kevin Ransom (msft) Co-authored-by: Petr --- src/FSharp.Core/FSharp.Core.fsproj | 1 - src/FSharp.Core/ILLink.LinkAttributes.xml | 179 ---------------------- 2 files changed, 180 deletions(-) delete mode 100644 src/FSharp.Core/ILLink.LinkAttributes.xml diff --git a/src/FSharp.Core/FSharp.Core.fsproj b/src/FSharp.Core/FSharp.Core.fsproj index 97a02162fa4..b0abd5048bd 100644 --- a/src/FSharp.Core/FSharp.Core.fsproj +++ b/src/FSharp.Core/FSharp.Core.fsproj @@ -64,7 +64,6 @@ Microsoft.FSharp.Core.SR FSCore.resx - diff --git a/src/FSharp.Core/ILLink.LinkAttributes.xml b/src/FSharp.Core/ILLink.LinkAttributes.xml deleted file mode 100644 index 47a3ff01f02..00000000000 --- a/src/FSharp.Core/ILLink.LinkAttributes.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 744aa02fe757e0f42edb23ab6721d36ca81a554a Mon Sep 17 00:00:00 2001 From: Petr Date: Fri, 22 Nov 2024 18:31:49 +0100 Subject: [PATCH 05/13] Update some internal guidelines (#18052) --- INTERNAL.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/INTERNAL.md b/INTERNAL.md index 6207180fd31..51cb12efd57 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -38,7 +38,7 @@ their respective branches. [VS 16.1](https://dev.azure.com/devdiv/DevDiv/_release?definitionId=1669&_a=releases) -VS 16.0 and prior were done manually +VS 16.0 and prior were done manually. ## VS Insertions as part of the build definition @@ -64,6 +64,7 @@ it's a good idea to check the previous link for any old or stalled insertions in Update the `insertTargetBranch` value at the bottom of `azure-pipelines.yml` in the appropriate release branch. E.g., when VS 17.3 snapped and switched to ask mode, [this PR](/~https://github.com/dotnet/fsharp/pull/13456/files) correctly updates the insertion target so that future builds from that F# branch will get auto-inserted to VS. ### When VS `main` is open for insertions for preview releases of VS: + 0. Disable auto-merges from `main` to **current** release branch, please make a change for the following file and create a pull request: /~https://github.com/dotnet/roslyn-tools/blob/6d7c182c46f8319d7922561e2c1586c7aadce19e/src/GitHubCreateMergePRs/config.xml#L52-L74 > You should comment out the `main -> release/devXX.X` flow until step #4 is completed (``) @@ -131,6 +132,23 @@ Since github issue filtering is currently not flexible enough, that query was ge Invoke-WebRequest -Uri "https://api.github.com/repos/dotnet/fsharp/labels?per_page=100" | ConvertFrom-Json | % { $_.name } | ? { $_.StartsWith("Area-") } | % { Write-Host -NoNewLine ('-label:"' + $_ + '" ') } ``` -## Other links +## Fix problems with the internal source mirror + +The repo is [here](https://dev.azure.com/dnceng/internal/_git/dotnet-fsharp), the CI is [here](https://dnceng.visualstudio.com/internal/_build?definitionId=499). + +If something breaks in the CI there and you want to experiment, the general workflow is the following: +1. Make a branch +2. Make a change +3. Run the build from your branch. If needed, set the "skipTests" variable to "true" - can save time at this stage. +4. Once the problem and the fix is identified, make a PR to THIS (dotnet/fsharp) repo - it will propagate to the internal mirror just afterwards. +5. Delete all your work in the internal repo. + +**DO NOT** try to push to the internal repo - this will mess up the flows. **DO NOT** create PRs to not confuse anyone. + +You need the following permissions to do the above investigations: +- "Generic contribute" +- "Create branch" +- "Queue builds" +- "Edit queue build configuration" -[Internal source mirror](https://dev.azure.com/dnceng/internal/_git/dotnet-fsharp). +If anything, reach out to the "First Responders" team. \ No newline at end of file From 30d9ea7cd6e8a222e3561c83a854c1632240b7fe Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:09:29 +0100 Subject: [PATCH 06/13] simplify temp dirs in service (#18046) Co-authored-by: Kevin Ransom (msft) --- tests/FSharp.Compiler.Service.Tests/Common.fs | 73 ++++--------------- .../ExprTests.fs | 59 ++++++--------- .../GeneratedCodeSymbolsTests.fs | 12 +-- .../ScriptOptionsTests.fs | 6 +- tests/FSharp.Test.Utilities/CompilerAssert.fs | 17 ++--- tests/FSharp.Test.Utilities/TestFramework.fs | 6 +- 6 files changed, 54 insertions(+), 119 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/Common.fs b/tests/FSharp.Compiler.Service.Tests/Common.fs index 986e2be5132..18276ae39a9 100644 --- a/tests/FSharp.Compiler.Service.Tests/Common.fs +++ b/tests/FSharp.Compiler.Service.Tests/Common.fs @@ -473,63 +473,16 @@ let assertRange Assert.Equal(Position.mkPos expectedStartLine expectedStartColumn, actualRange.Start) Assert.Equal(Position.mkPos expectedEndLine expectedEndColumn, actualRange.End) -[] -module TempDirUtils = - let getTempPath dir = - Path.Combine(tempDirectoryOfThisTestRun.Value.FullName, dir) - - /// Returns the file name part of a temp file name created with tryCreateTemporaryFileName () - /// and an added process id and thread id to ensure uniqueness between threads. - let getTempFileName() = - let tempFileName = getTemporaryFileName () - try - let tempFile, tempExt = Path.GetFileNameWithoutExtension tempFileName, Path.GetExtension tempFileName - let procId, threadId = Process.GetCurrentProcess().Id, Thread.CurrentThread.ManagedThreadId - String.concat "" [tempFile; "_"; string procId; "_"; string threadId; tempExt] // ext includes dot - finally - try - FileSystem.FileDeleteShim tempFileName - with _ -> () - - /// Given just a file name, returns it with changed extension located in %TEMP%\ExprTests - let getTempFilePathChangeExt dir tmp ext = - Path.Combine(getTempPath dir, Path.ChangeExtension(tmp, ext)) - - /// If it doesn't exists, create a folder 'ExprTests' in local user's %TEMP% folder - let createTempDir dirName = - let tempPath = getTempPath dirName - do - if Directory.Exists tempPath then () - else Directory.CreateDirectory tempPath |> ignore - - /// Clean up after a test is run. If you need to inspect the create *.fs files, change this function to do nothing, or just break here. - let cleanupTempFiles dirName files = - { new IDisposable with - member _.Dispose() = - for fileName in files do - try - // cleanup: only the source file is written to the temp dir. - FileSystem.FileDeleteShim fileName - with _ -> () - - try - // remove the dir when empty - let tempPath = getTempPath dirName - if Directory.GetFiles tempPath |> Array.isEmpty then - Directory.Delete tempPath - with _ -> () } - - let createProjectOptions dirName fileSources extraArgs = - let fileNames = fileSources |> List.map (fun _ -> getTempFileName()) - let temp2 = getTempFileName() - let fileNames = fileNames |> List.map (fun temp1 -> getTempFilePathChangeExt dirName temp1 ".fs") - let dllName = getTempFilePathChangeExt dirName temp2 ".dll" - let projFileName = getTempFilePathChangeExt dirName temp2 ".fsproj" - - createTempDir dirName - for fileSource: string, fileName in List.zip fileSources fileNames do - FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) - let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, []) |] - let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames |> List.toArray } - - cleanupTempFiles dirName (fileNames @ [dllName; projFileName]), options +let createProjectOptions fileSources extraArgs = + let tempDir = createTemporaryDirectory() + let temp2 = getTemporaryFileNameInDirectory tempDir + let dllName = changeExtension temp2 ".dll" + let projFileName = changeExtension temp2 ".fsproj" + + let sourceFiles = + [| for fileSource: string in fileSources do + let fileName = changeExtension (getTemporaryFileNameInDirectory tempDir) ".fs" + FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) + fileName |] + let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, []) |] + { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = sourceFiles } diff --git a/tests/FSharp.Compiler.Service.Tests/ExprTests.fs b/tests/FSharp.Compiler.Service.Tests/ExprTests.fs index 251e1fc3a91..7a594db1e38 100644 --- a/tests/FSharp.Compiler.Service.Tests/ExprTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ExprTests.fs @@ -13,6 +13,8 @@ open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Symbols open FSharp.Compiler.Symbols.FSharpExprPatterns +open TestFramework + type FSharpCore = | FC45 | FC46 @@ -593,7 +595,7 @@ let testMutableVar = mutableVar 1 let testMutableConst = mutableConst () """ - let createOptionsWithArgs args = createProjectOptions dirName [ fileSource1; fileSource2 ] args + let createOptionsWithArgs args = createProjectOptions [ fileSource1; fileSource2 ] args let createOptions() = createOptionsWithArgs [] @@ -659,8 +661,7 @@ let test{0}ToStringOperator (e1:{1}) = string e1 /// This test is run in unison with its optimized counterpart below [] let ``Test Unoptimized Declarations Project1`` () = - let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] - use _holder = cleanup + let options = Project1.createOptionsWithArgs [ "--langversion:preview" ] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -798,8 +799,7 @@ let ``Test Unoptimized Declarations Project1`` () = [] let ``Test Optimized Declarations Project1`` () = - let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] - use _holder = cleanup + let options = Project1.createOptionsWithArgs [ "--langversion:preview" ] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -938,15 +938,13 @@ let ``Test Optimized Declarations Project1`` () = let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimized = - let tempFileName = getTempFileName() - let filePath = getTempFilePathChangeExt dirName tempFileName ".fs" - let dllPath =getTempFilePathChangeExt dirName tempFileName ".dll" - let projFilePath = getTempFilePathChangeExt dirName tempFileName ".fsproj" + let tempFileName = getTemporaryFileName() + let filePath = changeExtension tempFileName ".fs" + let dllPath =changeExtension tempFileName ".dll" + let projFilePath = changeExtension tempFileName ".fsproj" let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=true) begin - use _cleanup = cleanupTempFiles dirName [filePath; dllPath; projFilePath] - createTempDir dirName let source = String.Format(Project1.operatorTests, dnName, fsName) let replace (s:string) r = s.Replace("let " + r, "// let " + r) let fileSource = excludedTests |> List.fold replace source @@ -3128,7 +3126,7 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = """ - let createOptions() = createProjectOptions dirName [fileSource1] [] + let createOptions() = createProjectOptions [fileSource1] [] #if !NETFRAMEWORK && DEBUG [] @@ -3136,8 +3134,7 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = [] #endif let ``Test expressions of declarations stress big expressions`` () = - let cleanup, options = ProjectStressBigExpressions.createOptions() - use _holder = cleanup + let options = ProjectStressBigExpressions.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3155,8 +3152,7 @@ let ``Test expressions of declarations stress big expressions`` () = [] #endif let ``Test expressions of optimized declarations stress big expressions`` () = - let cleanup, options = ProjectStressBigExpressions.createOptions() - use _holder = cleanup + let options = ProjectStressBigExpressions.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3211,12 +3207,11 @@ let f7() = callXY (C()) (D()) let f8() = callXY (D()) (C()) """ - let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] + let createOptions() = createProjectOptions [fileSource1] ["--langversion:7.0"] [] let ``Test ProjectForWitnesses1`` () = - let cleanup, options = ProjectForWitnesses1.createOptions() - use _holder = cleanup + let options = ProjectForWitnesses1.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3259,8 +3254,7 @@ let ``Test ProjectForWitnesses1`` () = [] let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` () = - let cleanup, options = ProjectForWitnesses1.createOptions() - use _holder = cleanup + let options = ProjectForWitnesses1.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3335,12 +3329,11 @@ type MyNumberWrapper = { MyNumber: MyNumber } """ - let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] + let createOptions() = createProjectOptions [fileSource1] ["--langversion:7.0"] [] let ``Test ProjectForWitnesses2`` () = - let cleanup, options = ProjectForWitnesses2.createOptions() - use _holder = cleanup + let options = ProjectForWitnesses2.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3391,12 +3384,11 @@ let s2 = sign p1 """ - let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] + let createOptions() = createProjectOptions [fileSource1] ["--langversion:7.0"] [] let ``Test ProjectForWitnesses3`` () = - let cleanup, options = createProjectOptions dirName [ ProjectForWitnesses3.fileSource1 ] ["--langversion:7.0"] - use _holder = cleanup + let options = createProjectOptions [ ProjectForWitnesses3.fileSource1 ] ["--langversion:7.0"] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3426,8 +3418,7 @@ let ``Test ProjectForWitnesses3`` () = [] let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` () = - let cleanup, options = ProjectForWitnesses3.createOptions() - use _holder = cleanup + let options = ProjectForWitnesses3.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3485,12 +3476,11 @@ let isNullQuoted (ts : 't[]) = """ - let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] + let createOptions() = createProjectOptions [fileSource1] ["--langversion:7.0"] [] let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` () = - let cleanup, options = ProjectForWitnesses4.createOptions() - use _holder = cleanup + let options = ProjectForWitnesses4.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -3523,12 +3513,11 @@ module N.M let rec f = new System.EventHandler(fun _ _ -> f.Invoke(null,null)) """ - let createOptions() = createProjectOptions dirName [fileSource1] [] + let createOptions() = createProjectOptions [fileSource1] [] [] let ``Test NoWarn HashDirective`` () = - let cleanup, options = ProjectForNoWarnHashDirective.createOptions() - use _holder = cleanup + let options = ProjectForNoWarnHashDirective.createOptions() let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate diff --git a/tests/FSharp.Compiler.Service.Tests/GeneratedCodeSymbolsTests.fs b/tests/FSharp.Compiler.Service.Tests/GeneratedCodeSymbolsTests.fs index 66b9782fb29..5fac752952c 100644 --- a/tests/FSharp.Compiler.Service.Tests/GeneratedCodeSymbolsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/GeneratedCodeSymbolsTests.fs @@ -5,9 +5,6 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Symbols -[] -let dirName = "GeneratedCodeSymbolsTests" - [] let ``IsUnionCaseTester for Is* member in a class`` () = let source = """ @@ -16,8 +13,7 @@ module Lib type T () = member x.IsM = 1 """ - let cleanup, options = createProjectOptions dirName [ source ] [ "--langversion:preview" ] - use _holder = cleanup + let options = createProjectOptions [ source ] [ "--langversion:preview" ] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=false) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -46,8 +42,7 @@ module Lib type T = A | B """ - let cleanup, options = createProjectOptions dirName [ source ] [ "--langversion:preview" ] - use _holder = cleanup + let options = createProjectOptions [ source ] [ "--langversion:preview" ] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=false) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -80,8 +75,7 @@ type T = member x.IsC with get () = false """ - let cleanup, options = createProjectOptions dirName [ source ] [ "--langversion:preview" ] - use _holder = cleanup + let options = createProjectOptions [ source ] [ "--langversion:preview" ] let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=false) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate diff --git a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs index 2d197af0398..328272defb8 100644 --- a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs @@ -41,9 +41,9 @@ let ``can generate options for different frameworks regardless of execution envi [] [] let ``can resolve nuget packages to right target framework for different frameworks regardless of execution environment``(flag) = - let path = DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)).FullName - let file = (getTemporaryFileNameInDirectory path) + ".fsx" - let scriptFullPath = Path.Combine(path, file) + let dir = DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)) + let file = (getTemporaryFileNameInDirectory dir) + ".fsx" + let scriptFullPath = Path.Combine(dir.FullName, file) let scriptSource = """ #r "nuget: FSharp.Data, 3.3.3" open System diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 202f7704a9d..5253ef16934 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -444,7 +444,7 @@ module CompilerAssertHelpers = let name = match nameOpt with | Some name -> name - | _ -> getTemporaryFileNameInDirectory outputDirectory.FullName + | _ -> getTemporaryFileNameInDirectory outputDirectory let outputFilePath = Path.ChangeExtension (Path.Combine(outputDirectory.FullName, name), if isExe then ".exe" else ".dll") let sources = @@ -508,7 +508,7 @@ module CompilerAssertHelpers = let compile isExe options (source:SourceCodeFileKind) f = let outputFilePath = Path.ChangeExtension (getTemporaryFileName (), if isExe then ".exe" else ".dll") - let tempDir = Path.GetDirectoryName outputFilePath + let tempDir = Directory.GetParent outputFilePath let sourceFile = match source.GetSourceText with @@ -521,10 +521,7 @@ module CompilerAssertHelpers = // On Disk file source - try - f (rawCompile outputFilePath isExe options TargetFramework.Current [sourceFile]) - finally - try Directory.Delete(tempDir, true) with | _ -> () + f (rawCompile outputFilePath isExe options TargetFramework.Current [sourceFile]) let rec compileCompilationAux outputDirectory ignoreWarnings (cmpl: Compilation) : (FSharpDiagnostic[] * exn option * string) * string list = @@ -548,7 +545,7 @@ module CompilerAssertHelpers = res, (deps @ deps2) - and evaluateReferences (outputPath:DirectoryInfo) ignoreWarnings (cmpl: Compilation) : string[] * string list = + and evaluateReferences (outputDir:DirectoryInfo) ignoreWarnings (cmpl: Compilation) : string[] * string list = match cmpl with | Compilation(_, _, _, _, cmpls, _, _) -> let compiledRefs = @@ -556,13 +553,13 @@ module CompilerAssertHelpers = |> List.map (fun cmpl -> match cmpl with | CompilationReference (cmpl, staticLink) -> - compileCompilationAux outputPath ignoreWarnings cmpl, staticLink + compileCompilationAux outputDir ignoreWarnings cmpl, staticLink | TestCompilationReference (cmpl) -> let fileName = match cmpl with | TestCompilation.CSharp c when not (String.IsNullOrWhiteSpace c.AssemblyName) -> c.AssemblyName - | _ -> getTemporaryFileNameInDirectory outputPath.FullName - let tmp = Path.Combine(outputPath.FullName, Path.ChangeExtension(fileName, ".dll")) + | _ -> getTemporaryFileNameInDirectory outputDir + let tmp = Path.Combine(outputDir.FullName, Path.ChangeExtension(fileName, ".dll")) cmpl.EmitAsFile tmp (([||], None, tmp), []), false) diff --git a/tests/FSharp.Test.Utilities/TestFramework.fs b/tests/FSharp.Test.Utilities/TestFramework.fs index ada5bf3363e..f50452eff9e 100644 --- a/tests/FSharp.Test.Utilities/TestFramework.fs +++ b/tests/FSharp.Test.Utilities/TestFramework.fs @@ -29,8 +29,10 @@ let createTemporaryDirectory () = let getTemporaryFileName () = createTemporaryDirectory().FullName ++ getShortId() -let getTemporaryFileNameInDirectory (directory: string) = - directory ++ getShortId() +let changeExtension path extension = Path.ChangeExtension(path, extension) + +let getTemporaryFileNameInDirectory (directory: DirectoryInfo) = + directory.FullName ++ getShortId() // Well, this function is AI generated. let rec copyDirectory (sourceDir: string) (destinationDir: string) (recursive: bool) = From af90690eb8526776cd48070fbf9e1925b91424c3 Mon Sep 17 00:00:00 2001 From: "Kevin Ransom (msft)" Date: Mon, 25 Nov 2024 09:29:06 -0800 Subject: [PATCH 07/13] Add Contributing.md (#18043) * contributing * Update CONTRIBUTING.md * Update CONTRIBUTING.md Co-authored-by: Tomas Grosup * Update CONTRIBUTING.md Co-authored-by: Vlad Zarytovskii * Update CONTRIBUTING.md Co-authored-by: Petr * Update CONTRIBUTING.md --------- Co-authored-by: Tomas Grosup Co-authored-by: Vlad Zarytovskii Co-authored-by: Petr --- CONTRIBUTING.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..b8c7c63108a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,106 @@ +# Contributing to F# + +One of the easiest ways to contribute is to participate in discussions on GitHub issues. You can also contribute by submitting pull requests with code changes. + +## General feedback and discussions? + +Start a [discussion](/~https://github.com/dotnet/fsharp/discussions) on the [repository issue tracker](/~https://github.com/dotnet/fsharp/issues). + +## Bugs and feature requests? + +❗ **IMPORTANT: If you want to report a security-related issue, please see the `Reporting security issues and bugs` section below.** + +Before reporting a new issue, try to find an existing issue if one already exists. If it already exists, upvote (👍) it. Also, consider adding a comment with your unique scenarios and requirements related to that issue. Upvotes and clear details on the issue's impact help us prioritize the most important issues to be worked on sooner rather than later. If you can't find one, that's okay, we'd rather get a duplicate report than none. + +If you can't find an existing issue, log a new issue in this GitHub repository. + +## Creating Issues + +- **DO** use a descriptive title that identifies the issue to be addressed or the requested feature. For example, when describing an issue where the compiler is not behaving as expected, write your bug title in terms of what the compiler should do rather than what it is doing – “F# compiler should report FS1234 when Xyz is used in Abcd.” +- **DO** specify a detailed description of the issue or requested feature. +- **DO** provide the following for bug reports + - Describe the expected behavior and the actual behavior. If it is not self-evident such as in the case of a crash, provide an explanation for why the expected behavior is expected. + - Provide an example with source code / projects that reproduce the issue. + - Specify any relevant exception messages and stack traces. +- **DO** subscribe to notifications for the created issue in case there are any follow up questions. + +## Reporting security issues and bugs + +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/security/ff852094.aspx). + +## Writing Code + +### Finding an issue to work on + + Over the years we've seen many PRs targeting areas, which we didn't plan to expand further at the time. In many of these these cases we had to say `no` to those PRs and close them. That, obviously, is not a great outcome for us. And it's especially bad for the contributor, as they've spent a lot of effort preparing the change. + To resolve this problem, we've decided to separate a bucket of issues, which would be great candidates for community members to contribute to. We mark these issues with the `help wanted` label. [help wanted](/~https://github.com/dotnet/fsharp/labels/help%20wanted) + + Within that set, we have additionally marked issues that are good candidates for first-time contributors. Here: [Good first issue](/~https://github.com/dotnet/fsharp/labels/good%20first%20issue) + + If you would like to make a contribution to an area not documented here, first open an issue with a description of the change you would like to make and the problem it solves so it can be discussed before a pull request is submitted. + +### The primary customers of the F# repository are users of the dotnet SDK, Visual Studio, Rider and Ionide. At all times their experience is paramount in our mind. + + We are very accepting of community pull requests, there are however, a set of firm considerations that we hold to when reviewing PRs and determining what to merge. These have been developed over the years to maintain the quality of the product and the experience that F# developers have when installing and upgrading the dotnet SDK and Visual Studio. + +- Does the change fix something that needs fixing, is there an issue, does the issue indicate a real problem? +- Does the change improve the readability of something that needs improvement? +- Does the change add a feature that is approved for adding? +- Does the code match or improve of the existing codebase? +- Is the performance improvement measured and can regressions be identified? +- Will our existing customers be able to without effort upgrading the **Major** release of an SDK or VS? +- Will our existing customers be able to without effort upgrading the **Minor** release of an SDK or VS? +- This change is not binary breaking (i.e. does not break VS, Rider or Ionide)? +- Does it have adequate testing? +- Do all existing tests run unmodified? + +In general answers to the above should be **Yes**. A **No** to any of them is not disqualifying of the PR, however a no answer will need an explanation and a discussion. + +There are additional considerations +- Is the risk of accepting this change High or even Medium, these really refer to how much of the existing user or codebase is impacted. How likely do we feel we are to revert the changes later. + For an acceptable PR with a high risk, we will definitely need to discuss mitigations for the risk. A decision to upgrade the SDK or VS needs to be always low risk for our customers, they have businesses to run, they don't want to have to deal with our - risky behavior. We may defer or delay risky PRs into a later release or abandon it. +- Is the change as small as possible +- Should it be chopped up into smaller, yet independently valuable and releasable to production, chunks +- Is the cost of reviewing the change worth the improvement made by the change +Again, some PR’s are too big or provide too little value to merge. + + +### Resources to help you get started + +Here are some resources to help you get started on how to contribute code or new content. + +- [Developers Guide](/~https://github.com/dotnet/fsharp/blob/main/DEVGUIDE.md) to get started on building the source code on your own. +- [Test Guide](/~https://github.com/dotnet/fsharp/blob/main/TESTGUIDE.md) how to build run and work with test cases. +- [F# compiler guide](/~https://github.com/dotnet/fsharp/blob/main/docs/index.md) +- [F# language specification](https://fsharp.org/specs/language-spec/) +- [F# language design](/~https://github.com/fsharp/fslang-design/) +- [F# language suggestions](/~https://github.com/fsharp/fslang-suggestions/) +- [help wanted](/~https://github.com/dotnet/fsharp/labels/help%20wanted) where to start + +### Submitting a pull request + +You will need to sign a [Contributor License Agreement](https://cla.dotnetfoundation.org/) when submitting your pull request. To complete the Contributor License Agreement (CLA), you will need to follow the instructions provided by the CLA bot when you send the pull request. This needs to only be done once for any .NET Foundation OSS project. + +If you don't know what a pull request is read this article: . Make sure the repository can build and all tests pass. Familiarize yourself with the project workflow and our coding conventions. + +- **DO** ensure submissions pass all Azure DevOps legs and are merge conflict free. +- **DO** submit language feature requests as issues in the [F# language](/~https://github.com/fsharp/fslang-suggestions) repos. Please note: approved in principle does not guarantee acceptance. +- **DO NOT** submit language features as PRs to this repo first, or they will likely be declined. +- **DO** submit issues for other features. This facilitates discussion of a feature separately from its implementation, and increases the acceptance rates for pull requests. +- **DO NOT** submit large code formatting changes without discussing with the team first. + +### Reviewing pull requests + +Our repository gets a high volume of pull requests and reviewing each of them is a significant time commitment. Our team priorities often force us to focus on reviewing a subset of the active pull requests at a given time. + +### Feedback + +Contributors will review your pull request and provide feedback. + +## Merging pull requests + +When your pull request has had the feedback addressed, and it has been signed off by two or more core team reviewers with commit access, and all checks are green, we will commit it. + +## Code of conduct + +See [CODE-OF-CONDUCT.md](./CODE-OF-CONDUCT.md) From 9a0cd2d7932f266f4307827edb1c9e227f9d239a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:10:31 +0000 Subject: [PATCH 08/13] [main] Update dependencies from dotnet/arcade (#17729) * Update dependencies from /~https://github.com/dotnet/arcade build 20240913.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24462.3 -> To Version 9.0.0-beta.24463.2 * Update dependencies from /~https://github.com/dotnet/arcade build 20240916.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24463.2 -> To Version 9.0.0-beta.24466.2 * Update dependencies from /~https://github.com/dotnet/arcade build 20240923.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24466.2 -> To Version 9.0.0-beta.24473.1 * Update dependencies from /~https://github.com/dotnet/arcade build 20241001.3 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24473.1 -> To Version 9.0.0-beta.24501.3 * Update dependencies from /~https://github.com/dotnet/arcade build 20241003.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24501.3 -> To Version 9.0.0-beta.24503.2 * Update dependencies from /~https://github.com/dotnet/arcade build 20241008.3 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24503.2 -> To Version 9.0.0-beta.24508.3 * Update dependencies from /~https://github.com/dotnet/arcade build 20241009.3 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24508.3 -> To Version 9.0.0-beta.24509.3 * Update dependencies from /~https://github.com/dotnet/arcade build 20241016.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24509.3 -> To Version 9.0.0-beta.24516.2 * Update dependencies from /~https://github.com/dotnet/arcade build 20241112.13 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24462.3 -> To Version 9.0.0-beta.24562.13 * Update dependencies from /~https://github.com/dotnet/arcade build 20241122.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk From Version 9.0.0-beta.24462.3 -> To Version 9.0.0-beta.24572.2 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Vlad Zarytovskii --- eng/Version.Details.xml | 8 +++--- .../steps/get-delegation-sas.yml | 11 +++++++- eng/common/internal/Tools.csproj | 25 ------------------- eng/common/sdk-task.ps1 | 2 +- eng/common/templates-official/job/job.yml | 1 + eng/common/templates/job/job.yml | 1 + eng/common/tools.ps1 | 6 ++--- global.json | 2 +- 8 files changed, 21 insertions(+), 35 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index dc43fb9cd4e..888e3f23f89 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -42,14 +42,14 @@ - + /~https://github.com/dotnet/arcade - 91b9734abbad751d575c002b30778c88d978993c + b41381d5cd633471265e9cd72e933a7048e03062 - + /~https://github.com/dotnet/arcade - 91b9734abbad751d575c002b30778c88d978993c + b41381d5cd633471265e9cd72e933a7048e03062 diff --git a/eng/common/core-templates/steps/get-delegation-sas.yml b/eng/common/core-templates/steps/get-delegation-sas.yml index d2901470a7f..9db5617ea7d 100644 --- a/eng/common/core-templates/steps/get-delegation-sas.yml +++ b/eng/common/core-templates/steps/get-delegation-sas.yml @@ -31,7 +31,16 @@ steps: # Calculate the expiration of the SAS token and convert to UTC $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads + # of correlation payloads. /~https://github.com/dotnet/dnceng/issues/3484 + $sas = "" + do { + $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to generate SAS token." + exit 1 + } + } while($sas.IndexOf('/') -ne -1) if ($LASTEXITCODE -ne 0) { Write-Error "Failed to generate SAS token." diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index db46d5582eb..32f79dfb340 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -6,31 +6,6 @@ false false - - - - - - - - - - - - - diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index aab40de3fd9..4f0546dce12 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.10.0-pre.4.0" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.12.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 3d16b41c78c..605692d2fb7 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -1,6 +1,7 @@ parameters: # Sbom related params enableSbom: true + runAsPublic: false PackageVersion: 9.0.0 BuildDropPath: '$(Build.SourcesDirectory)/artifacts' diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 07d317bf8f9..d1aeb92fcea 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -4,6 +4,7 @@ parameters: componentGovernanceIgnoreDirectories: '' # Sbom related params enableSbom: true + runAsPublic: false PackageVersion: 9.0.0 BuildDropPath: '$(Build.SourcesDirectory)/artifacts' diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 9574f4eb9df..aa94fb17459 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -383,8 +383,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.10.0-pre.4.0 - $defaultXCopyMSBuildVersion = '17.10.0-pre.4.0' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.12.0 + $defaultXCopyMSBuildVersion = '17.12.0' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -900,7 +900,7 @@ function IsWindowsPlatform() { } function Get-Darc($version) { - $darcPath = "$TempDir\darc\$(New-Guid)" + $darcPath = "$TempDir\darc\$([guid]::NewGuid())" if ($version -ne $null) { & $PSScriptRoot\darc-init.ps1 -toolpath $darcPath -darcVersion $version | Out-Host } else { diff --git a/global.json b/global.json index b866e3068b4..9a3de25e397 100644 --- a/global.json +++ b/global.json @@ -17,7 +17,7 @@ "perl": "5.38.2.2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24462.3", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24572.2", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23255.2" } } From 318e1c85a73428e6267781aef867f9f290b8bc18 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:24:57 +0100 Subject: [PATCH 09/13] Refactor AsyncMemoize, introduce AsyncLazy (#18002) * refactor AsyncMemoize, introduce AsyncLazy * use lock free mb in tests * remove unnecessary prop * update il baselines * rename * add requestCount name Co-authored-by: Petr Pokorny * improve as reviewed * lock for TryGet * more locks * ilverify * deallocate computation when completed * ilverify * style * typo --------- Co-authored-by: Petr Pokorny --- ...y_FSharp.Compiler.Service_Debug_net9.0.bsl | 12 +- ....Compiler.Service_Debug_netstandard2.0.bsl | 12 +- ...FSharp.Compiler.Service_Release_net9.0.bsl | 12 +- ...ompiler.Service_Release_netstandard2.0.bsl | 12 +- src/Compiler/Facilities/AsyncMemoize.fs | 686 +++++------------- src/Compiler/Facilities/AsyncMemoize.fsi | 17 +- src/Compiler/Service/TransparentCompiler.fs | 4 +- .../CompilerService/AsyncLock.fs | 26 - .../CompilerService/AsyncMemoize.fs | 340 +++++---- .../FSharp.Compiler.ComponentTests.fsproj | 1 - .../FSharpChecker/TransparentCompiler.fs | 4 - 11 files changed, 392 insertions(+), 734 deletions(-) delete mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index 527bb371db2..d59c7d2adda 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,14 +21,14 @@ [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@3510-779::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][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]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000E6][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::AddPathMapping([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, string)][offset 0x0000000B][found Char] Unexpected type on the stack. diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 164060143d0..be76953ef7d 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,18 +28,18 @@ [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@3510-779::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][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]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x00000059][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000DA][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1423-6::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$Symbols+fullName@2490-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000011][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. diff --git a/eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 7de0bcd6baf..1c6248ec60a 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,13 +21,13 @@ [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@3510-835::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x00000014][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. diff --git a/eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 21b484b426d..c358adc7976 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -28,17 +28,17 @@ [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 0x00000032][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@3510-835::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][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 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000BB][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1423-11::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000618][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-528::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$Symbols+fullName@2490-3::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000030][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x00000014][found Char] Unexpected type on the stack. diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index af1a52e5e5d..88b0b84bb8c 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -1,18 +1,101 @@ namespace Internal.Utilities.Collections open System -open System.Collections.Generic open System.Diagnostics open System.IO open System.Threading open System.Threading.Tasks +open System.Runtime.CompilerServices +open System.Runtime.ExceptionServices -open FSharp.Compiler -open FSharp.Compiler.BuildGraph -open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger open Internal.Utilities.Library -open System.Runtime.CompilerServices + +type AsyncLazyState<'t> = + | Initial of computation: Async<'t> + | Running of initialComputation: Async<'t> * work: Task<'t> * CancellationTokenSource * requestCount: int + | Completed of result: 't + | Faulted of exn + +/// Represents a computation that will execute only once but can be requested by multiple clients. +/// It keeps track of the number of requests. When all clients cancel their requests, the underlying computation will also cancel and can be restarted. +/// If cancelUnawaited is set to false, the computation will run to completion even when all requests are canceled. +type AsyncLazy<'t> private (initial: AsyncLazyState<'t>, cancelUnawaited: bool, cacheException: bool) = + + let stateUpdateSync = obj() + let mutable state = initial + + let cancelIfUnawaited () = + match state with + | Running(computation, _, cts, 0) -> + cts.Cancel() + state <- Initial computation + | _ -> () + + let afterRequest () = + match state with + | Running(computation, work, _, _) when work.IsCompleted -> + state <- + try + Completed work.Result + with + | exn -> + if cacheException then Faulted exn else Initial computation + | Running(c, work, cts, count) -> + state <- Running(c, work, cts, count - 1) + if cancelUnawaited then cancelIfUnawaited () + | _ -> () // Nothing more to do if another request already transitioned the state. + + let detachable (work: Task<'t>) = + async { + try + let! ct = Async.CancellationToken + let options = TaskContinuationOptions.ExecuteSynchronously + try + return! + // Using ContinueWith with a CancellationToken allows detaching from the running 'work' task. + // This ensures the lazy 'work' and its awaiting requests can be independently managed + // by separate CancellationTokenSources, enabling individual cancellation. + // Essentially, if this async computation is canceled, it won't wait for the 'work' to complete + // but will immediately proceed to the finally block. + work.ContinueWith((fun (t: Task<_>) -> t.Result), ct, options, TaskScheduler.Current) + |> Async.AwaitTask + // Cancellation check before entering the `with` ensures TaskCanceledException coming from the ContinueWith task will never be raised here. + // The cancellation continuation will always be called in case of cancellation. + with exn -> return raise exn + finally + lock stateUpdateSync afterRequest + } + + let request () = + match state with + | Initial computation -> + let cts = new CancellationTokenSource() + let work = Async.StartAsTask(computation, cancellationToken = cts.Token) + state <- Running (computation, work, cts, 1) + detachable work + | Running (c, work, cts, count) -> + state <- Running (c, work, cts, count + 1) + detachable work + | Completed result -> + async { return result } + | Faulted exn -> + async { return raise exn } + + // computation will deallocate after state transition to Completed ot Faulted. + new (computation, ?cancelUnawaited: bool, ?cacheException) = + AsyncLazy(Initial computation, defaultArg cancelUnawaited true, defaultArg cacheException false) + + member _.Request() = lock stateUpdateSync request + + member _.CancelIfUnawaited() = lock stateUpdateSync cancelIfUnawaited + + member _.State = state + + member this.TryResult = + match state with + | Completed result -> Some result + | _ -> None [] module internal Utils = @@ -29,52 +112,6 @@ module internal Utils = $"{dir}{Path.GetFileName path}" - let replayDiagnostics (logger: DiagnosticsLogger) = Seq.iter ((<|) logger.DiagnosticSink) - - [] - let (|TaskCancelled|_|) (ex: exn) = - match ex with - | :? System.Threading.Tasks.TaskCanceledException as tce -> ValueSome tce - //| :? System.AggregateException as ae -> - // if ae.InnerExceptions |> Seq.forall (fun e -> e :? System.Threading.Tasks.TaskCanceledException) then - // ae.InnerExceptions |> Seq.tryHead |> Option.map (fun e -> e :?> System.Threading.Tasks.TaskCanceledException) - // else - // None - | _ -> ValueNone - -type internal StateUpdate<'TValue> = - | CancelRequest - | OriginatorCanceled - | JobCompleted of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list - | JobFailed of exn * (PhasedDiagnostic * FSharpDiagnosticSeverity) list - -type internal MemoizeReply<'TValue> = - | New of CancellationToken - | Existing of Task<'TValue> - -type internal MemoizeRequest<'TValue> = GetOrCompute of Async<'TValue> * CancellationToken - -[] -type internal Job<'TValue> = - | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime * ResizeArray - | Completed of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list - | Canceled of DateTime - | Failed of DateTime * exn // TODO: probably we don't need to keep this - - member this.DebuggerDisplay = - match this with - | Running(_, cts, _, ts, _) -> - let cancellation = - if cts.IsCancellationRequested then - " ! Cancellation Requested" - else - "" - - $"Running since {ts.ToShortTimeString()}{cancellation}" - | Completed(value, diags) -> $"Completed {value}" + (if diags.Length > 0 then $" ({diags.Length})" else "") - | Canceled _ -> "Canceled" - | Failed(_, ex) -> $"Failed {ex}" - type internal JobEvent = | Requested | Started @@ -87,6 +124,7 @@ type internal JobEvent = | Strengthened | Failed | Cleared + static member AllEvents = [Requested; Started; Restarted; Finished; Canceled; Evicted; Collected; Weakened; Strengthened; Failed; Cleared] type internal ICacheKey<'TKey, 'TVersion> = abstract member GetKey: unit -> 'TKey @@ -111,40 +149,7 @@ type private KeyData<'TKey, 'TVersion> = Version: 'TVersion } -type internal AsyncLock() = - - let semaphore = new SemaphoreSlim(1, 1) - - member _.Semaphore = semaphore - - member _.Do(f) = - task { - do! semaphore.WaitAsync() - - try - return! f () - finally - semaphore.Release() |> ignore - } - - interface IDisposable with - member _.Dispose() = semaphore.Dispose() - -type internal CachingDiagnosticsLogger(originalLogger: DiagnosticsLogger option) = - inherit DiagnosticsLogger($"CachingDiagnosticsLogger") - - let capturedDiagnostics = ResizeArray() - - override _.ErrorCount = - originalLogger - |> Option.map (fun x -> x.ErrorCount) - |> Option.defaultValue capturedDiagnostics.Count - - override _.DiagnosticSink(diagnostic: PhasedDiagnostic, severity: FSharpDiagnosticSeverity) = - originalLogger |> Option.iter (fun x -> x.DiagnosticSink(diagnostic, severity)) - capturedDiagnostics.Add(diagnostic, severity) - - member _.CapturedDiagnostics = capturedDiagnostics |> Seq.toList +type Job<'t> = AsyncLazy * CapturingDiagnosticsLogger> [] type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality @@ -153,342 +158,39 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T and 'TVersion:not null #endif > - (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = + (?keepStrongly, ?keepWeakly, ?name: string, ?cancelUnawaitedJobs: bool, ?cancelDuplicateRunningJobs: bool) = let name = defaultArg name "N/A" + let cancelUnawaitedJobs = defaultArg cancelUnawaitedJobs true let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs false let event = Event<_>() - let mutable errors = 0 + let eventCounts = [for j in JobEvent.AllEvents -> j, ref 0] |> dict let mutable hits = 0 - let mutable started = 0 - let mutable completed = 0 - let mutable canceled = 0 - let mutable restarted = 0 - let mutable failed = 0 - let mutable evicted = 0 - let mutable collected = 0 - let mutable strengthened = 0 - let mutable cleared = 0 - - let mutable updates_in_flight = 0 - - let mutable cancel_ct_registration_original = 0 - let mutable cancel_exception_original = 0 - let mutable cancel_original_processed = 0 - let mutable cancel_ct_registration_subsequent = 0 - let mutable cancel_exception_subsequent = 0 - let mutable cancel_subsequent_processed = 0 - - let failures = ResizeArray() - let mutable avgDurationMs = 0.0 + let mutable duration = 0L + + let keyTuple (keyData: KeyData<_, _>) = keyData.Label, keyData.Key, keyData.Version + + let logK (eventType: JobEvent) key = + Interlocked.Increment(eventCounts[eventType]) |> ignore + event.Trigger(eventType, key) + + let log eventType keyData = logK eventType (keyTuple keyData) let cache = LruCache<'TKey, 'TVersion, Job<'TValue>>( keepStrongly = defaultArg keepStrongly 100, keepWeakly = defaultArg keepWeakly 200, - requiredToKeep = - (function - | Running _ -> true - | Job.Canceled at when at > DateTime.Now.AddMinutes -5.0 -> true - | Job.Failed(at, _) when at > DateTime.Now.AddMinutes -5.0 -> true - | _ -> false), event = (function - | CacheEvent.Evicted -> - (fun k -> - Interlocked.Increment &evicted |> ignore - event.Trigger(JobEvent.Evicted, k)) - | CacheEvent.Collected -> - (fun k -> - Interlocked.Increment &collected |> ignore - event.Trigger(JobEvent.Collected, k)) - | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) - | CacheEvent.Strengthened -> - (fun k -> - Interlocked.Increment &strengthened |> ignore - event.Trigger(JobEvent.Strengthened, k)) - | CacheEvent.Cleared -> - (fun k -> - Interlocked.Increment &cleared |> ignore - event.Trigger(JobEvent.Cleared, k))) - ) - - let requestCounts = Dictionary, int>() - let cancellationRegistrations = Dictionary<_, _>() - - let saveRegistration key registration = - cancellationRegistrations[key] <- - match cancellationRegistrations.TryGetValue key with - | true, registrations -> registration :: registrations - | _ -> [ registration ] - - let cancelRegistration key = - match cancellationRegistrations.TryGetValue key with - | true, registrations -> - for r: CancellationTokenRegistration in registrations do - r.Dispose() - - cancellationRegistrations.Remove key |> ignore - | _ -> () - - let incrRequestCount key = - requestCounts[key] <- - if requestCounts.ContainsKey key then - requestCounts[key] + 1 - else - 1 - - let decrRequestCount key = - if requestCounts.ContainsKey key then - requestCounts[key] <- requestCounts[key] - 1 - - let log (eventType, keyData: KeyData<_, _>) = - event.Trigger(eventType, (keyData.Label, keyData.Key, keyData.Version)) - - let lock = new AsyncLock() - - let processRequest post (key: KeyData<_, _>, msg) diagnosticLogger = - - lock.Do(fun () -> - task { - - let cached, otherVersions = cache.GetAll(key.Key, key.Version) - - let result = - match msg, cached with - | GetOrCompute _, Some(Completed(result, diags)) -> - Interlocked.Increment &hits |> ignore - diags |> replayDiagnostics diagnosticLogger - Existing(Task.FromResult result) - | GetOrCompute(_, ct), Some(Running(tcs, _, _, _, loggers)) -> - Interlocked.Increment &hits |> ignore - incrRequestCount key - - ct.Register(fun _ -> - let _name = name - Interlocked.Increment &cancel_ct_registration_subsequent |> ignore - post (key, CancelRequest)) - |> saveRegistration key - - loggers.Add diagnosticLogger - - Existing tcs.Task - - | GetOrCompute(computation, ct), None - | GetOrCompute(computation, ct), Some(Job.Canceled _) - | GetOrCompute(computation, ct), Some(Job.Failed _) -> - Interlocked.Increment &started |> ignore - incrRequestCount key - - ct.Register(fun _ -> - let _name = name - Interlocked.Increment &cancel_ct_registration_original |> ignore - post (key, OriginatorCanceled)) - |> saveRegistration key - - let cts = new CancellationTokenSource() - - cache.Set( - key.Key, - key.Version, - key.Label, - (Running( - TaskCompletionSource<'TValue>(TaskCreationOptions.RunContinuationsAsynchronously), - cts, - computation, - DateTime.Now, - ResizeArray() - )) - ) - - otherVersions - |> Seq.choose (function - | v, Running(_tcs, cts, _, _, _) -> Some(v, cts) - | _ -> None) - |> Seq.iter (fun (_v, cts) -> - use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] - //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") - if cancelDuplicateRunningJobs then - //System.Diagnostics.Trace.TraceWarning("Canceling") - cts.Cancel()) - - New cts.Token - - log (Requested, key) - return result - }) - - let internalError key message = - let ex = exn (message) - failures.Add(key, ex) - Interlocked.Increment &errors |> ignore - // raise ex -- Suppose there's no need to raise here - where does it even go? - - let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = - lock.Do(fun () -> - task { - - let cached = cache.TryGet(key.Key, key.Version) - - match action, cached with - - | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> - - Interlocked.Increment &cancel_original_processed |> ignore - - decrRequestCount key - - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - // Remember the job in case it completes after cancellation - cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) - requestCounts.Remove key |> ignore - log (Canceled, key) - Interlocked.Increment &canceled |> ignore - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] - () - - else - // We need to restart the computation - Task.Run(fun () -> - Async.StartAsTask( - async { - - let cachingLogger = new CachingDiagnosticsLogger(None) - - try - // TODO: Should unify starting and restarting - log (Restarted, key) - Interlocked.Increment &restarted |> ignore - System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" - let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger - DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger - - try - let! result = computation - post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) - return () - finally - DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger - with - | TaskCancelled _ -> - Interlocked.Increment &cancel_exception_subsequent |> ignore - post (key, CancelRequest) - () - | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) - } - ), - cts.Token) - |> ignore - - | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> - - Interlocked.Increment &cancel_subsequent_processed |> ignore - - decrRequestCount key - - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - // Remember the job in case it completes after cancellation - cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) - requestCounts.Remove key |> ignore - log (Canceled, key) - Interlocked.Increment &canceled |> ignore - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] - () - - // Probably in some cases cancellation can be fired off even after we just unregistered it - | CancelRequest, None - | CancelRequest, Some(Completed _) - | CancelRequest, Some(Job.Canceled _) - | CancelRequest, Some(Job.Failed _) - | OriginatorCanceled, None - | OriginatorCanceled, Some(Completed _) - | OriginatorCanceled, Some(Job.Canceled _) - | OriginatorCanceled, Some(Job.Failed _) -> () - - | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> - cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, Job.Failed(DateTime.Now, ex)) - requestCounts.Remove key |> ignore - log (Failed, key) - Interlocked.Increment &failed |> ignore - failures.Add(key.Label, ex) - - for logger in loggers do - diags |> replayDiagnostics logger - - tcs.TrySetException ex |> ignore - - | JobCompleted(result, diags), Some(Running(tcs, _cts, _c, started, loggers)) -> - cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, (Completed(result, diags))) - requestCounts.Remove key |> ignore - log (Finished, key) - Interlocked.Increment &completed |> ignore - let duration = float (DateTime.Now - started).Milliseconds - - avgDurationMs <- - if completed < 2 then - duration - else - avgDurationMs + (duration - avgDurationMs) / float completed - - for logger in loggers do - diags |> replayDiagnostics logger - - if tcs.TrySetResult result = false then - internalError key.Label "Invalid state: Completed job already completed" - - // Sometimes job can be canceled but it still manages to complete (or fail) - | JobFailed _, Some(Job.Canceled _) - | JobCompleted _, Some(Job.Canceled _) -> () - - // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever - | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" - - | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" - - | JobFailed(ex, _diags), Some(Completed(_job, _diags2)) -> - internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" - - | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> - internalError key.Label "Invalid state: Double-Completed job" - - | JobFailed(ex, _diags), Some(Job.Failed(_, ex2)) -> - internalError key.Label $"Invalid state: Double-Failed job \n%A{ex} \n%A{ex2}" - - | JobCompleted(_result, _diags), Some(Job.Failed(_, ex2)) -> - internalError key.Label $"Invalid state: Completed Failed job \n%A{ex2}" - }) - - let rec post msg = - Interlocked.Increment &updates_in_flight |> ignore - backgroundTask { - do! processStateUpdate post msg - Interlocked.Decrement &updates_in_flight |> ignore - } - |> ignore - - member this.Get'(key, computation) = - - let wrappedKey = - { new ICacheKey<_, _> with - member _.GetKey() = key - member _.GetVersion() = Unchecked.defaultof<_> - member _.GetLabel() = match key.ToString() with | null -> "" | s -> s - } - - this.Get(wrappedKey, computation) + | CacheEvent.Evicted -> logK JobEvent.Evicted + | CacheEvent.Collected -> logK JobEvent.Collected + | CacheEvent.Weakened -> logK JobEvent.Weakened + | CacheEvent.Strengthened -> logK JobEvent.Strengthened + | CacheEvent.Cleared -> logK JobEvent.Cleared)) member _.Get(key: ICacheKey<_, _>, computation) = - let key = { Label = key.GetLabel() @@ -496,136 +198,114 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Version = key.GetVersion() } - async { - let! ct = Async.CancellationToken + let wrappedComputation = + async { + use! _handler = Async.OnCancel (fun () -> log Canceled key) + let sw = Stopwatch.StartNew() + log Started key + let logger = CapturingDiagnosticsLogger "cache" + SetThreadDiagnosticsLoggerNoUnwind logger + + match! computation |> Async.Catch with + | Choice1Of2 result -> + log Finished key + Interlocked.Add(&duration, sw.ElapsedMilliseconds) |> ignore + return Result.Ok result, logger + | Choice2Of2 exn -> + log Failed key + return Result.Error exn, logger + } - let callerDiagnosticLogger = DiagnosticsThreadStatics.DiagnosticsLogger + let getOrAdd () = + let cached, otherVersions = cache.GetAll(key.Key, key.Version) - match! - processRequest post (key, GetOrCompute(computation, ct)) callerDiagnosticLogger - |> Async.AwaitTask - with - | New internalCt -> + let countHit v = Interlocked.Increment &hits |> ignore; v + let cacheSetNewJob () = + let job = Job(wrappedComputation, cancelUnawaited = cancelUnawaitedJobs) + cache.Set(key.Key, key.Version, key.Label, job) + job - let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) - let cachingLogger = new CachingDiagnosticsLogger(Some callerDiagnosticLogger) + otherVersions, - try - return! - Async.StartAsTask( - async { - // TODO: Should unify starting and restarting - let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger - DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger - - log (Started, key) - - try - let! result = computation - post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) - return result - finally - DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger - }, - cancellationToken = linkedCtSource.Token - ) - |> Async.AwaitTask - with - | TaskCancelled ex -> - // TODO: do we need to do anything else here? Presumably it should be done by the registration on - // the cancellation token or before we triggered our own cancellation + cached + |> Option.map countHit + |> Option.defaultWith cacheSetNewJob - // Let's send this again just in case. It seems sometimes it's not triggered from the registration? + async { + let otherVersions, job = lock cache getOrAdd - Interlocked.Increment &cancel_exception_original |> ignore + log Requested key - post (key, (OriginatorCanceled)) - return raise ex - | ex -> - post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) - return raise ex + if cancelDuplicateRunningJobs && not cancelUnawaitedJobs then + otherVersions |> Seq.map snd |> Seq.iter _.CancelIfUnawaited() - | Existing job -> return! job |> Async.AwaitTask + use _ = new CompilationGlobalsScope() + let! result, logger = job.Request() + logger.CommitDelayedDiagnostics DiagnosticsThreadStatics.DiagnosticsLogger + match result with + | Ok result -> + return result + | Error exn -> + return raise exn } member _.TryGet(key: 'TKey, predicate: 'TVersion -> bool) : 'TValue option = - let versionsAndJobs = cache.GetAll(key) - - versionsAndJobs - |> Seq.tryPick (fun (version, job) -> - match predicate version, job with - | true, Completed(completed, _) -> Some completed - | _ -> None) + lock cache <| fun () -> + cache.GetAll(key) + |> Seq.tryPick (fun (version, job) -> + match predicate version, job.TryResult with + | true, Some(Ok result, _) -> Some result + | _ -> None) - member _.Clear() = cache.Clear() + member _.Clear() = lock cache cache.Clear - member _.Clear predicate = cache.Clear predicate + member _.Clear predicate = lock cache <| fun () -> cache.Clear predicate member val Event = event.Publish member this.OnEvent = this.Event.Add - member _.Count = lock.Do(fun () -> Task.FromResult cache.Count).Result + member this.Count = lock cache <| fun () -> cache.Count - member _.Updating = updates_in_flight > 0 - - member _.Locked = lock.Semaphore.CurrentCount < 1 + member this.DebuggerDisplay = - member _.Running = - cache.GetValues() - |> Seq.filter (function - | _, _, Running _ -> true - | _ -> false) - |> Seq.toArray + let cachedJobs = cache.GetValues() |> Seq.map (fun (_,_,job) -> job) - member this.DebuggerDisplay = - let locked = if this.Locked then " [LOCKED]" else "" + let jobStateName = function + | Initial _ -> nameof Initial + | Running _ -> nameof Running + | Completed _ -> nameof Completed + | Faulted _ -> nameof Faulted - let valueStats = - cache.GetValues() - |> Seq.countBy (function - | _, _, Running _ -> "Running" - | _, _, Completed _ -> "Completed" - | _, _, Job.Canceled _ -> "Canceled" - | _, _, Job.Failed _ -> "Failed") - |> Map + let valueStats = cachedJobs |> Seq.countBy (_.State >> jobStateName) |> Map + let getStat key = valueStats.TryFind key |> Option.defaultValue 0 let running = - valueStats.TryFind "Running" - |> Option.map (sprintf " Running: %d ") - |> Option.defaultValue "" + let count = getStat "Running" + if count > 0 then $" Running {count}" else "" - let avgDuration = avgDurationMs |> sprintf "| Avg: %.0f ms" + let finished = eventCounts[Finished].Value + let avgDuration = if finished = 0 then "" else $"| Avg: %.0f{float duration / float finished} ms" - let hitRatio = - if started > 0 then - $" (%.0f{float hits / (float (started + hits)) * 100.0} %%)" - else - "" + let requests = eventCounts[Requested].Value + let hitRatio = if requests = 0 then "" else $" (%.0f{float hits / (float (requests)) * 100.0} %%)" + + let faulted = getStat "Faulted" + let failed = eventCounts[Failed].Value let stats = - [| - if errors + failed > 0 then + seq { + if faulted + failed > 0 then " (_!_) " - if errors > 0 then $"| ERRORS: {errors} " else "" - if failed > 0 then $"| FAILED: {failed} " else "" + for j in eventCounts.Keys do + let count = eventCounts[j].Value + if count > 0 then $"| {j}: {count}" else "" $"| hits: {hits}{hitRatio} " - if started > 0 then $"| started: {started} " else "" - if completed > 0 then $"| completed: {completed} " else "" - if canceled > 0 then $"| canceled: {canceled} " else "" - if restarted > 0 then $"| restarted: {restarted} " else "" - if evicted > 0 then $"| evicted: {evicted} " else "" - if collected > 0 then $"| collected: {collected} " else "" - if cleared > 0 then $"| cleared: {cleared} " else "" - if strengthened > 0 then - $"| strengthened: {strengthened} " - else - "" - |] + } |> String.concat "" - $"{locked}{running}{cache.DebuggerDisplay} {stats}{avgDuration}" + $"{running} {cache.DebuggerDisplay} {stats}{avgDuration}" /// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. [] @@ -640,4 +320,4 @@ type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equalit Interlocked.Increment &requests |> ignore computation - member _.DebuggerDisplay = $"(disabled) requests: {requests}" + member _.DebuggerDisplay = $"(disabled) requests: {requests}" \ No newline at end of file diff --git a/src/Compiler/Facilities/AsyncMemoize.fsi b/src/Compiler/Facilities/AsyncMemoize.fsi index d86352d9987..37d2d397fe2 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fsi +++ b/src/Compiler/Facilities/AsyncMemoize.fsi @@ -9,9 +9,6 @@ module internal Utils = /// Return file name with one directory above it val shortPath: path: string -> string - [] - val (|TaskCancelled|_|): ex: exn -> TaskCanceledException voption - type internal JobEvent = | Requested | Started @@ -39,13 +36,6 @@ type Extensions = [] static member internal WithExtraVersion: cacheKey: ICacheKey<'a, 'b> * extraVersion: 'c -> ICacheKey<'a, ('b * 'c)> -type internal AsyncLock = - interface System.IDisposable - - new: unit -> AsyncLock - - member Do: f: (unit -> #Task<'b>) -> Task<'b> - /// /// A cache/memoization for computations that makes sure that the same computation will only be computed once even if it's needed /// at multiple places/times. @@ -62,9 +52,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T /// Maximum number of strongly held results to keep in the cache /// Maximum number of weakly held results to keep in the cache /// Name of the cache - used in tracing messages + /// Cancels a job when all the awaiting requests are canceled. If set to false, unawaited job will run to completion and it's result will be cached. /// If true, when a job is started, all other jobs with the same key will be canceled. new: - ?keepStrongly: int * ?keepWeakly: int * ?name: string * ?cancelDuplicateRunningJobs: bool -> + ?keepStrongly: int * ?keepWeakly: int * ?name: string * ?cancelUnawaitedJobs: bool * ?cancelDuplicateRunningJobs: bool -> AsyncMemoize<'TKey, 'TVersion, 'TValue> member Clear: unit -> unit @@ -73,8 +64,6 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member Get: key: ICacheKey<'TKey, 'TVersion> * computation: Async<'TValue> -> Async<'TValue> - member Get': key: 'TKey * computation: Async<'TValue> -> Async<'TValue> - member TryGet: key: 'TKey * predicate: ('TVersion -> bool) -> 'TValue option member Event: IEvent @@ -83,8 +72,6 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member Count: int - member Updating: bool - /// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 5158ac7f25c..d0fb6b63768 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1821,9 +1821,7 @@ type internal TransparentCompiler Trace.TraceInformation($"Using in-memory project reference: {name}") return assemblyDataResult - with - | TaskCancelled ex -> return raise ex - | ex -> + with ex -> errorR (exn ($"Error while computing assembly data for project {projectSnapshot.Label}: {ex}")) return ProjectAssemblyDataResult.Unavailable true } diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs deleted file mode 100644 index ef4b69a3910..00000000000 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs +++ /dev/null @@ -1,26 +0,0 @@ -module CompilerService.AsyncLock - -open Internal.Utilities.Collections - -open Xunit -open System.Threading.Tasks - - -[] -let ``Async lock works`` () = - task { - use lock = new AsyncLock() - - let mutable x = 0 - - let job () = task { - let y = x - do! Task.Delay(10) - x <- y + 1 - } - - let jobs = [ for _ in 1..100 -> lock.Do job ] - let! _ = Task.WhenAll(jobs) - - Assert.Equal(100, x) - } \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index e178ccaa911..2c0b6c311be 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -11,40 +11,58 @@ open FSharp.Compiler.Diagnostics open Xunit -let tap f x = f x; x +let internal observe (cache: AsyncMemoize<_,_,_>) = -let internal record (cache: AsyncMemoize<_,_,_>) = + let collected = new MailboxProcessor<_>(fun _ -> async {}) - let events = Collections.Concurrent.ConcurrentQueue() + let arrivals = MailboxProcessor.Start(fun inbox -> + let rec loop events = async { + let! (e, (_, k, _)) = inbox.Receive() + let events = (e, k) :: events + printfn $"{k}: {e}" + collected.Post events + do! loop events + } + loop [] + ) + + cache.Event.Add arrivals.Post - let waitForIdle() = SpinWait.SpinUntil(fun () -> not cache.Updating) + let next () = collected.Receive(10_000) - waitForIdle() - cache.Event - |> Event.map (fun (e, (_, k, _)) -> e, k) - |> Event.add events.Enqueue + next + +let rec awaitEvents next condition = + async { + match! next () with + | events when condition events -> return events + | _ -> return! awaitEvents next condition + } - let getEvents () = - waitForIdle() - events |> List.ofSeq |> tap (printfn "events: %A") +let rec eventsWhen next condition = + awaitEvents next condition |> Async.RunSynchronously - getEvents +let waitUntil next condition = + eventsWhen next condition |> ignore -let check getEvents assertFunction = - let actual = getEvents() - assertFunction actual +let expect next (expected: 't list) = + let actual = eventsWhen next (List.length >> (=) expected.Length) + Assert.Equal<'t list>(expected, actual |> List.rev) -let waitUntil getEvents condition = - while getEvents() |> condition |> not do () +let countOf value events = + events |> Seq.filter (fst >> (=) value) |> Seq.length -let recorded (expected: 't list) (actual: 't list) = - Assert.Equal<'t>(expected, actual) +let received event = function (a, _) :: _ when a = event -> true | _ -> false -let countOf value count events = - events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count +let internal wrapKey key = + { new ICacheKey<_, _> with + member _.GetKey() = key + member _.GetVersion() = Unchecked.defaultof<_> + member _.GetLabel() = match key.ToString() with | null -> "" | s -> s + } -let received value events = - events |> List.tryLast |> Option.map (fst >> (=) value) |> Option.defaultValue false +let assertTaskCanceled (task: Task<_>) = + Assert.ThrowsAnyAsync(fun () -> task).Result |> ignore [] let ``Basics``() = @@ -54,16 +72,16 @@ let ``Basics``() = } let memoize = AsyncMemoize() - let events = record memoize + let events = observe memoize let result = seq { - memoize.Get'(5, computation 5) - memoize.Get'(5, computation 5) - memoize.Get'(2, computation 2) - memoize.Get'(5, computation 5) - memoize.Get'(3, computation 3) - memoize.Get'(2, computation 2) + memoize.Get(wrapKey 5, computation 5) + memoize.Get(wrapKey 5, computation 5) + memoize.Get(wrapKey 2, computation 2) + memoize.Get(wrapKey 5, computation 5) + memoize.Get(wrapKey 3, computation 3) + memoize.Get(wrapKey 2, computation 2) } |> Async.Parallel |> Async.RunSynchronously @@ -72,138 +90,148 @@ let ``Basics``() = Assert.Equal(expected, result) - check events <| fun events -> - let groups = events |> Seq.groupBy snd |> Seq.toList - Assert.Equal(3, groups.Length) - for key, events in groups do - Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) + let events = eventsWhen events (countOf Finished >> (=) 3) + + let groups = events |> Seq.groupBy snd |> Seq.toList + Assert.Equal(3, groups.Length) + for key, events in groups do + Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) [] -let ``We can cancel a job`` () = - task { +let ``We can disconnect a request from a running job`` () = - let jobStarted = new ManualResetEventSlim(false) - let cts = new CancellationTokenSource() - let ctsCancelled = new ManualResetEventSlim(false) + let cts = new CancellationTokenSource() + let canFinish = new ManualResetEventSlim(false) - let computation = async { - use! _catch = Async.OnCancel ignore - jobStarted.Set() - ctsCancelled.Wait() - do! async { } - failwith "Should be canceled before it gets here" - } + let computation = async { + canFinish.Wait() + } + + let memoize = AsyncMemoize<_, int, _>(cancelUnawaitedJobs = false, cancelDuplicateRunningJobs = true) + let events = observe memoize + + let key = 1 - let memoize = AsyncMemoize<_, int, _>() - let events = record memoize + let task1 = Async.StartAsTask( memoize.Get(wrapKey 1, computation), cancellationToken = cts.Token) - let key = 1 + waitUntil events (received Started) + cts.Cancel() - let _task1 = Async.StartAsTask( memoize.Get'(1, computation), cancellationToken = cts.Token) + assertTaskCanceled task1 - jobStarted.Wait() - cts.Cancel() - ctsCancelled.Set() + canFinish.Set() |> ignore - check events recorded - [ Requested, key - Started, key - Canceled, key ] + expect events + [ Requested, key + Started, key + Finished, key ] + +[] +let ``We can cancel a job`` () = + + let cts = new CancellationTokenSource() + + let computation = async { + while true do + do! Async.Sleep 1000 } + let memoize = AsyncMemoize<_, int, _>() + let events = observe memoize + + let key = 1 + + let task1 = Async.StartAsTask( memoize.Get(wrapKey 1, computation), cancellationToken = cts.Token) + + waitUntil events (received Started) + + cts.Cancel() + + assertTaskCanceled task1 + + expect events + [ Requested, key + Started, key + Canceled, key ] + [] let ``Job is restarted if first requestor cancels`` () = - let jobStarted = new SemaphoreSlim(0) + let jobCanComplete = new ManualResetEventSlim(false) - let jobCanComplete = new ManualResetEventSlim(false) + let computation key = async { + jobCanComplete.Wait() + return key * 2 + } - let computation key = async { - jobStarted.Release() |> ignore + let memoize = AsyncMemoize<_, int, _>() + let events = observe memoize - jobCanComplete.Wait() - return key * 2 - } + use cts1 = new CancellationTokenSource() - let memoize = AsyncMemoize<_, int, _>() - let events = record memoize + let key = 1 - use cts1 = new CancellationTokenSource() + let _task1 = Async.StartAsTask( memoize.Get(wrapKey key, computation key), cancellationToken = cts1.Token) - let key = 1 + waitUntil events (received Started) + cts1.Cancel() - let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) + waitUntil events (received Canceled) - jobStarted.Wait() - let task2 = Async.StartAsTask( memoize.Get'(key, computation key)) - let task3 = Async.StartAsTask( memoize.Get'(key, computation key)) + let task2 = Async.StartAsTask( memoize.Get(wrapKey key, computation key)) - waitUntil events (countOf Requested 3) + waitUntil events (countOf Started >> (=) 2) - cts1.Cancel() + jobCanComplete.Set() |> ignore - jobCanComplete.Set() |> ignore + Assert.Equal(2, task2.Result) - jobStarted.Wait() + expect events + [ Requested, key + Started, key + Canceled, key + Requested, key + Started, key + Finished, key ] - Assert.Equal(2, task2.Result) - Assert.Equal(2, task3.Result) - - check events recorded - [ Requested, key - Started, key - Requested, key - Requested, key - Restarted, key - Finished, key ] [] -let ``Job is restarted if first requestor cancels but keeps running if second requestor cancels`` () = - let jobStarted = new ManualResetEventSlim(false) +let ``Job keeps running if only one requestor cancels`` () = - let jobCanComplete = new ManualResetEventSlim(false) + let jobCanComplete = new ManualResetEventSlim(false) - let computation key = async { - jobStarted.Set() |> ignore - jobCanComplete.Wait() - return key * 2 - } + let computation key = async { + jobCanComplete.Wait() + return key * 2 + } - let memoize = AsyncMemoize<_, int, _>() - let events = record memoize - - use cts1 = new CancellationTokenSource() - use cts2 = new CancellationTokenSource() - - let key = 1 + let memoize = AsyncMemoize<_, int, _>() + let events = observe memoize - let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) + use cts = new CancellationTokenSource() - jobStarted.Wait() - jobStarted.Reset() |> ignore + let key = 1 - let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) - let task3 = Async.StartAsTask( memoize.Get'(key, computation key)) + let task1 = Async.StartAsTask( memoize.Get(wrapKey key, computation key)) - waitUntil events (countOf Requested 3) + waitUntil events (received Started) - cts1.Cancel() + let task2 = Async.StartAsTask( memoize.Get(wrapKey key, computation key) |> Async.Ignore, cancellationToken = cts.Token) - jobStarted.Wait() + waitUntil events (countOf Requested >> (=) 2) - cts2.Cancel() + cts.Cancel() - jobCanComplete.Set() |> ignore + assertTaskCanceled task2 - Assert.Equal(2, task3.Result) + jobCanComplete.Set() |> ignore - check events recorded - [ Requested, key - Started, key - Requested, key - Requested, key - Restarted, key - Finished, key ] + Assert.Equal(2, task1.Result) + expect events + [ Requested, key + Started, key + Requested, key + Finished, key ] type ExpectedException() = inherit Exception() @@ -290,7 +318,7 @@ let ``Stress test`` () = let timeoutMs = rng.Next(minTimeout, maxTimeout) let key = keys[rng.Next keys.Length] let result = key * 2 - let job = cache.Get'(key, computation durationMs result) + let job = cache.Get(wrapKey key, computation durationMs result) let cts = new CancellationTokenSource() let runningJob = Async.StartAsTask(job, cancellationToken = cts.Token) cts.CancelAfter timeoutMs @@ -328,60 +356,56 @@ let ``Stress test`` () = Assert.True ((float completed) > ((float started) * 0.1), "Less than 10 % completed jobs") -[] -[] -[] -let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = - let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) - - let mutable started = 0 - let mutable finished = 0 +[] +let ``Cancel running jobs with the same key`` () = + let cache = AsyncMemoize(cancelUnawaitedJobs = false, cancelDuplicateRunningJobs = true) - let job1started = new ManualResetEventSlim(false) - let job1finished = new ManualResetEventSlim(false) + let events = observe cache let jobCanContinue = new ManualResetEventSlim(false) - let job2started = new ManualResetEventSlim(false) - let job2finished = new ManualResetEventSlim(false) - - let work onStart onFinish = async { - Interlocked.Increment &started |> ignore - onStart() |> ignore + let work = async { jobCanContinue.Wait() - do! Async.Sleep 100 - Interlocked.Increment &finished |> ignore - onFinish() |> ignore } - let key1 = + let key version = { new ICacheKey<_, _> with member _.GetKey() = 1 - member _.GetVersion() = 1 - member _.GetLabel() = "key1" } + member _.GetVersion() = version + member _.GetLabel() = $"key1 {version}" } - cache.Get(key1, work job1started.Set job1finished.Set) |> Async.Catch |> Async.Ignore |> Async.Start + let cts = new CancellationTokenSource() - job1started.Wait() + let jobsToCancel = + [ for i in 1 .. 10 -> Async.StartAsTask(cache.Get(key i , work), cancellationToken = cts.Token) ] - let key2 = - { new ICacheKey<_, _> with - member _.GetKey() = key1.GetKey() - member _.GetVersion() = key1.GetVersion() + 1 - member _.GetLabel() = "key2" } + waitUntil events (countOf Started >> (=) 10) + + // detach requests from their running computations + cts.Cancel() + + for job in jobsToCancel do assertTaskCanceled job - cache.Get(key2, work job2started.Set job2finished.Set ) |> Async.Catch |> Async.Ignore |> Async.Start + let job = cache.Get(key 11, work) |> Async.StartAsTask - job2started.Wait() + // up til now the jobs should have been running unobserved + let current = eventsWhen events (received Requested) + Assert.Equal(0, current |> countOf Canceled) + + // new request should cancel the unobserved jobs + waitUntil events (received Started) jobCanContinue.Set() |> ignore - - job2finished.Wait() - - if not cancelDuplicate then - job1finished.Wait() - Assert.Equal((2, expectFinished), (started, finished)) + job.Wait() + + let events = eventsWhen events (received Finished) + + Assert.Equal(0, events |> countOf Failed) + + Assert.Equal(10, events |> countOf Canceled) + + Assert.Equal(1, events |> countOf Finished) type DummyException(msg) = inherit Exception(msg) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index ffc935a2075..a1a46d7a99e 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -300,7 +300,6 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 6dcd6463cd5..4b5987f65e5 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -33,17 +33,13 @@ let internal recordAllEvents groupBy = let mutable cache : AsyncMemoize<_,_,_> option = None let events = ConcurrentQueue() - let waitForIdle() = SpinWait.SpinUntil(fun () -> not cache.Value.Updating) - let observe (getCache: CompilerCaches -> AsyncMemoize<_,_,_>) (checker: FSharpChecker) = cache <- Some (getCache checker.Caches) - waitForIdle() cache.Value.Event |> Event.map (fun (e, k) -> groupBy k, e) |> Event.add events.Enqueue let getEvents () = - waitForIdle() events |> List.ofSeq observe, getEvents From ebdd53645b720c9a443a1d2f438297078dbc6e18 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 26 Nov 2024 16:52:32 +0100 Subject: [PATCH 10/13] Fix crashdump paths in CI (#18065) --- azure-pipelines-PR.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml index 7d11a931c24..63d791bb5f5 100644 --- a/azure-pipelines-PR.yml +++ b/azure-pipelines-PR.yml @@ -229,7 +229,7 @@ stages: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_DbgMiniDumpType: 3 # Triage dump, 1 for mini, 2 for Heap, 3 for triage, 4 for full. Don't use 4 unless you know what you're doing. - DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\$(_configuration)\$(Build.BuildId)-%e-%p-%t.dmp + DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\Release\$(Build.BuildId)-%e-%p-%t.dmp NativeToolsOnMachine: true displayName: Build @@ -247,8 +247,8 @@ stages: condition: failed() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: 'Windows $(_configuration) $(_testKind) process dumps' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' + ArtifactName: 'Windows Release $(_testKind) process dumps' ArtifactType: Container parallel: true @@ -269,7 +269,7 @@ stages: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_DbgMiniDumpType: 3 # Triage dump, 1 for mini, 2 for Heap, 3 for triage, 4 for full. Don't use 4 unless you know what you're doing. - DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\$(_configuration)\$(Build.BuildId)-%e-%p-%t.dmp + DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\Release\$(Build.BuildId)-%e-%p-%t.dmp NativeToolsOnMachine: true displayName: Build @@ -287,8 +287,8 @@ stages: condition: failed() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: 'Windows $(_configuration) $(_testKind) process dumps' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' + ArtifactName: 'Windows Release $(_testKind) process dumps' ArtifactType: Container parallel: true @@ -309,7 +309,7 @@ stages: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_DbgMiniDumpType: 3 # Triage dump, 1 for mini, 2 for Heap, 3 for triage, 4 for full. Don't use 4 unless you know what you're doing. - DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\$(_configuration)\$(Build.BuildId)-%e-%p-%t.dmp + DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\Release\$(Build.BuildId)-%e-%p-%t.dmp NativeToolsOnMachine: true displayName: Build @@ -327,8 +327,8 @@ stages: condition: failed() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: 'Windows $(_configuration) $(_testKind) process dumps' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' + ArtifactName: 'Windows Release $(_testKind) process dumps' ArtifactType: Container parallel: true @@ -349,7 +349,7 @@ stages: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_DbgMiniDumpType: 3 # Triage dump, 1 for mini, 2 for Heap, 3 for triage, 4 for full. Don't use 4 unless you know what you're doing. - DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\$(_configuration)\$(Build.BuildId)-%e-%p-%t.dmp + DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\Release\$(Build.BuildId)-%e-%p-%t.dmp NativeToolsOnMachine: true displayName: Build @@ -367,8 +367,8 @@ stages: condition: failed() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: 'Windows $(_configuration) $(_testKind) process dumps' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' + ArtifactName: 'Windows Release $(_testKind) process dumps' ArtifactType: Container parallel: true @@ -385,7 +385,7 @@ stages: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_DbgMiniDumpType: 3 # Triage dump, 1 for mini, 2 for Heap, 3 for triage, 4 for full. Don't use 4 unless you know what you're doing. - DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\$(_configuration)\$(Build.BuildId)-%e-%p-%t.dmp + DOTNET_DbgMiniDumpName: $(Build.SourcesDirectory)\artifacts\log\Release\$(Build.BuildId)-%e-%p-%t.dmp NativeToolsOnMachine: true displayName: Build @@ -403,8 +403,8 @@ stages: condition: failed() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: 'Windows $(_configuration) $(_testKind) process dumps' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' + ArtifactName: 'Windows Release $(_testKind) process dumps' ArtifactType: Container parallel: true From 0faaa8699828b3416aa37228f76fe8dba7ac4d45 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 26 Nov 2024 17:00:08 +0100 Subject: [PATCH 11/13] Various updates to ILVerify (#18060) --- DEVGUIDE.md | 8 +++- azure-pipelines-PR.yml | 2 +- {eng => tests/ILVerify}/ilverify.ps1 | 40 ++++++++++++++----- ...y_FSharp.Compiler.Service_Debug_net9.0.bsl | 0 ....Compiler.Service_Debug_netstandard2.0.bsl | 0 ...FSharp.Compiler.Service_Release_net9.0.bsl | 0 ...ompiler.Service_Release_netstandard2.0.bsl | 0 ...erify_FSharp.Core_Debug_netstandard2.0.bsl | 0 ...erify_FSharp.Core_Debug_netstandard2.1.bsl | 0 ...ify_FSharp.Core_Release_netstandard2.0.bsl | 0 ...ify_FSharp.Core_Release_netstandard2.1.bsl | 0 11 files changed, 38 insertions(+), 12 deletions(-) rename {eng => tests/ILVerify}/ilverify.ps1 (80%) rename {eng => tests/ILVerify}/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Core_Debug_netstandard2.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Core_Debug_netstandard2.1.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Core_Release_netstandard2.0.bsl (100%) rename {eng => tests/ILVerify}/ilverify_FSharp.Core_Release_netstandard2.1.bsl (100%) diff --git a/DEVGUIDE.md b/DEVGUIDE.md index b3ed65611c4..cf3a9419481 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -228,7 +228,8 @@ dotnet test tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fs ### Updating ILVerify baselines -These control IL for the core modules of the compiler. The baselines are located in the `eng` folder and look like: +These are IL baseline tests for the core assemblies of the compiler (FSharp.Core and FSharp.Compiler.Service). The baselines are located in the `tests/ILVerify` folder and look like: + ``` ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -240,7 +241,10 @@ ilverify_FSharp.Core_Release_netstandard2.0.bsl ilverify_FSharp.Core_Release_netstandard2.1.bsl ``` -If you want to update them, run the [ilverify.ps1]([url](/~https://github.com/dotnet/fsharp/blob/main/eng/ilverify.ps1)) script in PowerShell. The script will create `.actual` files. If the differences make sense, replace the original baselines with the actual files. +If you want to update them, either + +1. Run the [ilverify.ps1]([url](/~https://github.com/dotnet/fsharp/blob/main/tests/ILVerify/ilverify.ps1)) script in PowerShell. The script will create `.actual` files. If the differences make sense, replace the original baselines with the actual files. +2. Set the `TEST_UPDATE_BSL` to `1` (please refer to "Updating baselines in tests" section in this file) **and** run `ilverify.ps1` - this will automatically replace baselines. After that, please carefully review the change and push it to your branch if it makes sense. ## Automated Source Code Formatting diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml index 63d791bb5f5..42d1fa91ca9 100644 --- a/azure-pipelines-PR.yml +++ b/azure-pipelines-PR.yml @@ -825,6 +825,6 @@ stages: installationPath: $(Agent.ToolsDirectory)/dotnet - script: dotnet tool restore displayName: Restore dotnet tools - - pwsh: .\eng\ilverify.ps1 + - pwsh: .\tests\ILVerify\ilverify.ps1 displayName: Run ILVerify workingDirectory: $(Build.SourcesDirectory) diff --git a/eng/ilverify.ps1 b/tests/ILVerify/ilverify.ps1 similarity index 80% rename from eng/ilverify.ps1 rename to tests/ILVerify/ilverify.ps1 index 3fd8f9eda64..760724c27c6 100644 --- a/eng/ilverify.ps1 +++ b/tests/ILVerify/ilverify.ps1 @@ -4,11 +4,12 @@ Write-Host "Checking whether running on Windows: $IsWindows" -[string] $repo_path = (Get-Item -Path $PSScriptRoot).Parent +[string] $repo_path = (Get-Item -Path $PSScriptRoot).Parent.Parent Write-Host "Repository path: $repo_path" [string] $script = if ($IsWindows) { Join-Path $repo_path "build.cmd" } else { Join-Path $repo_path "build.sh" } +[string] $additional_arguments = if ($IsWindows) { "-noVisualStudio" } else { "" } # Set configurations to build [string[]] $configurations = @("Debug", "Release") @@ -29,7 +30,7 @@ $projects = @{ # Run build script for each configuration (NOTE: We don't build Proto) foreach ($configuration in $configurations) { Write-Host "Building $configuration configuration..." - & $script -c $configuration + & $script -c $configuration $additional_arguments if ($LASTEXITCODE -ne 0 -And $LASTEXITCODE -ne '') { Write-Host "Build failed for $configuration configuration (last exit code: $LASTEXITCODE)." exit 1 @@ -111,14 +112,20 @@ foreach ($project in $projects.Keys) { } } - $baseline_file = Join-Path $repo_path "eng" "ilverify_${project}_${configuration}_${tfm}.bsl" + $baseline_file = Join-Path $repo_path "tests/ILVerify" "ilverify_${project}_${configuration}_${tfm}.bsl" $baseline_actual_file = [System.IO.Path]::ChangeExtension($baseline_file, 'bsl.actual') if (-not (Test-Path $baseline_file)) { Write-Host "Baseline file not found: $baseline_file" - $ilverify_output | Set-Content $baseline_actual_file - $failed = $true + if ($env:TEST_UPDATE_BSL -eq "1") { + Write-Host "Creating initial baseline file: $baseline_file" + $ilverify_output | Set-Content $baseline_file + } else { + Write-Host "Creating .actual baseline file: $baseline_actual_file" + $ilverify_output | Set-Content $baseline_actual_file + $failed = $true + } continue } @@ -127,8 +134,14 @@ foreach ($project in $projects.Keys) { if ($baseline.Length -eq 0) { Write-Host "Baseline file is empty: $baseline_file" - $ilverify_output | Set-Content $baseline_actual_file - $failed = $true + if ($env:TEST_UPDATE_BSL -eq "1") { + Write-Host "Updating empty baseline file: $baseline_file" + $ilverify_output | Set-Content $baseline_file + } else { + Write-Host "Creating initial .actual baseline file: $baseline_actual_file" + $ilverify_output | Set-Content $baseline_actual_file + $failed = $true + } continue } @@ -142,10 +155,19 @@ foreach ($project in $projects.Keys) { Write-Host "ILverify output does not match baseline, differences:" $cmp | Format-Table | Out-String | Write-Host - $ilverify_output | Set-Content $baseline_actual_file + + # Update baselines if TEST_UPDATE_BSL is set to 1 + if ($env:TEST_UPDATE_BSL -eq "1") { + Write-Host "Updating baseline file: $baseline_file" + $ilverify_output | Set-Content $baseline_file + } else { + $ilverify_output | Set-Content $baseline_actual_file + } $failed = $true continue } + + } } } @@ -155,4 +177,4 @@ if ($failed) { exit 1 } -exit 0 +exit 0 \ No newline at end of file diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl rename to tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl rename to tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl diff --git a/eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl rename to tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl diff --git a/eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl rename to tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl diff --git a/eng/ilverify_FSharp.Core_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Core_Debug_netstandard2.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Core_Debug_netstandard2.0.bsl rename to tests/ILVerify/ilverify_FSharp.Core_Debug_netstandard2.0.bsl diff --git a/eng/ilverify_FSharp.Core_Debug_netstandard2.1.bsl b/tests/ILVerify/ilverify_FSharp.Core_Debug_netstandard2.1.bsl similarity index 100% rename from eng/ilverify_FSharp.Core_Debug_netstandard2.1.bsl rename to tests/ILVerify/ilverify_FSharp.Core_Debug_netstandard2.1.bsl diff --git a/eng/ilverify_FSharp.Core_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Core_Release_netstandard2.0.bsl similarity index 100% rename from eng/ilverify_FSharp.Core_Release_netstandard2.0.bsl rename to tests/ILVerify/ilverify_FSharp.Core_Release_netstandard2.0.bsl diff --git a/eng/ilverify_FSharp.Core_Release_netstandard2.1.bsl b/tests/ILVerify/ilverify_FSharp.Core_Release_netstandard2.1.bsl similarity index 100% rename from eng/ilverify_FSharp.Core_Release_netstandard2.1.bsl rename to tests/ILVerify/ilverify_FSharp.Core_Release_netstandard2.1.bsl From 8c0bf14cde83c03d5a8acbe08514e0558f3a34b8 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 26 Nov 2024 20:16:45 +0100 Subject: [PATCH 12/13] Update azure-pipelines-PR.yml --- azure-pipelines-PR.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml index 42d1fa91ca9..75d0484cac9 100644 --- a/azure-pipelines-PR.yml +++ b/azure-pipelines-PR.yml @@ -248,7 +248,7 @@ stages: continueOnError: true inputs: PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' - ArtifactName: 'Windows Release $(_testKind) process dumps' + ArtifactName: 'Windows Release WindowsLangVersionPreview process dumps' ArtifactType: Container parallel: true @@ -288,7 +288,7 @@ stages: continueOnError: true inputs: PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' - ArtifactName: 'Windows Release $(_testKind) process dumps' + ArtifactName: 'Windows Release WindowsNoRealsig_testCoreclr process dumps' ArtifactType: Container parallel: true @@ -328,7 +328,7 @@ stages: continueOnError: true inputs: PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' - ArtifactName: 'Windows Release $(_testKind) process dumps' + ArtifactName: 'Windows Release WindowsNoRealsig_testDesktop process dumps' ArtifactType: Container parallel: true @@ -368,7 +368,7 @@ stages: continueOnError: true inputs: PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' - ArtifactName: 'Windows Release $(_testKind) process dumps' + ArtifactName: 'Windows Release WindowsStrictIndentation process dumps' ArtifactType: Container parallel: true @@ -404,7 +404,7 @@ stages: continueOnError: true inputs: PathToPublish: '$(Build.SourcesDirectory)\artifacts\log\Release' - ArtifactName: 'Windows Release $(_testKind) process dumps' + ArtifactName: 'Windows Release WindowsNoStrictIndentation process dumps' ArtifactType: Container parallel: true From 95fe045af5b105c27ac401cc99a396c755031ae7 Mon Sep 17 00:00:00 2001 From: Ch13 Date: Wed, 27 Nov 2024 16:11:21 +0300 Subject: [PATCH 13/13] Add null annotation to Async.SwitchToContext (#18059) --- docs/release-notes/.FSharp.Core/9.0.200.md | 1 + src/FSharp.Core/async.fs | 4 ---- src/FSharp.Core/async.fsi | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/.FSharp.Core/9.0.200.md b/docs/release-notes/.FSharp.Core/9.0.200.md index 2ea077abe4b..fdcc65f5537 100644 --- a/docs/release-notes/.FSharp.Core/9.0.200.md +++ b/docs/release-notes/.FSharp.Core/9.0.200.md @@ -1,6 +1,7 @@ ### Fixed * Fix exception on Post after MailboxProcessor was disposed ([Issue #17849](/~https://github.com/dotnet/fsharp/issues/17849), [PR #17922](/~https://github.com/dotnet/fsharp/pull/17922)) +* Fix missing null annotation in Async.SwitchToContext ([Issue #18055](/~https://github.com/dotnet/fsharp/issues/18055), [PR #18059](/~https://github.com/dotnet/fsharp/pull/18059)) ### Added diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs index 1c5138b93dc..959d47d0aab 100644 --- a/src/FSharp.Core/async.fs +++ b/src/FSharp.Core/async.fs @@ -868,11 +868,7 @@ module AsyncPrimitives = /// - Initial cancellation check /// - Call syncCtxt.Post with exception protection. This may fail as it is arbitrary user code -#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT let CreateSwitchToAsync (syncCtxt: SynchronizationContext) = -#else - let CreateSwitchToAsync (syncCtxt: SynchronizationContext | null) = -#endif MakeAsyncWithCancelCheck(fun ctxt -> ctxt.PostWithTrampoline syncCtxt ctxt.cont) /// - Initial cancellation check diff --git a/src/FSharp.Core/async.fsi b/src/FSharp.Core/async.fsi index 1cd02f9694b..e01ba0d8d75 100644 --- a/src/FSharp.Core/async.fsi +++ b/src/FSharp.Core/async.fsi @@ -647,7 +647,7 @@ namespace Microsoft.FSharp.Control /// Threads and Contexts /// /// - static member SwitchToContext : syncContext:System.Threading.SynchronizationContext -> Async + static member SwitchToContext : syncContext:(System.Threading.SynchronizationContext | null) -> Async /// Creates an asynchronous computation that captures the current /// success, exception and cancellation continuations. The callback must