dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.93k stars 786 forks source link

Null value returned from trait call #12386

Open gusty opened 3 years ago

gusty commented 3 years ago

There are some cases where an invalid value is returned from a trait call.

Repro steps

  1. Step A

try in fsi

type A = A with
    static member ($) (A, a: float  ) = 0.0
    static member ($) (A, a: decimal) = 0M
    static member ($) (A, a: 't     ) = 0

let inline call x = ($) A x

send it

  1. Step B

Now try this call 42.

Expected behavior

val it : float = 0.0

Actual behavior

val it : obj = <null>

Known workarounds

I haven't found one so far.

I didn't try in older versions, but I'm under the impression that this is a regression.

gusty commented 3 years ago

And here's a repro to send to FSI in one shot

type A = A with
    static member ($) (A, a: float  ) = 0.0
    static member ($) (A, a: decimal) = 0M
    static member ($) (A, a: 't     ) = 0

let inline call x = ($) A x

call 42.  + 1.1

Which results in

type A =
  | A
  with
    static member ( $ ) : A:A * a:'t -> int + 2 overloads
  end
val inline call :
  x: ^a ->  ^_arg3
    when (A or  ^a) : (static member ( $ ) : A *  ^a ->  ^_arg3)
val it : float = nan

when the expeced result should be 1.1

edgarfgp commented 2 years ago

@gusty I have tried today with on a script and on fsi and seems to be fixed now :) FYI @vzarytovskii Screenshot 2022-09-19 at 14 57 33

gusty commented 2 years ago

Yes, it works for me as well. I wonder what was the fixing PR.

vzarytovskii commented 2 years ago

Yes, it works for me as well. I wonder what was the fixing PR.

It is likely that the work on the static abstract interface methods

gusty commented 2 years ago

Hmm, I don't think so. I'm still on F# 6 and it seems fixed.

vzarytovskii commented 2 years ago

Hmm, I don't think so. I'm still on F# 6 and it seems fixed.

Only feature itself is tied to language version, some changes and fixes are not.

Thought if you're on 6.x.x, then it's not it yes

edgarfgp commented 2 years ago

Might be worth adding a regression test 😀

gusty commented 2 years ago

I just tried it again in both F#6 and F#7 my second repro works well.

But the original repro (the one in the issue step by step) still fails in both, surprisingly in different ways:

call 42. ;; val it: obj =

> type A = A with
    static member ($) (A, a: float  ) = 0.0
    static member ($) (A, a: decimal) = 0M
    static member ($) (A, a: 't     ) = 0

let inline call x = ($) A x;;
type A =
  | A
  static member ($) : A: A * a: 't -> int + 2 overloads
val inline call:
  x: ^a -> '_arg3 when (A or ^a) : (static member ($) : A * ^a -> '_arg3)

> call 42. ;;

  call 42. ;;
  ^^^^^^^^

stdin(7,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type
    val it: '_a    
Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

@edgarfgp I think you tried sending all the code to fsi at once. Try it in steps as described in the issue.

edgarfgp commented 2 years ago

@gusty You are right I can reproduce it by sending line by line . But works when sending all at once, seems to be a FSI bug/limitation

Note: I have tried with dotnet fsi --multiemit and does NOT make a difference

gusty commented 2 years ago

@edgarfgp note that this difference between sending all at once is not that uncommon: type inference uses "last minute" information to specialize generic functions. When you create the function and immediately after you use it, the "use" is taken into account. If you send both fragments separately type inference is independent.

gusty commented 1 year ago

Confirmed to be reproducing in F# 8 Microsoft (R) F# Interactive version 12.8.0.0 for F# 8.0 with the value restriction variant described above https://github.com/dotnet/fsharp/issues/12386#issuecomment-1251868590