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

Using multiple operators in INumber-constrained generic functions leads to compiler error #17062

Open
En3Tho opened this issue Apr 17, 2024 · 12 comments
Labels
Area-Compiler-Checking Type checking, attributes and all aspects of logic checking Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Milestone

Comments

@En3Tho
Copy link
Contributor

En3Tho commented Apr 17, 2024

Please provide a succinct description of the issue.

// ok
let numericOpsPlus<'T when INumber<'T>>(num: 'T) =
    num + num

// FS0670 - code is not sufficiently generic
let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =
    num + num + num

// ok again
let inline numericOpsPlusPlusInlined<'T when INumber<'T>>(num: 'T) =
    num + num + num
@github-actions github-actions bot added this to the Backlog milestone Apr 17, 2024
@vzarytovskii
Copy link
Member

Note: Works fine when chaining op_Addition calls.

@brianrourkeboll
Copy link
Contributor

For reference — these tests should be extended to cover this:

#nowarn "64"
open Types
module ``Use SRTP from IWSAM generic code`` =
module ``Use SRTP operators from generic IWSAM code`` =
let fAdd<'T when 'T :> IAdditionOperator<'T>>(x: 'T, y: 'T) =
x + y
let fSin<'T when ISinOperator<'T>>(x: 'T) =
sin x
module ``Use SRTP operators from generic IWSAM code not rigid`` =
let fAdd(x: 'T when 'T :> IAdditionOperator<'T>, y: 'T) =
x + y
let fSin(x: 'T when ISinOperator<'T>) =
sin x
module ``Use SRTP operators from generic IWSAM code flex`` =
let fAdd(x: #IAdditionOperator<'T>, y) =
x + y
let fSin(x: #ISinOperator<'T>) =
sin x
module ``Use SRTP operators from generic IWSAM code super flex`` =
let fAdd(x: #IAdditionOperator<_>, y) =
x + y
let fSin(x: #ISinOperator<_>) =
sin x
printfn ""

@brianrourkeboll
Copy link
Contributor

brianrourkeboll commented Apr 17, 2024

Note: Works fine when chaining op_Addition calls.

Yes, as in

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    num
    |> fun num' -> 'T.(+) (num, num')
    |> fun num' -> 'T.(+) (num, num')

or

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    num
    |> fun num' -> 'T.op_Addition (num, num')
    |> fun num' -> 'T.op_Addition (num, num')

or

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    let (+) x y = 'T.(+) (x, y)
    num + num + num

@vzarytovskii
Copy link
Member

Note: Works fine when chaining op_Addition calls.

Yes, as in

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    num
    |> fun num' -> 'T.(+) (num, num')
    |> fun num' -> 'T.(+) (num, num')

or

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    num
    |> fun num' -> 'T.op_Addition (num, num')
    |> fun num' -> 'T.op_Addition (num, num')

or

let numericOpsPlus<'T when INumber<'T>> (num: 'T) =
    let (+) x y = 'T.(+) (x, y)
    num + num + num

Yes, also:

open System.Numerics
let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =
    ((num + num) : 'T) + num

and

open System.Numerics
let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =
    'T.op_Addition('T.op_Addition(num, num), num)

@vzarytovskii
Copy link
Member

vzarytovskii commented Apr 17, 2024

We suffer flexibility of the (+) here. I am not fully sure we can "fix" it here without seriously breaking something. I wonder if it's the same with some other operators (maybe not that common in F#).

@brianrourkeboll
Copy link
Contributor

Yes, also:

open System.Numerics
let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =
    ((num + num) : 'T) + num

This might show where the problem is: something is probably getting confused when looking at the multiple type parameters in IAdditionOperators<'TSelf, 'TOther, 'TResult> and trying to unify the 'TResult of the first application with the 'TOther of the second.

@vzarytovskii
Copy link
Member

Yes, also:

open System.Numerics

let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =

((num + num) : 'T) + num

This might show where the problem is: something is probably getting confused when looking at the multiple type parameters in IAdditionOperators<'TSelf, 'TOther, 'TResult> and trying to unify the 'TResult of the first application with the 'TOther of the second.

It's probably only in conjunction with + (might be others too).

@vzarytovskii
Copy link
Member

IAdditionOperators<'TSelf, 'TOther, 'TResult> and trying to unify the 'TResult of the first application with the 'TOther of the second.

@brianrourkeboll regarding the above, since it's constrained via INumber, I would expect it to have all typars to be unified.

@brianrourkeboll
Copy link
Contributor

brianrourkeboll commented Apr 17, 2024

It's probably only in conjunction with + (might be others too).

It does for the other System.Numerics operators (-, *, %, unary + and -, etc.).

Interestingly, this does not trigger the problem:

open System.Numerics

#nowarn "3535"

type IFace<'a, 'b, 'c> =
    static abstract (>*..*<) : 'a * 'b -> 'c

let customOps<'T when IFace<'T, 'T, 'T>> (num: 'T) =
    num >*..*< num >*..*< num

type T =
    | T of int
    interface IFace<T, T, T> with
        static member (>*..*<) (T a, T b) = T (a + b)

customOps (T 1)

@vzarytovskii
Copy link
Member

It's probably only in conjunction with + (might be others too).

It does for the other System.Numerics operators (-, *, %, unary + and -, etc.).

Interestingly, this does not trigger the problem:

open System.Numerics



#nowarn "3535"



type IFace<'a, 'b, 'c> =

    static abstract (>*..*<) : 'a * 'b -> 'c



let customOps<'T when IFace<'T, 'T, 'T>> (num: 'T) =

    num >*..*< num >*..*< num



type T =

    | T of int

    interface IFace<T, T, T> with

        static member (>*..*<) (T a, T b) = T (a + b)



customOps (T 1)

Yeah, many of the "standard" operators have somewhat special treatment.

@En3Tho
Copy link
Contributor Author

En3Tho commented Apr 18, 2024

By the way, this is how I found this bug: m4rs-mt/ILGPU#463 (comment)

I've been playing with F# and ILGpu and so far it feels quite nice.

@vzarytovskii
Copy link
Member

When using inline, code does compile to

public static T numericOpsPlus<T>(T num) where T : INumber<T>
    {
        T val = num + num;
        return val + num;
    }

@abonie abonie added Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code. Area-Compiler-Checking Type checking, attributes and all aspects of logic checking and removed Needs-Triage labels Apr 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-Checking Type checking, attributes and all aspects of logic checking Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Projects
Status: New
Development

No branches or pull requests

4 participants