Skip to content

Handle binder type symbols in extractor patterns #4056

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

Merged
merged 1 commit into from
Mar 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import core._
import Types._
import Contexts._
import Flags._
import ast.Trees._
import ast.tpd
import ast._
import Trees._
import Decorators._
import Symbols._
import StdNames._
Expand Down Expand Up @@ -299,16 +299,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
if (res) Typ(and, true) else Empty
}

/* Whether the extractor is irrefutable */
/** Whether the extractor is irrefutable */
def irrefutable(unapp: Tree): Boolean = {
// TODO: optionless patmat
unapp.tpe.widen.finalResultType.isRef(scalaSomeClass) ||
(unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case)) ||
productArity(unapp.tpe.widen.finalResultType) > 0
}

/** Return the space that represents the pattern `pat`
*/
/** Return the space that represents the pattern `pat` */
def project(pat: Tree): Space = pat match {
case Literal(c) =>
if (c.value.isInstanceOf[Symbol])
Expand All @@ -326,9 +325,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
if (fun.symbol.owner == scalaSeqFactoryClass)
projectSeq(pats)
else
Prod(pat.tpe.stripAnnots, fun.tpe, fun.symbol, projectSeq(pats) :: Nil, irrefutable(fun))
Prod(erase(pat.tpe.stripAnnots), fun.tpe, fun.symbol, projectSeq(pats) :: Nil, irrefutable(fun))
else
Prod(pat.tpe.stripAnnots, fun.tpe, fun.symbol, pats.map(project), irrefutable(fun))
Prod(erase(pat.tpe.stripAnnots), fun.tpe, fun.symbol, pats.map(project), irrefutable(fun))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be sure: basically this throws away _$1 and _$2, which is fine now but might be less fine if we had type arguments for extractor patterns (which at least I'd like eventually?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we check according to semantics, I think it's correct. I cannot come up with a counter-example. If some type cannot be checked at run-time, they get a warning instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; since type arguments to classes/traits are erased at runtime, the only use case is for annotating what GADT inference should deduce or for binding type variables generated by GADT inference. For instance:

trait Exp[T]
case class[A, B])[f: Exp[A => B])
def eval[T](e: Exp[T])(env: Env): T = e match {
    case Fun[a, b](x, body) =>
      (v: a) => body.eval(env + (x -> v))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the example doesn't compile at all, despite any repairing efforts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned upfront the issue is with a potential future feature, so of course my example doesn't work yet:

if we had type arguments for extractor patterns (which at least I'd like eventually?)

case Typed(pat @ UnApply(_, _, _), _) => project(pat)
case Typed(expr, tpt) =>
Typ(erase(expr.tpe.stripAnnots), true)
Expand Down
1 change: 1 addition & 0 deletions tests/patmat/enum-approx.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24: Pattern Match Exhaustivity: Fun.ConstNullClass(_)
31 changes: 31 additions & 0 deletions tests/patmat/enum-approx.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
enum Fun[-T, +U >: Null] {
def f: T => U = this match {
case Identity(g) => g
case ConstNull => (_ => null)
case ConstNullClass(y) => (_ => null)
case ConstNullSimple => null
}

case Identity[T, U >: Null](g: T => U) extends Fun[T, U]
case ConstNull
case ConstNullClass(x: T)
case ConstNullSimple
}

object Test {
def main(args: Array[String]) = {
val x: Null = Fun.ConstNull.f("abc")
val y: Null = Fun.ConstNullClass("hello").f("abc")
assert(Fun.ConstNullSimple.f == null)
}

import Fun._

def f[T, U >: Null](f: Fun[T, U]): T => U = f match {
case Identity(g) => g
case ConstNull => (_ => null)
case ConstNullClass(y: Int) => (_ => null)
case ConstNullSimple => null
}
}