diff --git a/.config/service-branch-merge.json b/.config/service-branch-merge.json index b52ba67be18..97c573af195 100644 --- a/.config/service-branch-merge.json +++ b/.config/service-branch-merge.json @@ -1,19 +1,6 @@ { "merge-flow-configurations": { - // regular branch flow - "release/dev17.12": { - "MergeToBranch": "release/dev17.13", - "ExtraSwitches": "-QuietComments" - }, - "release/dev17.13": { - "MergeToBranch": "release/dev17.14", - "ExtraSwitches": "-QuietComments" - }, - "release/dev17.14": { - "MergeToBranch": "release/dev18.0", - "ExtraSwitches": "-QuietComments" - }, - "release/dev18.0": { + "release/10.0.4xx": { "MergeToBranch": "main", "ExtraSwitches": "-QuietComments", "ResetToTargetPaths": [ @@ -26,6 +13,18 @@ "azure-pipelines.yml", "azure-pipelines-PR.yml" ] + }, + "main": { + "MergeToBranch": "feature/net12-scouting", + "ExtraSwitches": "-QuietComments", + "ResetToTargetPaths": [ + "global.json", + "eng/Version.Details.xml", + "eng/Version.Details.props", + "eng/Versions.props", + "eng/common/**", + "eng/TargetFrameworks.props" + ] } } } diff --git a/.github/agents/release-notes-to-narrated-slides.md b/.github/agents/release-notes-to-narrated-slides.md new file mode 100644 index 00000000000..5cf36d61cdd --- /dev/null +++ b/.github/agents/release-notes-to-narrated-slides.md @@ -0,0 +1,26 @@ +--- +name: Release notes to narrated slides +description: Turn a URL to written release notes into a short slide deck plus the script a presenter speaks over it. Inputs - release-notes URL, talk length in minutes. +--- + +# Release notes to narrated slides + +Wrapper around [OnurGumus/codebase-video at commit 9deddc3](https://github.com/OnurGumus/codebase-video/tree/9deddc3f7b3a414650966850201b5da703367740). Download exactly that commit, not a branch head. Read its `SKILL.md` and engine notes, and use its setup, rendering and checks. Replace Claude-specific calls with your own. Do not copy it here. +It narrates with a synthetic voice. Here the presenter speaks, so you produce slides and a script, not audio. +To find release content, the [F# release announcement agent](fsharp-release-announcement.md) can run first. + +**Inputs:** release-notes URL, minutes. Ask once with the ask tool: presenter name and team, audience, deep-dive topics, topics to skip, a one-line motivation, highlights of the previous release, and a design template (a style description, images or a URL). Never infer a team from a handle. Without answers, state your defaults. + +**Output:** step-through slides (self-contained HTML plus PDF; no .pptx) and a script holding only the words to say, as speaker notes and a separate file. Show a rendered draft before the presenter records. + +## Rules for release notes + +- **Length:** about 30 s per slide, 1.8 spoken words per second, within the limit. Say the estimate is unverified until the presenter records. +- **Slide 1:** thanks; name and team only if known. One line on the preceding preview or RC of the same product with "go read those notes"; verify it, keep the presenter's claim, flag mismatches in the report. End with the agenda in one sentence. +- **Story:** lead with the benefit. One beat per slide, headline claim or number in the title, a one-sentence bridge at the end of each note. Depth on the presenter's topics; everything else on one "also" slide. Housekeeping goes in a footnote. +- **Evidence:** follow linked articles, tables and charts that back a slide. Show tables complete, including unchanged rows. Compile and run every code sample you show on the SDK the notes name, else mark it unverified. Show real compiler errors. Never retype numbers or code. +- **Narration:** say what the team did, then the range, never cherry-picked points. Caveats go on the slide, not in speech. Say preview, prototype, or not production once per topic. Separate what ships from copy-from-repo prototypes. Never promise speed; say "experiment and measure". +- **Voice:** natural first person, motivation before mechanism, the presenter's framing in plain terms. Do not invent technical claims. +- **Template:** follow its rules on type size and colour. It may be confidential: keep derived art, fonts and colours out of the repository. +- **Check:** render and look at every slide. Reject overflow, off-slide content, and text under the template's minimum (24 px at 1920 px wide if none). +- **Finish with a report:** recap verified, samples run (N/N), slides visually checked, claims you could not verify. diff --git a/docs/release-notes/.FSharp.Core/11.0.200.md b/docs/release-notes/.FSharp.Core/11.0.200.md index 5240f757be4..23a4bea0427 100644 --- a/docs/release-notes/.FSharp.Core/11.0.200.md +++ b/docs/release-notes/.FSharp.Core/11.0.200.md @@ -4,4 +4,6 @@ ### Fixed +* Quotation debug text from `Expr.ToString()`, `Expr.ToString(full)`, and `%A` now prints struct tuple construction as `NewStructTuple`; reference tuples remain `NewTuple`. ([Issue #20800](https://github.com/dotnet/fsharp/issues/20800), [PR #20814](https://github.com/dotnet/fsharp/pull/20814)) + ### Changed diff --git a/eng/TargetFrameworks.props b/eng/TargetFrameworks.props index 2bea1ee3384..bd879d7f3fd 100644 --- a/eng/TargetFrameworks.props +++ b/eng/TargetFrameworks.props @@ -16,6 +16,9 @@ $([System.Text.RegularExpressions.Regex]::Replace('$(FSharpNetCoreProductTargetFramework)', '^net(\d+)\.0$', '$1')) + + 12.0 + net10.0 diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 8251142a313..dc56a1c1225 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props - 12.0.0-beta.26505.2 + 12.0.0-beta.26509.4 10.0.14 10.0.14 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index fead4e0cd63..ba06132dda3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -77,9 +77,9 @@ - + https://github.com/dotnet/arcade - 5a63d054ea793bdbdb6aa9f4558558e3a8f41529 + 7dad90156e9d69e991ad499ece9cf805cabcf506 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 305132a6d6d..64c5ed10263 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -181,10 +181,6 @@ try { if (-not $PSBoundParameters.ContainsKey('nodeReuse')) { $nodeReuse = $false } - # MSBuild's multi-threaded mode isn't run on CI unless it was explicitly requested via -msbuildMultiThreaded. - if (-not $PSBoundParameters.ContainsKey('msbuildMultiThreaded')) { - $msbuildMultiThreaded = $false - } } if (-not [string]::IsNullOrEmpty($binaryLogName)) { diff --git a/eng/common/core-templates/steps/astred-artifacts.yml b/eng/common/core-templates/steps/astred-artifacts.yml index b27a54e08ef..86420925b44 100644 --- a/eng/common/core-templates/steps/astred-artifacts.yml +++ b/eng/common/core-templates/steps/astred-artifacts.yml @@ -21,7 +21,7 @@ steps: FeedUrl: 'https://pkgs.dev.azure.com/dnceng/_packaging/dotnet-internal-FoSSE/nuget/v3/index.json' - pwsh: | - $ErrorActionPreference = 'Continue' + $ErrorActionPreference = 'Stop' $apjOut = Join-Path $env:ASTRED_CAPTURE_PATH 'apj' New-Item -ItemType Directory -Force -Path $apjOut | Out-Null @@ -34,10 +34,13 @@ steps: $project = Join-Path $apjOut '.astred.project.json' $binlogPaths = @($binlogs.FullName) + $responseFile = Join-Path $apjOut 'binlogs.rsp' + $binlogPaths | ForEach-Object { '"{0}"' -f $_ } | + Set-Content -LiteralPath $responseFile -Encoding utf8 Write-Host "astproj: processing $($binlogPaths.Count) binlog(s)" - astred astproj -nofolders @binlogPaths "-o:$project" + astred astproj -nofolders "@$responseFile" "-o:$project" if ($LASTEXITCODE -ne 0) { - Write-Host "##vso[task.logissue type=warning]astproj failed (exit $LASTEXITCODE)" + throw "astproj failed (exit $LASTEXITCODE)" } displayName: Generate Astred Project Files workingDirectory: ${{ parameters.sourcesPath }} diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1 index 8076945064d..161f8e8077c 100644 --- a/eng/common/msbuild.ps1 +++ b/eng/common/msbuild.ps1 @@ -19,11 +19,6 @@ try { $nodeReuse = $false } - # MSBuild's multi-threaded mode isn't run on CI unless it was explicitly requested via -msbuildMultiThreaded. - if ($ci -and -not $PSBoundParameters.ContainsKey('msbuildMultiThreaded')) { - $msbuildMultiThreaded = $false - } - MSBuild @extraArgs } catch { diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index cea14279b85..9cb84a8a941 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -31,8 +31,9 @@ # Set to true to reuse msbuild nodes. Recommended to not reuse on CI. [bool]$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { !$ci } -# Set to true to build with MSBuild's multi-threaded mode (-mt). Enabled by default for local builds and not run on CI. -[bool]$msbuildMultiThreaded = if (Test-Path variable:msbuildMultiThreaded) { $msbuildMultiThreaded } else { !$ci } +# Set to true to build with MSBuild's multi-threaded mode (-mt). Enabled by default for both local and CI builds. +# Not applied when building with msbuild.exe (VS MSBuild). +[bool]$msbuildMultiThreaded = if (Test-Path variable:msbuildMultiThreaded) { $msbuildMultiThreaded } else { $true } # Configures warning treatment in msbuild. [bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } @@ -812,8 +813,9 @@ function MSBuild() { $cmdArgs = "$($buildTool.Command) /m /nologo /clp:Summary /v:$verbosity /nr:$nodeReuse /p:ContinuousIntegrationBuild=$ci" - # Build with MSBuild's multi-threaded mode. - if ($msbuildMultiThreaded) { + # Build with MSBuild's multi-threaded mode, but not with msbuild.exe (VS MSBuild) for now. + # TODO: Enable for VS MSBuild as well: https://github.com/dotnet/dotnet/issues/9989 + if ($msbuildMultiThreaded -and [System.IO.Path]::GetFileName($buildTool.Path) -ne 'msbuild.exe') { $cmdArgs += ' -mt' } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index fdbc19243f1..3b4a5ac5142 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -60,13 +60,9 @@ else node_reuse=${node_reuse:-true} fi -# Set to true to build with MSBuild's multi-threaded mode (-mt). Enabled by default for local builds and not run on CI. +# Set to true to build with MSBuild's multi-threaded mode (-mt). Enabled by default for both local and CI builds. msbuild_multi_threaded=$(NormalizeBoolArg "${msbuild_multi_threaded:-}") -if [[ "$ci" == true ]]; then - msbuild_multi_threaded=${msbuild_multi_threaded:-false} -else - msbuild_multi_threaded=${msbuild_multi_threaded:-true} -fi +msbuild_multi_threaded=${msbuild_multi_threaded:-true} # Configures warning treatment in msbuild. warn_as_error=$(NormalizeBoolArg "${warn_as_error:-}") diff --git a/global.json b/global.json index c01cb2bb7fc..07c8230ed59 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "11.0.100-rc.1.26420.103", + "version": "12.0.100-alpha.1.26480.102", "allowPrerelease": true, "rollForward": "latestMinor", "paths": [ @@ -13,7 +13,7 @@ "runner": "Microsoft.Testing.Platform" }, "tools": { - "dotnet": "11.0.100-rc.1.26420.103", + "dotnet": "12.0.100-alpha.1.26480.102", "vs": { "version": "18.0", "components": [ @@ -23,7 +23,7 @@ "xcopy-msbuild": "18.0.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "12.0.0-beta.26505.2", + "Microsoft.DotNet.Arcade.Sdk": "12.0.0-beta.26509.4", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23255.2" } } diff --git a/src/FSharp.Core/quotations.fs b/src/FSharp.Core/quotations.fs index 4ac472877e2..dfc74644b0c 100644 --- a/src/FSharp.Core/quotations.fs +++ b/src/FSharp.Core/quotations.fs @@ -378,7 +378,13 @@ and [] Expr( | CombTerm(NewRecordOp ty, args) -> combL "NewRecord" (typeL ty :: exprs args) | CombTerm(NewUnionCaseOp unionCase, args) -> combL "NewUnionCase" (ucaseL unionCase :: exprs args) | CombTerm(UnionCaseTestOp unionCase, args) -> combL "UnionCaseTest" (exprs args @ [ ucaseL unionCase ]) - | CombTerm(NewTupleOp _, args) -> combL "NewTuple" (exprs args) + | CombTerm(NewTupleOp ty, args) -> + combL + (if ty.IsValueType then + "NewStructTuple" + else + "NewTuple") + (exprs args) | CombTerm(TupleGetOp(_, i), [ arg ]) -> combL "TupleGet" ([ expr arg ] @ [ objL i ]) | CombTerm(ValueOp(v, _, Some nm), []) -> combL "ValueWithName" [ objL v; wordL (tagLocal nm) ] | CombTerm(ValueOp(v, _, None), []) -> combL "Value" [ objL v ] diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs index dfe128da71d..c2230089d1e 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs @@ -49,6 +49,7 @@ $(POUND_R) $(TARGETFRAMEWORK) + 12.0 $(RUNTIMEIDENTIFIER) false true diff --git a/tests/EndToEndBuildTests/MultithreadedTasks/Fixture.fsproj b/tests/EndToEndBuildTests/MultithreadedTasks/Fixture.fsproj index c6386be1144..398bab9cb8b 100644 --- a/tests/EndToEndBuildTests/MultithreadedTasks/Fixture.fsproj +++ b/tests/EndToEndBuildTests/MultithreadedTasks/Fixture.fsproj @@ -1,6 +1,6 @@ - net11.0 + $(FSharpNetCoreProductTargetFramework) Exe false true diff --git a/tests/EndToEndBuildTests/MultithreadedTasks/run.fsx b/tests/EndToEndBuildTests/MultithreadedTasks/run.fsx index 6f8d2a1b6a7..125abe00d8a 100644 --- a/tests/EndToEndBuildTests/MultithreadedTasks/run.fsx +++ b/tests/EndToEndBuildTests/MultithreadedTasks/run.fsx @@ -53,12 +53,17 @@ let succeed label arguments = let _, sdk = succeed "sdk" [ "--version" ] let _, msbuild = succeed "msbuild" [ "msbuild"; "-version"; "-nologo" ] +let tfm = + let props = File.ReadAllText(Path.Combine(repo, "eng", "TargetFrameworks.props")) + let matched = Regex.Match(props, @"]*>(net\d+\.\d+)") + if not matched.Success then failwith "Could not determine FSharpNetCoreProductTargetFramework from eng/TargetFrameworks.props" + matched.Groups[1].Value let sdkPath = Path.Combine(repo, ".dotnet", "sdk", sdk.Trim()) -let product = Path.Combine(repo, "artifacts", "bin", "fsc", configuration, "net11.0") +let product = Path.Combine(repo, "artifacts", "bin", "fsc", configuration, tfm) let taskProduct = Path.Combine(repo, "artifacts", "bin", "FSharp.Build", configuration, "netstandard2.0") let taskAssembly = Path.Combine(taskProduct, "FSharp.Build.dll") let compiler = Path.Combine(product, "fsc.dll") -let localFsi = Path.Combine(repo, "artifacts", "bin", "fsi", configuration, "net11.0", "fsi.dll") +let localFsi = Path.Combine(repo, "artifacts", "bin", "fsi", configuration, tfm, "fsi.dll") for path in [ taskAssembly; compiler; localFsi ] do if not (File.Exists path) then failwithf "Build local product bits first: missing %s" path let taskHash = hash taskAssembly @@ -67,18 +72,18 @@ write (Path.Combine(root, "versions.txt")) $"SDK {sdk}MSBuild {msbuild}Tasks {ta // An ordinary SDK helper project uses only assemblies supplied by the running SDK, without NuGet packages. let helper = Path.Combine(root, "probe") -write (Path.Combine(root, "Directory.Build.props")) "false" +write (Path.Combine(root, "Directory.Build.props")) $"""false""" write (Path.Combine(root, "Directory.Build.targets")) "" write (Path.Combine(helper, "Probe.cs")) (File.ReadAllText(Path.Combine(__SOURCE_DIRECTORY__, "Probe.cs"))) write (Path.Combine(helper, "Probe.csproj")) $""" - net11.0false + {tfm}false {xml sdkPath}/Microsoft.Build.Framework.dllfalse {xml sdkPath}/Microsoft.Build.Utilities.Core.dllfalse """ succeed "probe-build" [ "build"; Path.Combine(helper, "Probe.csproj"); "-nologo"; "-v:minimal"; "-nr:false" ] |> ignore -let probeAssembly = Path.Combine(helper, "bin", "Debug", "net11.0", "Probe.dll") +let probeAssembly = Path.Combine(helper, "bin", "Debug", tfm, "Probe.dll") let graph = Path.Combine(root, "graph") let projects = [| for index in 1 .. projectCount -> sprintf "P%02d" index |] write (Path.Combine(graph, "Directory.Build.props")) $""" @@ -192,10 +197,10 @@ let validateOutputs label = for file in [ "fsi.txt"; "obj/substituted/template.txt" ] do if File.ReadAllText(Path.Combine(directory, file)) <> name then failwithf "%s: wrong %s" name file for file, marker in [ "Strings.fs", "RESX_" + name; "ILLink.Substitutions.xml", $"fullname=\"{name}\"" ] do - if not (File.ReadAllText(Path.Combine(directory, "obj/Release/net11.0", file)).Contains marker) then + if not (File.ReadAllText(Path.Combine(directory, $"obj/Release/{tfm}", file)).Contains marker) then failwithf "%s: generated %s has the wrong marker" name file - succeed (label + "-" + name) [ Path.Combine(directory, "bin", "Release", "net11.0", name + ".dll") ] |> ignore - for child in [ "bin/Release/net11.0"; "obj/Release/net11.0" ] do + succeed (label + "-" + name) [ Path.Combine(directory, "bin", "Release", tfm, name + ".dll") ] |> ignore + for child in [ $"bin/Release/{tfm}"; $"obj/Release/{tfm}" ] do for file in Directory.GetFiles(Path.Combine(directory, child), "*", SearchOption.AllDirectories) do if [ ".dll"; ".pdb"; ".fs"; ".resx"; ".resources"; ".xml" ] |> List.contains (Path.GetExtension file) then yield Path.GetRelativePath(graph, file), hash file ] |> Map.ofList @@ -248,7 +253,7 @@ for mt in [ false; true ] do if failure = "resource" && errors.Length <> 1 then failwithf "%s reported %d errors for one malformed resource" label errors.Length for name in projects |> Array.skip 1 do - let dll = Path.Combine(graph, name, "bin/Release/net11.0", name + ".dll") + let dll = Path.Combine(graph, name, $"bin/Release/{tfm}", name + ".dll") succeed (label + "-" + name) [ dll ] |> ignore if hash dll <> baseline[Path.GetRelativePath(graph, dll)] then failwithf "%s contaminated %s" label name printfn "%s: expected failure; %d unaffected executables match baseline" label (projectCount - 1) @@ -263,7 +268,7 @@ let resourceNames file = [ for handle in metadata.ManifestResources -> metadata.GetString(metadata.GetManifestResource(handle).Name) ] let trimProject = Path.Combine(graph, projects[1]) let metadata names = names |> List.filter (fun (name: string) -> name.StartsWith("FSharpSignature") || name.StartsWith("FSharpOptimization")) |> Set.ofList -let untrimmed = Path.Combine(trimProject, "bin/Release/net11.0", projects[1] + ".dll") +let untrimmed = Path.Combine(trimProject, $"bin/Release/{tfm}", projects[1] + ".dll") let originalMetadata = metadata (resourceNames untrimmed) if originalMetadata.IsEmpty then failwith "Trimming control has no F# metadata" let trimFailures = ResizeArray() diff --git a/tests/FSharp.Build.UnitTests/ResourceTests.fs b/tests/FSharp.Build.UnitTests/ResourceTests.fs index 711e9e2a0ab..c91faabf8d3 100644 --- a/tests/FSharp.Build.UnitTests/ResourceTests.fs +++ b/tests/FSharp.Build.UnitTests/ResourceTests.fs @@ -27,6 +27,7 @@ let private createSdkProjectDirectory () = false {productTfm} + 12.0 {buildDirectory ++ "Microsoft.FSharp.NetSdk.targets"} {initialConfig.FSharpBuild} "{initialConfig.DOTNETFSCCOMPILERPATH}" diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/EncMethodDebugInformationTests.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/EncMethodDebugInformationTests.fs index e7d748c1673..652085644cd 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/EncMethodDebugInformationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/EncMethodDebugInformationTests.fs @@ -329,6 +329,7 @@ let private buildCSharpScratchPdb () = Library {TestFramework.productTfm} + 12.0 portable false true diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Quotations/FSharpQuotations.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Quotations/FSharpQuotations.fs index 4b2c1caf3d3..d354f69c0ac 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Quotations/FSharpQuotations.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Quotations/FSharpQuotations.fs @@ -94,6 +94,115 @@ type FSharpQuotationsTests() = member x.GetConstructorFiltersOutStaticConstructor() = ignore <@ System.Exception() @> + [] + member x.``Issue 20800 - struct tuple quotation printing`` () = + Assert.Equal( + """NewStructTuple (Value (1), Value ("a"))""", + (<@ struct (1, "a") @>).ToString() + ) + + [] + member x.``Issue 20800 - reference tuple quotation printing`` () = + Assert.Equal( + """NewTuple (Value (1), Value ("a"))""", + (<@ (1, "a") @>).ToString() + ) + + static member private TupleLambdas<'T>() = + [ + (<@ fun (x: 'T) -> struct (x, 1) @> :> Expr), + typeof<'T -> struct ('T * int)>, typeof, "NewStructTuple" + (<@ fun (x: 'T) -> (x, 1) @> :> Expr), + typeof<'T -> ('T * int)>, typeof<'T * int>, "NewTuple" + ] + + [] + member x.``Issue 20800 - tuple rendering preserves types patterns and shapes`` () = + let assertPrinted expected (expr: Expr) = + Assert.Equal(expected, expr.ToString()) + Assert.Equal(expected, expr.ToString(false)) + Assert.Equal(expected, expr.ToString(true)) + Assert.Equal(expected, sprintf "%A" expr) + + let assertTuple (expectedType: Type) expectedArgs expected (expr: Expr) = + match expr with + | ExprShape.ShapeCombination(shape, args) -> + Assert.Equal(expectedArgs, args) + let rebuilt = ExprShape.RebuildShapeCombination(shape, args) + for tuple in [expr; rebuilt] do + Assert.Equal(expectedType, tuple.Type) + Assert.Equal(expectedType.IsValueType, tuple.Type.IsValueType) + match tuple with + | NewTuple args -> Assert.Equal(expectedArgs, args) + | _ -> Assert.Fail("Expected NewTuple to match both tuple kinds") + match tuple with + | NewStructTuple args -> + Assert.True(expectedType.IsValueType) + Assert.Equal(expectedArgs, args) + | _ -> Assert.False(expectedType.IsValueType) + assertPrinted expected tuple + | _ -> Assert.Fail("Expected a tuple combination") + + let pairArgs = [ <@@ 1 @@>; <@@ "a" @@> ] + for expectedType, label, expressions in [ + typeof, "NewTuple", + [ (<@ (1, "a") @> :> Expr); <@@ (1, "a") @@>; Expr.NewTuple pairArgs ] + typeof, "NewStructTuple", + [ (<@ struct (1, "a") @> :> Expr); <@@ struct (1, "a") @@> + Expr.NewStructTuple pairArgs + Expr.NewStructTuple(typeof.Assembly, pairArgs) ] + ] do + for expr in expressions do + assertTuple expectedType pairArgs ($"""{label} (Value (1), Value ("a"))""") expr + + for expr, expectedType, args, expected in [ + (<@ struct ((1, "a"), 2) @> :> Expr), typeof, + [ <@@ (1, "a") @@>; <@@ 2 @@> ], + """NewStructTuple (NewTuple (Value (1), Value ("a")), Value (2))""" + (<@ (struct (1, "a"), 2) @> :> Expr), typeof, + [ <@@ struct (1, "a") @@>; <@@ 2 @@> ], + """NewTuple (NewStructTuple (Value (1), Value ("a")), Value (2))""" + (<@ struct (1, "a", true) @> :> Expr), typeof, + [ yield! pairArgs; <@@ true @@> ], + """NewStructTuple (Value (1), Value ("a"), Value (true))""" + (<@ (1, "a", true) @> :> Expr), typeof, + [ yield! pairArgs; <@@ true @@> ], + """NewTuple (Value (1), Value ("a"), Value (true))""" + ] do + assertTuple expectedType args expected expr + + for expr, functionType, tupleType, label in [ + yield! FSharpQuotationsTests.TupleLambdas() + yield! FSharpQuotationsTests.TupleLambdas() + ] do + Assert.Equal(functionType, expr.Type) + assertPrinted $"Lambda (x, {label} (x, Value (1)))" expr + match expr with + | Lambda(v, body) -> + assertTuple tupleType [ Expr.Var v; <@@ 1 @@> ] $"{label} (x, Value (1))" body + | _ -> Assert.Fail("Expected a generic tuple lambda") + + let firstSix = "Value (1), Value (2), Value (3), Value (4), Value (5), Value (6)" + for arity, tail, referenceType, structType, referenceLiteral, structLiteral in [ + 7, "Value (7)", typeof, + typeof, + (<@ (1,2,3,4,5,6,7) @> :> Expr), (<@ struct (1,2,3,4,5,6,7) @> :> Expr) + 8, "Value (7), Value (8)", typeof, + typeof, + (<@ (1,2,3,4,5,6,7,8) @> :> Expr), (<@ struct (1,2,3,4,5,6,7,8) @> :> Expr) + 9, "Value (7), Value (8), Value (9)", typeof, + typeof, + (<@ (1,2,3,4,5,6,7,8,9) @> :> Expr), (<@ struct (1,2,3,4,5,6,7,8,9) @> :> Expr) + ] do + let args = [ for i in 1..arity -> Expr.Value i ] + for expectedType, label, indent, literal, constructed in [ + referenceType, "NewTuple", " ", referenceLiteral, Expr.NewTuple args + structType, "NewStructTuple", " ", structLiteral, Expr.NewStructTuple args + ] do + let expected = $"{label} ({firstSix},\n{indent}{tail})" + for expr in [literal; constructed] do + assertTuple expectedType args expected expr + [] member x.``NewStructTuple literal should be recognized by NewStructTuple active pattern`` () = match <@ struct(1, "") @> with diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 1a8a05f38e3..c5e5e44702c 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -458,6 +458,7 @@ let private renderFsProj (p: SyntheticProject) = Exe {productTfm} + 12.0 diff --git a/tests/FSharp.Test.Utilities/Utilities.fs b/tests/FSharp.Test.Utilities/Utilities.fs index 1db689ca706..51a98ea387b 100644 --- a/tests/FSharp.Test.Utilities/Utilities.fs +++ b/tests/FSharp.Test.Utilities/Utilities.fs @@ -146,6 +146,7 @@ module Utilities = Exe $TARGETFRAMEWORK + 12.0 true true diff --git a/tests/fsharp/single-test.fs b/tests/fsharp/single-test.fs index eddb0dc6080..79c4b62e4aa 100644 --- a/tests/fsharp/single-test.fs +++ b/tests/fsharp/single-test.fs @@ -145,6 +145,7 @@ let generateProjectArtifacts (pc:ProjectConfiguration) outputType (targetFramewo $(OUTPUTTYPE) $(TARGETFRAMEWORK) + 12.0 true false $(DEBUG)