Skip to content

Commit

Permalink
irrefutable name-based extractors
Browse files Browse the repository at this point in the history
  • Loading branch information
martijnhoekstra committed Nov 24, 2020
1 parent 867f63a commit 6ac807b
Show file tree
Hide file tree
Showing 5 changed files with 15 additions and 13 deletions.
Expand Up @@ -324,21 +324,31 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
}

object IrrefutableExtractorTreeMaker {
def isIrrefutablyIsEmptyFalse(sym: Symbol): Boolean =
sym.isMethod && sym.name == TermName("isEmpty") && (sym.asMethod.returnType match {
case c: ConstantType => c.value == Constant(false)
case _ => false
})
// will an extractor with unapply method of methodtype `tp` always succeed?
// note: this assumes the other side-conditions implied by the extractor are met
// (argument of the right type, length check succeeds for unapplySeq,...)
def irrefutableExtractorType(tp: Type): Boolean = tp.resultType.dealias match {
//Some(x) is always irrefutable
case TypeRef(_, SomeClass, _) => true
//name based pattern matching checks for constant false `isEmpty`.
//TODO: align with dotty and spec -- constant true `isDefined` or `value: A`
//could also make sense, but when those don't align, we have to pick
//one "leading" one.
case TypeRef(_, res, _) => res.tpe.members.exists(isIrrefutablyIsEmptyFalse)
// probably not useful since this type won't be inferred nor can it be written down (yet)
case ConstantTrue => true
case _ => false
}

def unapply(xtm: ExtractorTreeMaker): Option[(Tree, Symbol)] = xtm match {
case ExtractorTreeMaker(extractor, None, nextBinder) if irrefutableExtractorType(extractor.tpe) =>
case ExtractorTreeMaker(extractor, None, nextBinder) if irrefutableExtractorType(extractor.tpe) =>
Some((extractor, nextBinder))
case _ =>
None
case _ => None
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/Array.scala
Expand Up @@ -559,7 +559,7 @@ object Array {
def unapplySeq[T](x: Array[T]): UnapplySeqWrapper[T] = new UnapplySeqWrapper(x)

final class UnapplySeqWrapper[T](private val a: Array[T]) extends AnyVal {
def isEmpty: Boolean = false
def isEmpty: false = false
def get: UnapplySeqWrapper[T] = this
def lengthCompare(len: Int): Int = a.lengthCompare(len)
def apply(i: Int): T = a(i)
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/collection/Factory.scala
Expand Up @@ -308,7 +308,7 @@ object SeqFactory {
}

final class UnapplySeqWrapper[A](private val c: SeqOps[A, Seq, Seq[A]]) extends AnyVal {
def isEmpty: Boolean = false
def isEmpty: false = false
def get: UnapplySeqWrapper[A] = this
def lengthCompare(len: Int): Int = c.lengthCompare(len)
def apply(i: Int): A = c(i)
Expand Down
4 changes: 0 additions & 4 deletions test/files/run/patmatnew.check
Expand Up @@ -16,10 +16,6 @@ patmatnew.scala:356: warning: multiline expressions might require enclosing pare
patmatnew.scala:356: warning: a pure expression does nothing in statement position
case 3 => assert(false); "KO"
^
patmatnew.scala:175: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
def doMatch(xs: List[String]): String = xs match {
^
patmatnew.scala:178: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
def doMatch2(xs: List[String]): List[String] = xs match {
Expand Down
4 changes: 0 additions & 4 deletions test/files/run/t3530.check
@@ -1,7 +1,3 @@
t3530.scala:9: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
def f2[T](x: List[T]) = println(x match {
^
two
three
list: 4
Expand Down

0 comments on commit 6ac807b

Please sign in to comment.