Using multiple operators in INumber-constrained generic functions leads to compiler error
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
Note: Works fine when chaining op_Addition calls.
For reference — these tests should be extended to cover this:
https://github.com/dotnet/fsharp/blob/273cea532b81b33596d9635c6d8c6fb0a610093d/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/testFiles/UseSRTPFromIWSAMGenericCode.fs#L1-L33
Note: Works fine when chaining
op_Additioncalls.
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
Note: Works fine when chaining
op_Additioncalls.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)
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#).
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.
Yes, also:
open System.Numerics
let numericOpsPlusPlus<'T when INumber<'T>>(num: 'T) =
((num + num) : 'T) + numThis 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'TResultof the first application with the'TOtherof the second.
It's probably only in conjunction with + (might be others too).
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.
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)
It's probably only in conjunction with + (might be others too).
It does for the other
System.Numericsoperators (-,*,%, 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.
By the way, this is how I found this bug: https://github.com/m4rs-mt/ILGPU/issues/463#issuecomment-2061598101
I've been playing with F# and ILGpu and so far it feels quite nice.
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;
}