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

Internal error: Undefined or unsolved type variable with SRTP chains #5973

Open
realvictorprm opened this issue Dec 1, 2018 · 7 comments
Open
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Milestone

Comments

@realvictorprm
Copy link
Contributor

realvictorprm commented Dec 1, 2018

While digging into #4924 I found an example with SRTP chains which produces an internal error.

Repro steps

Try compiling this in FSI:

type Bar =
    static member inline bar (f: ^c -> ^b, (a, b) : ^c) : ^b = f (a, b)
    static member inline bar (f: ^c -> ^b, (a, b, c) : ^c) : ^b = f (a, b, c)

let inline bar_(f:^a -> ^b) (x: ^c) : ^d when (^bar or ^d) : (static member bar : (^a -> ^b) * ^c -> ^d) = 
    ((^bar or ^d) : (static member bar : (^a -> ^b) * ^c -> ^d)(f, x))

type Foo =
    static member inline foo (f : ^a -> ^b, (a, b)) : ^c = bar_ f (a,b)
    static member inline foo (f : ^a -> ^b, (a, b, c)) : ^c = bar_ f (a,b,c)

let inline foo< ^foo, ^a, ^b, ^c, ^d when (^foo or ^a) : (static member foo : (^a -> ^b) * ^c -> ^d)> (f: ^a -> ^b) (x: ^c) : ^d =
        ((^foo or ^a) : (static member foo : (^a -> ^b) * ^c -> ^d)(f, x))

let inline callFoo (f: ^a -> ^b) (x: ^c) : ^d when (Foo or ^d) : (static member foo: (^a -> ^b) * ^c -> ^d)=
    foo<Foo, ^a, ^b, ^c, ^d> f x

let test = (fun (a : int, b : int) -> (a, b))

let appliedFoo : int * int -> int * int =
    callFoo test

Will produce an error like: FS0073: Internal Error: Undefined or unsolved type variable: ^_?35727
for the last function appliedFoo.

Expected behavior

No internal error.

Actual behavior

An internal error is thrown.

Known workarounds

None

Related information

F# 4.5

@cartermp
Copy link
Contributor

Labeling as a bug due to the internal error - whether or not this should actually compile is a separate question, but from a usability standpoint the internal error needs to be done away with so your entire editor won't go red.

@dsyme
Copy link
Contributor

dsyme commented Dec 11, 2018

From compiler office hours:

@dsyme says:

  1. A stack trace would be the first thing, to find which phase.
  2. Minimizing the sample more if it can be done

@realvictorprm says

  1. The compiler noticed that something went super wrong at the last stage, code generation (edited)
    I already tried 2. it was complex enough to get it down to what I wrote

@dsyme says

OK, then use the debugger to determine which construct still contains an unsolved type variable. There are several possibilities.

@realvictorprm
Copy link
Contributor Author

I debugged the compiler and found out:

  1. This should could not compile, ^bar should have been inferred to be of type obj
  2. I discovered that the compiler does not infer obj as default type in inline exprs if full annotations are given in partial function application.

@dsyme
Copy link
Contributor

dsyme commented Dec 13, 2018

I discovered that the compiler does not infer obj as default type in inline exprs if full annotations are given in partial function application.

Is this in the body of the inline expression, or the copy of the inline expression (after it's been inlined into a callsite)?

@realvictorprm
Copy link
Contributor Author

Part of the call stack is:
image

@realvictorprm
Copy link
Contributor Author

I think I've got a different reproduction for this issue which is based on the code from #4924.

type Either<'l,'r> = Left of 'l | Right of 'r
type Maybe<'a> = Either<unit,'a>

let inline addfst x y = (x, y)

module Either =

    let rmap f : Either<'l,'a> -> Either<'l,'b> =
        function Right x -> Right (f x) | Left e -> Left e

type Multifunctor() =
    static member inline mapB (f : ^b -> ^b0, (a : ^a, b : ^b)) : ^a * ^b0 = (a, f b)
    // If you comment the line below you'll get an internal error undefined or unsolved type variable
    // Otherwise it compiles but you won't get compile errors although the type signature ofwhatInTheWorld is wrong
    // static member inline mapB (f : ^b -> ^b0, x : Either< ^a,^b>) : Either< ^a,^b0> = Either.rmap f x
    static member inline mapB (f : ^b -> ^b0, (a : ^a, b : ^b, c : ^c)) : ^a * ^b0 * ^c = (a, f b, c)

let inline mapB_< ^mf, ^f, ^a, ^b when (^mf or ^a) : (static member mapB : ^f * ^a -> ^b)> (f : ^f, x : ^a) : ^b =
    ((^mf or ^a) : (static member mapB : ^f * ^a -> ^b) (f, x))

type Multitraverse() =
    static member inline traverseBB (f : ^b1 -> ^b2, x : ^a1 * ^c2 * ^d3) = fun a -> printfn "hi"; a
    static member inline traverseBB (f : ^b -> ^b0, (a : ^a, b : ^b)) : ^b1
        when (Multifunctor or ^b0) : (static member mapB : _ * ^b0 -> ^b1) =
            mapB_<Multifunctor,_,_,_> (addfst a, (f b))

let inline traverseBB_< ^mf, ^a, ^b, ^c, ^d when (^mf or ^a) : (static member traverseBB : (^a -> ^b) * ^c -> ^d)> (f : ^a -> ^b, x : ^c) : ^d =
    ((^mf or ^a) : (static member traverseBB : (^a -> ^b) * ^c -> ^d) (f, x))

let inline traverseBB (f : ^a -> ^b) (x : ^c) : ^d
    when (Multitraverse or ^c) : (static member traverseBB : (^a -> ^b) * ^c -> ^d) =
        // The intent of requiring monad is just trying to be lawful
        traverseBB_<Multitraverse,^a,^b,^c,^d> (f, x)

let private maybeZero : int -> Maybe<int> =
    function
    | 0 -> Right 0
    | _ -> Left ()

// This type clearly does not match the implementation, and yet it type checks
// full signature is: int -> Maybe<int> -> list<string> * int -> Either<string,list<string>>
// This selects the function traverseBB : (f : ^b -> ^b0, (a : ^a, b : ^b)) : ^b1
//                                        (f : int -> Maybe<int>, (a : list<string>, b : int)) : Either<string, list<string>>
// Inner selected function is: mapB : _ * ^b0 -> ^b1
//                                  : (^x -> (list<string> * ^x)) * Maybe<int> -> Either<string, list<string>>
// Which would be:             mapB (f : ^b/int -> ^b0/list<string> * int, x : Either< ^a/unit, ^b/int>) : Either< ^a/unit,^b0/list<string> * int>
let private whatInTheWorld : list<string>*int -> Either<unit,list<string> * float> =
    traverseBB maybeZero

@cartermp cartermp modified the milestones: 16.0, 16.1 Feb 21, 2019
@cartermp cartermp modified the milestones: 16.1, 16.2 Apr 23, 2019
@cartermp cartermp modified the milestones: 16.2, Backlog Apr 30, 2019
@dsyme dsyme added the Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. label Aug 26, 2020
@dsyme
Copy link
Contributor

dsyme commented Aug 26, 2020

I referenced this in #6805 to make sure we pin down known status for this in that branch

@dsyme dsyme added Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen and removed Area-Compiler labels Mar 31, 2022
@vzarytovskii vzarytovskii moved this to Not Planned in F# Compiler and Tooling Jun 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Projects
Status: New
Development

No branches or pull requests

3 participants