Skip to content

Pattern matching should retain the singleton type of the scrutinee #8083

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

Closed
LPTK opened this issue Jan 23, 2020 · 2 comments
Closed

Pattern matching should retain the singleton type of the scrutinee #8083

LPTK opened this issue Jan 23, 2020 · 2 comments

Comments

@LPTK
Copy link
Contributor

LPTK commented Jan 23, 2020

minimized code

This works in Scala 2.13, but fails in Dotty:

trait A {
  class B
}
abstract class Test2 { val a: A; val b: a.B }
object Test2 {
  def unapply(that: Test2): Option[(that.a.type, that.a.B)] = Some((that.a, that.b))
}
object anA extends A
object Test extends App {
  val t = new Test2 { val a = anA; val b = new a.B }
  t match {
    case Test2(u, v) =>
      u: A
      u: t.a.type // error
      v: t.a.B // error
  }
}

Scastie: https://scastie.scala-lang.org/nLrm92OqQgaolCvmPsf4ZA

Compilation output
      u: t.a.type // error
Found:    (u : (?1.a : A))
Required: (Test.t.a : A)

where:    ?1 is an unknown value of type Test2

      v: t.a.B // error
Found:    (v : ?1.a.B)
Required: Test.t.a.B

where:    ?1 is an unknown value of type Test2

It will probably be needed for #8073.

@LPTK LPTK added the itype:bug label Jan 23, 2020
@odersky
Copy link
Contributor

odersky commented Jan 29, 2020

This is actually due to something else: Anonymous classes lose their member info (since selecting that info would in general require reflection, which we don't want to support by default anymore). There have been several discussions about this and a resolution that we should selectively re-enable propagating the member info for anonymous classes implementing a Structural trait (or something named similarly). But that is not implemented yet. If I change the example to:

  val t: Test2 { val a: anA.type; val b: anA.B } = new Test2 { val a: anA.type = anA; val b = new a.B }

it compiles.

It also compiles if I make t an object:

  object t extends Test2 { val a = anA; val b = new a.B }

@LPTK
Copy link
Contributor Author

LPTK commented Jan 29, 2020

Interesting, thanks for having a look. So that's why it worked in Scala 2.

However, I am still thinking the original should work in Dotty. That is, it would be nice if the following:

  val t: Test2 = new Test2 { val a = anA; val b = new a.B }
  t match {
    case Test2(u, v) =>
      u: t.a.type
      v: t.a.B
  }

would be equivalent to this:

  val t: Test2 = new Test2 { val a = anA; val b = new a.B }
  t match {
    case $t: (t.type & Test2) =>
      val u: $t.a.type = $t.a
      val v: $t.b.type = $t.b
      u: t.a.type // works
      v: t.a.B // works
  }

Perhaps more importantly, this version:

  t match {
    case q @ Test2(u, v) =>
      u: q.a.type
      v: q.a.B
  }

should arguably work, since the unapply is dependent:

object Test2 {
  def unapply(that: Test2): Option[(that.a.type, that.a.B)] = Some((that.a, that.b))
}

and it would be nice if it also retained the dependency between the two extracted variables without having to write an explicit q @ pattern prefix (using a freshly-generated name).

odersky added a commit that referenced this issue Jan 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants