Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for F# anonymous records #2530

Closed
psfinaki opened this issue Feb 26, 2024 · 6 comments · May be fixed by #2336
Closed

Support for F# anonymous records #2530

psfinaki opened this issue Feb 26, 2024 · 6 comments · May be fixed by #2336

Comments

@psfinaki
Copy link
Member

Repro

namespace Perf

open BenchmarkDotNet.Running
open BenchmarkDotNet.Attributes

module Main =

    type Test() =

        [<Benchmark>]
        member _.AnonymousRecord() =
            let array = Array.init 5 id
            array |> Array.countBy (fun n -> {| Field = n |})

    [<EntryPoint>]
    let main args = 
        printfn "Running benchmarks..."
        BenchmarkSwitcher.FromAssembly(typeof<Test>.Assembly).Run(args) |> ignore
        0

What happens

// Build Error: Standard output:

 Standard error:
 MSBuild version 17.10.0-preview-23619-02+5cf78584f for .NET
C:\Program Files\dotnet\sdk\9.0.100-alpha.1.23620.14\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(311,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,37): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,37): error CS1003: Syntax error, '(' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,80): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,94): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,94): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,95): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,96): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,114): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,114): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,115): error CS8124: Tuple must contain at least two elements. [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,116): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(186,116): error CS1026: ) expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,38): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,38): error CS1003: Syntax error, '(' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,81): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,95): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,95): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,96): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,97): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,115): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,115): error CS1003: Syntax error, ',' expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,116): error CS8124: Tuple must contain at least two elements. [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,117): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(188,117): error CS1026: ) expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(239,29): error CS1519: Invalid token '<<' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(239,72): error CS1519: Invalid token ',' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(239,86): error CS1519: Invalid token '>' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(239,88): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(318,28): error CS1519: Invalid token '<<' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(318,71): error CS1519: Invalid token ',' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(318,85): error CS1519: Invalid token '>' in class, record, struct, or interface member declaration [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(318,87): error CS1001: Identifier expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,40): error CS1026: ) expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,42): error CS1525: Invalid expression term '>' [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,83): error CS1002: ; expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,83): error CS1513: } expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,100): error CS1002: ; expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]
FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\1a71ca95-94eb-441e-9549-46a619937196.notcs(326,100): error CS1513: } expected [FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196\BenchmarkDotNet.Autogenerated.csproj]

Build FAILED

    38 Error(s)
Time Elapsed 00:00:01.94

// BenchmarkDotNet has failed to build the auto-generated boilerplate code.
// It can be found in FSharpConsoleApp\bin\Release\net8.0\1a71ca95-94eb-441e-9549-46a619937196
// Please follow the troubleshooting guide: https://benchmarkdotnet.org/articles/guides/troubleshooting.html

What should happen

Well something more clear. It should either work or give a clear message. This might be related to the fact that anon records in F# are very generic under the hood and AFAIK BDN has problems with those.

Env

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22621.3155/22H2/2022Update/SunValley2)
11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK 9.0.100-alpha.1.23620.14
  [Host] : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI DEBUG

@adamsitnik
Copy link
Member

Please follow the troubleshooting guide: https://benchmarkdotnet.org/articles/guides/troubleshooting.html

BDN generates a C# project that references the F# project and generates a class that derives from the type that defined the benchmarks (to generate the boilerplate code and run every benchmark in a standalone process to avoid any side-effects affecting other benchmarks). Please use the link provided above and troubleshoot the issue (I suspect that BDN fails to reference the F# method with such name) and send a PR with a fix.

Here is the logic that generates the C# code: https://github.com/dotnet/BenchmarkDotNet/blob/master/src/BenchmarkDotNet/Code/CodeGenerator.cs

Here is the template it's filling:
https://github.com/dotnet/BenchmarkDotNet/blob/master/src/BenchmarkDotNet/Templates/BenchmarkType.txt

@timcassell
Copy link
Collaborator

timcassell commented Feb 27, 2024

I'm guessing F# anonymous records are implemented similar to C# anonymous types where their underlying type name in IL cannot be expressed in C#. If that's the case, there is nothing we can do about it. You will have to wrap it in another type that C# can consume (or up-cast to object), or use an InProcess toolchain.

@psfinaki
Copy link
Member Author

I'm guessing F# anonymous records are implemented similar to C# anonymous types where their underlying type name in IL cannot be expressed in C#. If that's the case, there is nothing we can do about it. You will have to wrap it in another type that C# can consume (or up-cast to object), or use an InProcess toolchain.

Very well might be - in that case yeah, at least a proper error message would be helpful.

I haven't worked with debugging BDN yet but this looks like an interesting opportunity. Will post heads up here if I start working on this.

@timcassell timcassell closed this as not planned Won't fix, can't repro, duplicate, stale Dec 21, 2024
@timcassell
Copy link
Collaborator

I think the only way for this to work would be for someone to create a toolchain that generates F# source code and fsproj the way the CsProjCoreToolchain does. Since this is easily worked around by returning object instead of an anonymous type, I don't think such a toolchain is worth the effort and maintenance costs.

It's also impossible for us to detect build errors such as this before actually running the build, so the only thing we can do is report the build error, which already occurs.

@timcassell
Copy link
Collaborator

From experimenting with #2336, I found that C# is perfectly capable of calling the F# method, as long as the return type is not used at all. It may not be broadly applicable (we still can't use anonymous types for args/params), but it's interesting that this particular case is possible to make work. Of course, that's blocked by the results of #2334, but I thought it could be handy to leave the findings here.

@psfinaki
Copy link
Member Author

Thanks for the investigation @timcassell. Indeed, this probably doesn't have to be addressed in general - even in a few annoying cases (like this one) we handled, it would be nice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants