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

Unnecessary(?) breaking change in FSharp.Core 9.0.100: ofObj now requires not struct as well as null #18320

Closed
pleaseletmesearch opened this issue Feb 14, 2025 · 4 comments · Fixed by #18323
Assignees
Milestone

Comments

@pleaseletmesearch
Copy link

This code works with FSharp.Core 8.0.100:

module Foo =
    let failIfNull<'a when 'a : null> (a : 'a) : 'a =
        a |> Option.ofObj |> Option.orFail "hi"

In FSharp.Core 9.0.100, it does not compile:

error FS0001: Type mismatch. Expecting a� ''a -> 'b' �but given a� ''a -> 'a option' �A type parameter is missing a constraint 'when 'a: not struct'

The null constraint should surely already imply not struct? The docs at https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/constraints say "This [not null] generic constraint does allow value types, since those can never be null.", which suggests that the null constraint logically implies the not struct constraint.

Repro steps

Thing.fsproj

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>netstandard2.0</TargetFramework></PropertyGroup>
<ItemGroup><Compile Include="Thing.fs" /></ItemGroup>
<ItemGroup><PackageReference Update="FSharp.Core" Version="9.0.100" /></ItemGroup>
</Project>

Thing.fs

namespace Blah

module Foo =
    let inline orFail (err:  string) (option : 'a Option) =
        match option with
        | Some a -> a
        | None -> failwith err

    let failIfNull<'a when 'a : null> (a : 'a) : 'a =
        a |> Option.ofObj |> orFail "hi"

Observe the compile error; then downgrade FSharp.Core to 8.0.100 and observe the compile success.

Known workarounds

  • Edit user code to add the (apparently redundant?) constraint not struct.
  • Don't upgrade FSharp.Core.

Provide a description of any known workarounds.

Related information

I'm using SDK 9.0.200 on linux-x64.

@vzarytovskii
Copy link
Member

vzarytovskii commented Feb 15, 2025

Nullable adds this constraint, it is intentional, so it's by design as of now.

@smoothdeveloper
Copy link
Contributor

I faced this but didn't look too much after trying to minimize my code.

I personally think 'a : null implies 'a : not struct so it would be nice if the type checker would add it implicitly in such case.

@Smaug123
Copy link
Contributor

Is there an example of something that's null but also fails to be not struct?

@T-Gro
Copy link
Member

T-Gro commented Feb 17, 2025

The 'not struct' is already automatically emitted in IL for any code that uses 'null' constraint.
Also the constraints are automatically inferred when consuming functions that require it.
The exposure to this issue should be therefore limited to code which uses explicit annotation of type parameters, and does not use their automatic inference from called functions.

That being said, I think we do have a precedence where comparison implies equality.
The constraint will be for sure there (it's needed by design), but it could be implied in the case of explicit typar constraints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
5 participants