diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml index 4b76589ea7a..a0eab5042f4 100644 --- a/azure-pipelines-PR.yml +++ b/azure-pipelines-PR.yml @@ -100,7 +100,7 @@ stages: helixRepo: dotnet/fsharp jobs: # Determinism, we want to run it only in PR builds - - job: Determinism_Debug + - job: Determinism_Release condition: eq(variables['Build.Reason'], 'PullRequest') variables: - name: _SignType @@ -129,15 +129,15 @@ stages: workingDirectory: $(Build.SourcesDirectory) installationPath: $(Build.SourcesDirectory)/.dotnet - script: .\eng\common\dotnet.cmd - - script: .\eng\test-determinism.cmd -configuration Debug + - script: .\eng\test-determinism.cmd -configuration Release env: FSHARP_EXPERIMENTAL_FEATURES: $(_experimental_flag) - displayName: Determinism tests with Debug configuration + displayName: Determinism tests with Release configuration - task: PublishPipelineArtifact@1 displayName: Publish Determinism Logs inputs: - targetPath: '$(Build.SourcesDirectory)/artifacts/log/Debug' - artifactName: 'Determinism_Debug Attempt $(System.JobAttempt) Logs' + targetPath: '$(Build.SourcesDirectory)/artifacts/log/Release' + artifactName: 'Determinism_Release Attempt $(System.JobAttempt) Logs' continueOnError: true condition: not(succeeded()) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 7d4162f804e..2f9cee50159 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Improve determinism under parallel optimization: `Optimize/DetupleArgs` and `Optimize/InnerLambdasToTopLevelFuncs` now walk their `Val` sets in stable source-position order before calling into `NiceNameGenerator`, so compiler-generated names no longer vary across builds due to `Val.Stamp` assignment races during parallel type-check. ([Issue #19732](https://github.com/dotnet/fsharp/issues/19732), [PR #19810](https://github.com/dotnet/fsharp/pull/19810)) * Honor `--nowarn` and `--warnaserror` for warnings emitted during command-line option parsing ([Issue #19576](https://github.com/dotnet/fsharp/issues/19576), [PR #19776](https://github.com/dotnet/fsharp/pull/19776)) * Fix `[]` prefix attributes being silently dropped on class members, and fix false-positive `AllowMultiple=false` errors when `[]` and `[]` are applied to the same binding. ([Issue #17904](https://github.com/dotnet/fsharp/issues/17904), [Issue #19020](https://github.com/dotnet/fsharp/issues/19020), [PR #19738](https://github.com/dotnet/fsharp/pull/19738)) * Fix attributes on return type of unparenthesized tuple methods being silently dropped from IL. ([Issue #462](https://github.com/dotnet/fsharp/issues/462), [PR #19714](https://github.com/dotnet/fsharp/pull/19714)) diff --git a/src/Compiler/Driver/OptimizeInputs.fs b/src/Compiler/Driver/OptimizeInputs.fs index 78bca4bf979..a9bedca3b70 100644 --- a/src/Compiler/Driver/OptimizeInputs.fs +++ b/src/Compiler/Driver/OptimizeInputs.fs @@ -511,8 +511,12 @@ let ApplyAllOptimizations let results, optEnvFirstLoop = match tcConfig.optSettings.processingMode with - // Parallel optimization breaks determinism - turn it off in deterministic builds. | Optimizer.OptimizationProcessingMode.Parallel -> + // Determinism under Parallel mode relies on the per-pass sorts in + // DetupleArgs.determineTransforms and InnerLambdasToTopLevelFuncs.CreateNewValuesForTLR + // (via valSourceOrderKey). Any new pass calling NiceNameGenerator from a + // parallel optimizer phase must sort its Val collection the same way. + // See https://github.com/dotnet/fsharp/issues/19732. let results, optEnvFirstPhase = ParallelOptimization.optimizeFilesInParallel optEnv phases implFiles diff --git a/src/Compiler/Optimize/DetupleArgs.fs b/src/Compiler/Optimize/DetupleArgs.fs index b0dd2d62835..e583d16c101 100644 --- a/src/Compiler/Optimize/DetupleArgs.fs +++ b/src/Compiler/Optimize/DetupleArgs.fs @@ -704,7 +704,8 @@ let determineTransforms g (z: Results) = let callPatterns = sitesCPs sites // callPatterns from sites decideTransform g z f callPatterns (m, tps, vss, retTy) // make transform (if required) - let vtransforms = Zmap.chooseL selectTransform z.Uses + let vtransforms = + Zmap.chooseL selectTransform z.Uses let vtransforms = Zmap.ofList valOrder vtransforms vtransforms diff --git a/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fs b/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fs index 00761538123..83401b7a9ae 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fs @@ -44,6 +44,15 @@ module internal ExprConstruction = member _.Compare(v1, v2) = compareBy v1 v2 _.Stamp } + // Source-position-derived order key for Vals. Used to walk Val collections + // in a stable, build-independent order before calling NiceNameGenerator + // from parallel optimizer passes. Stamp is the final tiebreaker for + // synthetic Vals at the same location; stamps are fixed within a single + // process so the order is total. See https://github.com/dotnet/fsharp/issues/19732. + let valSourceOrderKey (v: Val) = + let r = v.Range + struct (r.FileIndex, r.StartLine, r.StartColumn, v.LogicalName, v.Stamp) + let tyconOrder = { new IComparer with member _.Compare(tycon1, tycon2) = compareBy tycon1 tycon2 _.Stamp diff --git a/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fsi b/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fsi index 36942be52f1..09a00276dfe 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.ExprConstruction.fsi @@ -22,6 +22,12 @@ module internal ExprConstruction = /// An ordering for value definitions, based on stamp val valOrder: IComparer + /// Stable, source-position-derived key for ordering Vals. + /// Use this before calling NiceNameGenerator from parallel optimizer passes + /// so the generated names do not depend on Val.Stamp assignment race. + /// See https://github.com/dotnet/fsharp/issues/19732. + val valSourceOrderKey: Val -> struct (int * int * int * string * int64) + /// An ordering for type definitions, based on stamp val tyconOrder: IComparer