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

GADT pattern matching fails to infer type equations #12478

Closed
TomasMikula opened this issue May 14, 2021 · 9 comments · Fixed by #16744
Closed

GADT pattern matching fails to infer type equations #12478

TomasMikula opened this issue May 14, 2021 · 9 comments · Fixed by #16744
Assignees
Milestone

Comments

@TomasMikula
Copy link
Contributor

Compiler version

3.0.0

Minimized code

enum Foo[T] {
  case Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]]
}

def some1[T](foo: Foo[T]): Foo[Option[T]] =
  foo match {
    case Foo.Bar(fu) =>
      Foo.Bar(fu.map(Option(_)))
  }

// the same thing, but with type annotations and comments
def some2[T](foo: Foo[T]): Foo[Option[T]] =
  foo match {
    // capturing the type parameter of Bar into a type variale `f`
    case Foo.Bar(fu) : Foo.Bar[f] =>
      // now it is known that T = f[Unit]

      val fu1: List[Option[f[Unit]]] =
        fu.map(Option(_))

      val res: Foo[Option[f[Unit]]] =
        Foo.Bar[[x] =>> Option[f[x]]](fu1)

      // given that f[Unit] = T,
      // `res` should pass as a return value of type Foo[Option[T]]
      res
  }

Output

-- [E007] Type Mismatch Error: gadt.scala:8:28 ---------------------------------
8 |      Foo.Bar(fu.map(Option(_)))
  |                            ^
  |      Found:    (_$2 : F$1[Unit])
  |      Required: Unit
  |
  |      where:    F$1 is a type in method some1 with bounds <: [_$1] =>> Any

longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: gadt.scala:26:6 ---------------------------------
26 |      res
   |      ^^^
   |      Found:    (res : Foo[Option[f[Unit]]])
   |      Required: Foo[Option[T]]
   |
   |      where:    f is a type in method some2 with bounds <: [_$1] =>> Any

longer explanation available when compiling with `-explain`
2 errors found

Expectation

The code should compile, the comments in the some2 method explain why.

@LPTK
Copy link
Contributor

LPTK commented May 14, 2021

Here is a weird workaround:

def some1[T](foo: Foo[T]): Foo[Option[T]] =
  foo match {
    case foo: (foo.type & Foo.Bar[f]) =>
      Foo.Bar[[x] =>> Option[f[x]]](foo.fu.map(Option(_)))
  }

It does give off a spurious exhaustivity warning, probably for the same reason as in #12475 (comment)

@TomasMikula
Copy link
Contributor Author

@LPTK Thanks for the workaround.

@dwijnand
Copy link
Member

def some2 now compiles, provided you drop the unapply and just use the type test. Then you need the type variable to call the constructor.

@TomasMikula
Copy link
Contributor Author

@dwijnand what version is "now", and what's the compiling code?

@dwijnand
Copy link
Member

enum Foo[T] {
  case Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]]
}

// the same thing, but with type annotations and comments
def some2[T](foo: Foo[T]): Foo[Option[T]] =
  foo match {
    // capturing the type parameter of Bar into a type variale `f`
    case bar : Foo.Bar[f] =>
      // now it is known that T = f[Unit]

      val fu = bar.fu

      val fu1: List[Option[f[Unit]]] =
        fu.map(Option(_))

      val res: Foo[Option[f[Unit]]] =
        Foo.Bar[[x] =>> Option[f[x]]](fu1)

      // given that f[Unit] = T,
      // `res` should pass as a return value of type Foo[Option[T]]
      res
  }

I just tested with 3.2.2.

@TomasMikula
Copy link
Contributor Author

TomasMikula commented Jan 20, 2023

I just checked and your version compiles already in 3.0.0, so that doesn't show any improvement compared to when this issue was raised, just another workaround.

@dwijnand
Copy link
Member

Then is the bug you're having that this minimisation doesn't compile?

enum Foo[T] {
  case Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]]
}

def wrap[F[_]](fu2: List[Option[F[Unit]]]): Foo[Option[F[Unit]]] =
  Foo.Bar(fu2)

@TomasMikula
Copy link
Contributor Author

Not really, but thanks for helping me realize that the original example would require to infer [x] =>> Option[F[x]] as the type argument of the Bar constructor, and I should not expect that much.

The bug that's bugging me can be simplified to this not compiling:

enum Foo[T] {
  case Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]]
}

def identity[T](foo: Foo[T]): Foo[T] =
  foo match {
    case Foo.Bar(fu) =>
      Foo.Bar(fu)
  }

@TomasMikula
Copy link
Contributor Author

It is a regression from 2.x. The following compiles in 2.13.10, but not in 3.2.2:

sealed trait Foo[T]

object Foo {
  case class Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]]
}

def identity[T](foo: Foo[T]): Foo[T] =
  foo match {
    case Foo.Bar(fu) =>
      Foo.Bar(fu)
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants