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

Statically resolved type parameters don't propagate correctly, and hashed typenames like ^?9104 in unjust compile error #3508

Closed
abelbraaksma opened this issue Aug 26, 2017 · 6 comments
Labels
Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Milestone

Comments

@abelbraaksma
Copy link
Contributor

I must admit, I have seen this error before, but I have always managed to somehow mysteriously fix it, but now I'm stymied and I decided to report it.

The issue is: compile errors are raised on missing type parameters, but the error shows a garbled name which makes it virtually impossible to understand what is going on.

In addition, in this particular scenario, the error is incorrect, I think this source code should compile.

Repro steps

To repro, just copy the following code and try to compile, the error is on the last line.

type MyByte = MyByte of byte with
    static member MinValue = MyByte Byte.MinValue
    static member MaxValue = MyByte Byte.MaxValue
    static member op_Explicit (MyByte x): int64 = int64 x
    static member op_Explicit (MyByte x): double = double x

let inline isWithinMinMaxRange< ^b, ^a when 
                    ^b: comparison 
                    and ^a: comparison 
                    and ^b: (static member MinValue: ^b) 
                    and ^b: (static member MaxValue: ^b)
                    and ^b : (static member op_Explicit: ^b -> ^a) > 
                    (value: ^a) =

    let minRange = ((^b) : (static member MinValue: ^b) ())
    let maxRange = ((^b) : (static member MaxValue: ^b) ())
    let cast v = (^b : (static member op_Explicit: ^b -> ^a) v)

    value > cast minRange && value < cast maxRange

// compiles and runs fine, returns false
let test() = 
    isWithinMinMaxRange<MyByte, _> 256.0 
    
// Does not compile, FS0001 and FS0043 on the last line
let test<  ^source, ^value when 
        ^source: comparison 
        and ^value: comparison 
        and ^source: (static member MinValue: ^source) 
        and ^source: (static member MaxValue: ^source)
        and ^source : (static member op_Explicit: ^source -> ^value)> (v: ^value) =
    isWithinMinMaxRange< ^source, ^value> v     // error FS0001/FS0043: A type parameter is missing a constraint 'when  ^source : (static member op_Explicit :  ^source ->  ^?9104)'

Expected behavior

This should either compile just fine, or give an error that shows specifically what type parameter constraint is missing. The hashed names should not be shown. However, in this case, I don't think there's a constraint missing, as the call-site has equal type parameter constraints as the definition.

Actual behavior

It throws both FS0001 and FS0043, with equal texts:

error FS0001: A type parameter is missing a constraint 'when ^source : (static member op_Explicit : ^source -> ^?9104)'

and
error FS0043: A type parameter is missing a constraint 'when ^source : (static member op_Explicit : ^source -> ^?9104)'

Known workarounds

None, unfortunately (except for dynamic type resolution, but that is not an option here).

Related information

@abelbraaksma abelbraaksma changed the title Statically resolved type parameters don't propagate correctly, nasty unresolvable and unnecessary compile error: FS0001: A type parameter is missing a constraint 'when ^source : (static member op_Explicit : ^source -> ^?9104)' Statically resolved type parameters don't propagate correctly, and hashed typenames in compile error, seemingly impossible to understand or resolve Aug 26, 2017
@abelbraaksma abelbraaksma changed the title Statically resolved type parameters don't propagate correctly, and hashed typenames in compile error, seemingly impossible to understand or resolve Statically resolved type parameters don't propagate correctly, and hashed typenames like ^?9104 in compile error Aug 26, 2017
@abelbraaksma abelbraaksma changed the title Statically resolved type parameters don't propagate correctly, and hashed typenames like ^?9104 in compile error Statically resolved type parameters don't propagate correctly, and hashed typenames like ^?9104 in unjust compile error Aug 26, 2017
@dsyme
Copy link
Contributor

dsyme commented Aug 29, 2017

@abelbraaksma Is there a repro without using op_Explicit constraints? thanks

@dsyme dsyme added Area-Compiler Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. labels Aug 29, 2017
@abelbraaksma
Copy link
Contributor Author

@dsyme, yes, it happens with basically any method. But perhaps more likely with op_Explicit because that is usually used to be able to create an explicit cast (or, in other words, a member that has a different return type but the same parameters, which I believe is not allowed with any other operator, except op_Implicit).

Example with op_Implicit:

type MyByte = MyByte of byte with
    static member MinValue = MyByte Byte.MinValue
    static member op_Implicit (MyByte x): int64 = int64 x
    static member op_Implicit (MyByte x): double = double x

let inline isWithinMinMaxRange< ^b, ^a when 
                    ^b: comparison 
                    and ^a: comparison 
                    and ^b: (static member MinValue: ^b) 
                    and ^b : (static member op_Implicit: ^b -> ^a) > 
                    (value: ^a) =

    let minRange = ((^b) : (static member MinValue: ^b) ())
    let cast v = (^b : (static member op_Implicit: ^b -> ^a) v)

    value > cast minRange

// Does not compile, FS0001 and FS0043 on the last line
let inline test<  ^source, ^value when 
        ^source: comparison 
        and ^value: comparison 
        and ^source: (static member MinValue: ^source)
        and ^source : (static member op_Implicit: ^source -> ^value) > (v: ^value) =
    isWithinMinMaxRange< ^source, ^value> v   // error FS001 and FS0043 shown here with same text

Error:

error FS0001: A type parameter is missing a constraint 'when ^source : (static member op_Implicit : ^source -> ^?9809)'
error FS0043: A type parameter is missing a constraint 'when ^source : (static member op_Implicit : ^source -> ^?9809)'

Example with a regular method (this code, however, is deliberately wrong, but it shows the error is faulty, or at the very least hard to understand):

type MyByte = MyByte of byte with
    static member MinValue = MyByte Byte.MinValue
    static member Foo (MyByte x): int64 = int64 x

let inline isWithinMinMaxRange< ^b, ^a when 
                    ^b: comparison 
                    and ^a: comparison 
                    and ^b: (static member MinValue: ^b) 
                    and ^b : (static member Foo: ^b -> ^a) > 
                    (value: ^a) =

    let minRange = ((^b) : (static member MinValue: ^b) ())
    let cast v = (^b : (static member Foo: ^b -> ^a) v)

    value > cast minRange

// Does not compile, FS0001 and FS0043 on the last line
let inline test<  ^source, ^value when 
        ^source: comparison 
        and ^value: comparison 
        and ^source: (static member MinValue: ^source)> (v: ^value) =
    isWithinMinMaxRange< ^source, ^value> v

Error:

error FS0001: A type parameter is missing a constraint 'when ^source : (static member Foo : ^source -> ^?9796)'
error FS0043: A type parameter is missing a constraint 'when ^source : (static member Foo : ^source -> ^?9796)'

Of course, in the latter example, the member constraint is actually missing in the declaration of test. However, it shows that a strange/wrong error is shown.

It helps perhaps to show what I tried to achieve. I wanted to create a generic function with statically resolved type parameters, that can do a range check (and some other checks, removed for brevity) on a given type. Just some ducktyping, essentially. But for ease of use, I also hoped to be able to parameterize only the source type, as the target type would be known by the compiler from its argument.

Here's what I thought ought to work, the original idea (note the missing type declarations on the declaration of test):

type MyByte = MyByte of byte with
    static member MinValue = MyByte Byte.MinValue
    static member op_Explicit (MyByte x): int64 = int64 x
    static member op_Explicit (MyByte x): double = double x

let inline isWithinMinMaxRange< ^b, ^a when 
                    ^b: comparison 
                    and ^a: comparison 
                    and ^b: (static member MinValue: ^b) 
                    and (^b) : (static member op_Explicit: ^b -> ^a) > 
                    (value: ^a) =

    let minRange = ((^b) : (static member MinValue: ^b) ())
    let cast v = (( ^b) : (static member op_Explicit: ^b -> ^a) v)

    value > cast minRange

let inline test v =
    isWithinMinMaxRange<MyByte, _> v

I think this code should compile, but it throws a compile error on the last line:

error FS0043: The type 'MyByte' does not support a conversion to the type ''a'

Which made add such method (a dummy to do a conversion to an unknown target type), which ultimately led to #3509 (crash of FSC). I think, still, that the code above is valid and that the FS0043 error should not be raised.

I have meanwhile found a workaround:

Since such code is only possible with using either op_Explicit or op_Implicit (because of limitations on return type variance), I noticed that, even if you only need a conversion in one direction (type ^b to ^a in this example), you must use the or syntax with both types to get it to work, as in (^a or ^b) .....

So, given the same example, this works:

type MyByte = MyByte of byte with
    static member MinValue = MyByte Byte.MinValue
    static member op_Explicit (MyByte x): int64 = int64 x
    static member op_Explicit (MyByte x): double = double x

let inline isWithinMinMaxRange< ^b, ^a when 
                    ^b: comparison 
                    and ^a: comparison 
                    and ^b: (static member MinValue: ^b) 
                    and (^a or ^b) : (static member op_Explicit: ^b -> ^a) > 
                    (value: ^a) =

    let minRange = ((^b) : (static member MinValue: ^b) ())
    let cast v = ((^a or ^b) : (static member op_Explicit: ^b -> ^a) v)

    value > cast minRange

let inline test v =
    isWithinMinMaxRange<MyByte, _> v 

It compile fine and runs fine when used.

That still leaves us with the two findings here:

  • An unintelligible error (two of them, actually)
  • Code that should compile but doesn't (and you may argue that the compiler issues a warning on op_Explicit and that this is what can happen, the same problem happens with op_Implicit, which doesn't give the warning, but shows the same problems).

@cartermp cartermp added this to the Unknown milestone Aug 25, 2018
@abelbraaksma
Copy link
Contributor Author

Just checking if my bug is still valid. As of 15.9.2, the behavior with the unintelligible error is still apparent, so yes:

image

@brianberns
Copy link

I see that this has been in the backlog for a while, and just want to bump it up if possible. I find that I run into it very easily when I try to do basic generic arithmetic, such as:

let inline plus< ^a, ^b, ^c when (^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)> (x : ^a) (y : ^b) =
    x + y   // A type parameter is missing a constraint 'when ( ^a or  ^?1493953) : (static member ( + ) :  ^a *  ^?1493953 ->  ^?1493954)'

@dsyme
Copy link
Contributor

dsyme commented Nov 5, 2021

Treating this as a duplicate of #385

@dsyme dsyme closed this as completed Nov 5, 2021
@abelbraaksma
Copy link
Contributor Author

Reopening this one, as the duplicate was resolved, but that resolution did not fix this issue. The original code still raises the weird numbered types in exceptions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Projects
None yet
Development

No branches or pull requests

4 participants