Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c628564
Initial plan
Copilot Sep 17, 2025
e8d910f
Add compiler compatibility test suite with anonymous records
Copilot Sep 17, 2025
bbefae5
Complete compiler compatibility test suite implementation
Copilot Sep 17, 2025
13ca28d
Final implementation of compiler compatibility test suite
Copilot Sep 17, 2025
c5a2700
Address code review feedback: parametrized tests, global.json support…
Copilot Sep 17, 2025
360ae63
Address code review: fix LoadLocalFSharpBuild, simplify versions, rem…
Copilot Sep 17, 2025
9ea30da
Fix test failures: replace Unicode checkmarks with platform-agnostic …
Copilot Sep 18, 2025
86a9207
Add .NET 9 test cases and build verification info
Copilot Sep 19, 2025
2720a6f
Enhance build verification with detailed compiler tracking
Copilot Sep 19, 2025
91da72a
Fix CI failures by removing .NET 9 dependency
Copilot Sep 19, 2025
9846cef
Merge branch 'main' into copilot/fix-7efdf64f-592c-4ce7-9871-9e7dadc5…
T-Gro Sep 22, 2025
194b5fe
Implement proper build-time generated BuildInfo files with MSBuild ta…
Copilot Sep 22, 2025
8fc9d44
Fix CI compilation errors by converting arrays to lists for String.co…
Copilot Sep 22, 2025
f3cd733
Add comprehensive build verification assertions to prevent regressions
Copilot Sep 22, 2025
8294e80
Remove BuildInfo files from repository - they should be generated at …
Copilot Sep 22, 2025
c2a1464
Merge branch 'main' into copilot/fix-7efdf64f-592c-4ce7-9871-9e7dadc5…
T-Gro Oct 16, 2025
9f5a170
address build errors
T-Gro Oct 16, 2025
ee94052
Make it actually correct via human intervention
T-Gro Oct 17, 2025
4b7d566
Apply suggestion from @T-Gro
T-Gro Oct 17, 2025
c5a8fa3
Merge branch 'main' into copilot/fix-7efdf64f-592c-4ce7-9871-9e7dadc5…
T-Gro Oct 17, 2025
45c0567
Fix SDK version compatibility and rollForward policy
Copilot Oct 17, 2025
44e61f0
Fix all remaining SDK version references to 9.0.300
Copilot Oct 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,13 @@ TestResults/*.trx
StandardOutput.txt
StandardError.txt
**/TestResults/

# CompilerCompat test project generated files
tests/projects/CompilerCompat/**/nuget.config
tests/projects/CompilerCompat/**/global.json
tests/projects/CompilerCompat/**/*.deps.json
tests/projects/CompilerCompat/**/*.xml
tests/projects/CompilerCompat/local-nuget-packages/
tests/projects/CompilerCompat/lib-output-*/
tests/projects/CompilerCompat/**/bin/
tests/projects/CompilerCompat/**/obj/
322 changes: 322 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/CompilerCompatibilityTests.fs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked"]
|> withOptions ["--checked"]
|> compile
|> shouldSucceed

Expand All @@ -33,7 +33,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked+"]
|> withOptions ["--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -43,7 +43,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked-"]
|> withOptions ["--checked-"]
|> compile
|> shouldSucceed

Expand All @@ -53,7 +53,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked-"]
|> withOptions ["--checked-"]
|> compile
|> shouldSucceed

Expand All @@ -63,7 +63,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked"]
|> withOptions ["--checked"]
|> compile
|> shouldSucceed

Expand All @@ -73,7 +73,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked+"]
|> withOptions ["--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -83,7 +83,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked-"]
|> withOptions ["--checked-"]
|> compile
|> shouldSucceed

Expand All @@ -96,7 +96,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked"; "--checked+"]
|> withOptions ["--checked"; "--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -106,7 +106,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked-"; "--checked+"]
|> withOptions ["--checked-"; "--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -116,7 +116,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked+"; "--checked-"]
|> withOptions ["--checked+"; "--checked-"]
|> compile
|> shouldSucceed

Expand All @@ -126,7 +126,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked"; "--checked+"]
|> withOptions ["--checked"; "--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -136,7 +136,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked-"; "--checked+"]
|> withOptions ["--checked-"; "--checked+"]
|> compile
|> shouldSucceed

Expand All @@ -146,7 +146,7 @@ module Checked =
compilation
|> getCompilation
|> asFsx
|> withOptions["--checked+"; "--checked-"]
|> withOptions ["--checked+"; "--checked-"]
|> compile
|> shouldSucceed

Expand All @@ -157,7 +157,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--Checked"]
|> withOptions ["--Checked"]
|> compile
|> shouldFail
|> withDiagnostics [
Expand All @@ -170,7 +170,7 @@ module Checked =
compilation
|> getCompilation
|> asFs
|> withOptions["--checked*"]
|> withOptions ["--checked*"]
|> compile
|> shouldFail
|> withDiagnostics [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@
<Compile Include="FSharpChecker\SymbolUse.fs" />
<Compile Include="FSharpChecker\FindReferences.fs" />
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs" />
<Compile Include="CompilerCompatibilityTests.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions tests/projects/CompilerCompat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ignore test-generated files
global.json
commandline.txt
**/*BuildInfo.fs
StandardOutput.txt
StandardError.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="AppBuildInfo.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<!-- Reference the library as a NuGet package instead of a project reference -->
<!-- This allows testing cross-compiler scenarios where lib and app use different compilers -->
<PackageReference Include="CompilerCompatLib" Version="1.0.0" />
</ItemGroup>

<Target Name="GenerateAppBuildInfo" BeforeTargets="BeforeCompile">
<PropertyGroup>
<IsLocalBuildValue Condition="'$(LoadLocalFSharpBuild)' == 'True'">true</IsLocalBuildValue>
<IsLocalBuildValue Condition="'$(LoadLocalFSharpBuild)' != 'True'">false</IsLocalBuildValue>
<CleanDotnetFscCompilerPath Condition="'$(DotnetFscCompilerPath)' != ''">$(DotnetFscCompilerPath.Replace('&quot;', ''))</CleanDotnetFscCompilerPath>
<CleanDotnetFscCompilerPath Condition="'$(DotnetFscCompilerPath)' == ''">N/A</CleanDotnetFscCompilerPath>
</PropertyGroup>
<WriteLinesToFile
File="AppBuildInfo.fs"
Overwrite="true"
Lines="namespace CompilerCompatApp
module AppBuildInfo =
let sdkVersion = &quot;$(NETCoreSdkVersion)&quot;
let fsharpCompilerPath = &quot;$(FscToolPath)\$(FscToolExe)&quot;
let dotnetFscCompilerPath = &quot;$(CleanDotnetFscCompilerPath)&quot;
let isLocalBuild = $(IsLocalBuildValue)" />
</Target>

</Project>
54 changes: 54 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatApp/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
open CompilerCompatLib
open CompilerCompatApp
open System

[<EntryPoint>]
let main _argv =
try
// Helper to get the actual compiler path (prefer dotnetFscCompilerPath when using local build)
let getActualCompilerPath (dotnetPath: string) (fallbackPath: string) =
if dotnetPath <> "N/A" && dotnetPath <> "" then dotnetPath else fallbackPath

// Print detailed build information to verify which compiler was used
printfn "=== BUILD VERIFICATION ==="
printfn "Library Build Info:"
printfn " SDK Version: %s" LibBuildInfo.sdkVersion
printfn " F# Compiler Path: %s" (getActualCompilerPath LibBuildInfo.dotnetFscCompilerPath LibBuildInfo.fsharpCompilerPath)
printfn " Is Local Build: %b" LibBuildInfo.isLocalBuild
printfn "Application Build Info:"
printfn " SDK Version: %s" AppBuildInfo.sdkVersion
printfn " F# Compiler Path: %s" (getActualCompilerPath AppBuildInfo.dotnetFscCompilerPath AppBuildInfo.fsharpCompilerPath)
printfn " Is Local Build: %b" AppBuildInfo.isLocalBuild
printfn "=========================="

// Test basic anonymous record functionality
let record = Library.getAnonymousRecord()
printfn "Basic record: X=%d, Y=%s" record.X record.Y

// Verify expected values
if record.X <> 42 || record.Y <> "hello" then
printfn "ERROR: Basic record values don't match expected"
1
else
printfn "SUCCESS: Basic record test passed"

// Test complex anonymous record functionality
let complex = Library.getComplexAnonymousRecord()
printfn "Complex record: Simple.A=%d, Simple.B=%s" complex.Simple.A complex.Simple.B
printfn "Complex record: List has %d items" complex.List.Length
printfn "Complex record: Tuple=(%d, Value=%f)" (fst complex.Tuple) (snd complex.Tuple).Value

// Test function that takes anonymous record
let processed = Library.processAnonymousRecord({| X = 123; Y = "test" |})
printfn "Processed result: %s" processed

if processed = "Processed: X=123, Y=test" then
printfn "SUCCESS: All compiler compatibility tests passed"
0
else
printfn "ERROR: Processed result doesn't match expected"
1

with ex ->
printfn "ERROR: Exception occurred: %s" ex.Message
1
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

<!-- Package properties for NuGet pack -->
<IsPackable>true</IsPackable>
<PackageId>CompilerCompatLib</PackageId>
<Version>1.0.0</Version>
<Authors>Test</Authors>
<Description>Test library for compiler compatibility tests</Description>
</PropertyGroup>

<ItemGroup>
<Compile Include="LibBuildInfo.fs" />
<Compile Include="Library.fs" />
</ItemGroup>

<!-- When building with local compiler, include FSharp.Core in the package so consumers can find it -->
<ItemGroup Condition="'$(LoadLocalFSharpBuild)' == 'True'">
<Content Include="$(LocalFSharpCompilerPath)/artifacts/bin/FSharp.Core/$(LocalFSharpCompilerConfiguration)/netstandard2.0/FSharp.Core.dll">
<PackagePath>lib/$(TargetFramework)</PackagePath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>true</Pack>
</Content>
</ItemGroup>

<Target Name="GenerateLibBuildInfo" BeforeTargets="BeforeCompile">
<PropertyGroup>
<IsLocalBuildValue Condition="'$(LoadLocalFSharpBuild)' == 'True'">true</IsLocalBuildValue>
<IsLocalBuildValue Condition="'$(LoadLocalFSharpBuild)' != 'True'">false</IsLocalBuildValue>
<CleanDotnetFscCompilerPath Condition="'$(DotnetFscCompilerPath)' != ''">$(DotnetFscCompilerPath.Replace('&quot;', ''))</CleanDotnetFscCompilerPath>
<CleanDotnetFscCompilerPath Condition="'$(DotnetFscCompilerPath)' == ''">N/A</CleanDotnetFscCompilerPath>
</PropertyGroup>
<WriteLinesToFile
File="LibBuildInfo.fs"
Overwrite="true"
Lines="namespace CompilerCompatLib
module LibBuildInfo =
let sdkVersion = &quot;$(NETCoreSdkVersion)&quot;
let fsharpCompilerPath = &quot;$(FscToolPath)\$(FscToolExe)&quot;
let dotnetFscCompilerPath = &quot;$(CleanDotnetFscCompilerPath)&quot;
let isLocalBuild = $(IsLocalBuildValue)" />
</Target>

</Project>
17 changes: 17 additions & 0 deletions tests/projects/CompilerCompat/CompilerCompatLib/Library.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace CompilerCompatLib

module Library =
/// Returns an anonymous record to test compiler compatibility
let getAnonymousRecord () = {| X = 42; Y = "hello" |}

/// Returns a more complex anonymous record with nested structure
let getComplexAnonymousRecord () =
{|
Simple = {| A = 1; B = "test" |};
List = [ {| Id = 1; Name = "first" |}; {| Id = 2; Name = "second" |} ];
Tuple = (42, {| Value = 3.14; Label = "pi" |})
|}

/// Function that takes an anonymous record as parameter
let processAnonymousRecord (record: {| X: int; Y: string |}) =
sprintf "Processed: X=%d, Y=%s" record.X record.Y
10 changes: 10 additions & 0 deletions tests/projects/CompilerCompat/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project>
<!--
Isolated Directory.Build.props for CompilerCompat test projects.
Prevents import of repository root's Directory.Build.props for isolated build environments.
-->

<PropertyGroup>
<CompilerCompatTestProject>true</CompilerCompatTestProject>
</PropertyGroup>
</Project>
13 changes: 13 additions & 0 deletions tests/projects/CompilerCompat/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project>
<!--
Isolated Directory.Build.targets for CompilerCompat test projects.
This prevents importing the repository root's build configuration, ensuring isolated build environments.

We import UseLocalCompiler here (after SDK targets have been imported) rather than in .props
to ensure the Build target exists before UseLocalCompiler props are applied.
-->

<!-- Import UseLocalCompiler props when testing with local compiler -->
<!-- Note: This is imported in targets file to ensure SDK has been fully initialized -->
<Import Project="../../../UseLocalCompiler.Directory.Build.props" Condition="'$(LoadLocalFSharpBuild)' == 'true'" />
</Project>
Loading