Abel
Abel

Reputation: 57149

Using inline within type members fails with FS1114, FS1113, FS1116, FS1118

I've been hit by these errors before in a much more complex scenario. Since then I have simplified many things and had working code for a long time, until I needed to override Equals. It uses an inlined member which F# doesn't seem to be happy about.

Basically, the scenario can be summarized with the following code:

[<Flags>]
type MyType =
    | Integer = 0b0001
    | Float   = 0b0010

module Test =
    [<CustomEquality;NoComparison>]
    type SomeType =
        | Int of int64
        | Float of float

        override x.Equals other =
            match other with
            | :? SomeType as y -> 
                // following line throws on compiling this
                match SomeType.getType x &&& SomeType.getType y with
                | MyType.Integer -> int64 x = int64 y       // highest type is integer (both are int)
                | MyType.Float -> float x = float y         // highest type is float (either is or both are float)
                | _ -> false // impossible
            | _ -> false

        override x.GetHashCode() =
            match x with Int i -> hash i | Float f -> hash f

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

        static member inline getType x =
            match x with
            | Int _ -> MyType.Integer
            | Float _ -> MyType.Float

The following errors are raised (this is similar to my previous question but that involved complex ducktyping).

error FS1114: The value 'Test.SomeType.getType' was marked inline but was not bound in the optimization environment
error FS1113: The value 'getType' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible
warning FS1116: A value marked as 'inline' has an unexpected value
error FS1118: Failed to inline the value 'getType' marked 'inline', perhaps because a recursive value was marked 'inline'

Now, there is no recursive value, and the target type is known (as a result of the implicit cast to SomeType in the match pattern), so I don't think there's a reason for this inlining to be impossible.

Does anyone have some thoughts on this? Or a pattern, including the inlined op_Explicit (if you remove getType, you will get the error on those) and preferably also the getType as inlined?

I know I can resolve this with an OO hierarchy, interfaces and what not, but I'd rather use this approach, both for clarity (the type system is a lattice, not a hierarchy) and for performance (an earlier version with the inlining showed more than 4x speed increase in test scenarios, and speed is important).

As an afterthought, the following, simpler scenario also throws these errors:

module Test =
    type SomeType =
        | Int of int64
        | Float of float

        static member MyEquals (x, other: SomeType) =
            // following line throws on compiling this
            float x = float other 

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

When I remove the type decoration other: SomeType, the error disappears. I don't know why that should matter, as a narrower type with the same statically inferred methods should not raise this error, I'd think.

And since the override x.Equals has a type annotation of obj, I don't see how I could use that knowledge (remove the type decoration) to help me here.

Upvotes: 6

Views: 708

Answers (1)

CodeMonkey
CodeMonkey

Reputation: 4738

It seems to be that the F# compiler can't do inlining with unordered code. As you rightly point out in the comment below this appears to be a bug.

open System

[<Flags>]
type MyType =
    | Integer = 0b0001
    | Float   = 0b0010

module Test =
    [<CustomEquality;NoComparison>]
    type SomeType =
        | Int of int64
        | Float of float

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

        static member inline getType x =
            match x with
            | Int _ -> MyType.Integer
            | Float _ -> MyType.Float  

        override x.Equals other =
            match other with
            | :? SomeType as y -> 
                // following line throws on compiling this
                match SomeType.getType x &&& SomeType.getType y with
                | MyType.Integer -> int64 x = int64 y       // highest type is integer (both are int)
                | MyType.Float -> float x = float y         // highest type is float (either is or both are float)
                | _ -> false // impossible
            | _ -> false

        override x.GetHashCode() =
            match x with Int i -> hash i | Float f -> hash f

Upvotes: 4

Related Questions