From 495ed9957b3b74932bf47ef4c7051182b3a26a50 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Fri, 12 May 2017 22:44:39 +0200 Subject: [PATCH 01/11] Fix #2363: better handle extractors in exhaustivity check 1. Better handle exactors 2. Translate the pattern `List(pat1, pat2, c:_*)` to space semantically 3. Deal with extractors that return `Some[_]` Previously we approximate extractor either to the extracted type or Empty, which is too coarse, and it causes some false positives about unreachable case. Introducing the concept extractor into the theory enables us to inspect the patterns inside extractors and check more unreachable cases, which is not supported by Scalac. Scalac is silent with following code, but Dotty reports both unexhaustive and unreachable warnings. sealed trait Node case class NodeA(i: Int) extends Node case class NodeB(b: Boolean) extends Node case class NodeC(s: String) extends Node object Node { def unapply(node: Node): Option[(Node, Node)] = ??? } object Test { def foo(x: Node): Boolean = x match { // unexhaustive case Node(NodeA(_), NodeB(_)) => true case Node(NodeA(4), NodeB(false)) => true // unreachable code } } --- .../tools/dotc/transform/patmat/Space.scala | 154 ++++++++++++++---- tests/patmat/exhausting.check | 2 +- tests/patmat/i2363.check | 2 + tests/patmat/i2363.scala | 25 +++ tests/patmat/patmat-adt.check | 2 +- tests/patmat/patmat-extractor.check | 2 + tests/patmat/patmatexhaust.check | 1 + tests/patmat/t6420.check | 2 +- tests/patmat/t7020.check | 8 +- tests/patmat/t7466.check | 2 +- tests/patmat/t9129.check | 2 +- tests/patmat/t9232.check | 2 +- tests/patmat/t9351.check | 2 +- 13 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 tests/patmat/i2363.check create mode 100644 tests/patmat/i2363.scala create mode 100644 tests/patmat/patmat-extractor.check diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2f1c5f37f7f8..0622be154251 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -29,6 +29,7 @@ import config.Printers.{ exhaustivity => debug } * 3. A union of spaces `S1 | S2 | ...` is a space * 4. For a case class Kon(x1: T1, x2: T2, .., xn: Tn), if S1, S2, ..., Sn * are spaces, then `Kon(S1, S2, ..., Sn)` is a space. + * 5. `Fun(S1, S2, ..., Sn)` is an extractor space. * * For the problem of exhaustivity check, its formulation in terms of space is as follows: * @@ -63,6 +64,9 @@ case class Typ(tp: Type, decomposed: Boolean) extends Space /** Space representing a constructor pattern */ case class Kon(tp: Type, params: List[Space]) extends Space +/** Space representing an extractor pattern */ +case class Fun(tp: Type, fun: Type, params: List[Space]) extends Space + /** Union of spaces */ case class Or(spaces: List[Space]) extends Space @@ -98,19 +102,31 @@ trait SpaceLogic { def show(sp: Space): String /** Simplify space using the laws, there's no nested union after simplify */ - def simplify(space: Space): Space = space match { + def simplify(space: Space, aggressive: Boolean = false): Space = space match { case Kon(tp, spaces) => - val sp = Kon(tp, spaces.map(simplify _)) + val sp = Kon(tp, spaces.map(simplify(_))) + if (sp.params.contains(Empty)) Empty + else sp + case Fun(tp, fun, spaces) => + val sp = Fun(tp, fun, spaces.map(simplify(_))) if (sp.params.contains(Empty)) Empty else sp case Or(spaces) => - val set = spaces.map(simplify _).flatMap { + val set = spaces.map(simplify(_)).flatMap { case Or(ss) => ss case s => Seq(s) } filter (_ != Empty) if (set.isEmpty) Empty else if (set.size == 1) set.toList(0) + else if (aggressive && spaces.size < 5) { + val res = set.map(sp => (sp, set.filter(_ ne sp))).find { + case (sp, sps) => + isSubspace(sp, Or(sps)) + } + if (res.isEmpty) Or(set) + else simplify(Or(res.get._2), aggressive) + } else Or(set) case Typ(tp, _) => if (canDecompose(tp) && decompose(tp).isEmpty) Empty @@ -137,26 +153,33 @@ trait SpaceLogic { def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) - val res = (a, b) match { + val res = (simplify(a), b) match { case (Empty, _) => true case (_, Empty) => false - case (Or(ss), _) => ss.forall(isSubspace(_, b)) + case (Or(ss), _) => + ss.forall(isSubspace(_, b)) case (Typ(tp1, _), Typ(tp2, _)) => - isSubType(tp1, tp2) || tryDecompose1(tp1) || tryDecompose2(tp2) - case (Typ(tp1, _), Or(ss)) => + isSubType(tp1, tp2) + case (Typ(tp1, _), Or(ss)) => // optimization ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) + case (_, Or(_)) => + simplify(minus(a, b)) == Empty case (Typ(tp1, _), Kon(tp2, ss)) => - isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) || - tryDecompose1(tp1) + isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) case (Kon(tp1, ss), Typ(tp2, _)) => - isSubType(tp1, tp2) || - simplify(a) == Empty || - (isSubType(tp2, tp1) && tryDecompose1(tp1)) || - tryDecompose2(tp2) - case (Kon(_, _), Or(_)) => - simplify(minus(a, b)) == Empty + isSubType(tp1, tp2) case (Kon(tp1, ss1), Kon(tp2, ss2)) => isEqualType(tp1, tp2) && ss1.zip(ss2).forall((isSubspace _).tupled) + case (Fun(tp1, fun, ss), Typ(tp2, _)) => + isSubType(tp1, tp2) + case (Typ(tp2, _), Fun(tp1, fun, ss)) => + false // approximation: assume a type can never be fully matched by an extractor + case (Kon(_, _), Fun(_, _, _)) => + false // approximation + case (Fun(_, _, _), Kon(_, _)) => + false // approximation + case (Fun(_, fun1, ss1), Fun(_, fun2, ss2)) => + isEqualType(fun1, fun2) && ss1.zip(ss2).forall((isSubspace _).tupled) } debug.println(s"${show(a)} < ${show(b)} = $res") @@ -169,7 +192,7 @@ trait SpaceLogic { def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b) def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp))) - val res = (a, b) match { + val res: Space = (a, b) match { case (Empty, _) | (_, Empty) => Empty case (_, Or(ss)) => Or(ss.map(intersect(a, _)).filterConserve(_ ne Empty)) case (Or(ss), _) => Or(ss.map(intersect(_, b)).filterConserve(_ ne Empty)) @@ -193,6 +216,24 @@ trait SpaceLogic { if (!isEqualType(tp1, tp2)) Empty else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty else Kon(tp1, ss1.zip(ss2).map((intersect _).tupled)) + case (Typ(tp1, _), Fun(tp2, _, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) b // prefer extractor space for better approximation + else if (canDecompose(tp1)) tryDecompose1(tp1) + else Empty + case (Fun(tp1, _, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) a + else if (canDecompose(tp2)) tryDecompose2(tp2) + else Empty + case (Fun(tp1, _, _), Kon(tp2, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) a + else Empty + case (Kon(tp1, _), Fun(tp2, _, _)) => + if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) b + else Empty + case (Fun(tp1, fun1, ss1), Fun(tp2, fun2, ss2)) => + if (!isEqualType(fun1, fun2)) Empty + else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty + else Fun(tp1, fun1, ss1.zip(ss2).map((intersect _).tupled)) } debug.println(s"${show(a)} & ${show(b)} = ${show(res)}") @@ -238,6 +279,25 @@ trait SpaceLogic { Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { case (ri, i) => Kon(tp1, ss1.updated(i, ri)) }) + case (Fun(tp1, _, _), Typ(tp2, _)) => + if (isSubType(tp1, tp2)) Empty + else a + case (Typ(tp1, _), Fun(tp2, _, _)) => + a // approximation + case (Fun(_, _, _), Kon(_, _)) => + a + case (Kon(_, _), Fun(_, _, _)) => + a + case (Fun(tp1, fun1, ss1), Fun(tp2, fun2, ss2)) => + if (!isEqualType(fun1, fun2)) a + else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) a + else if (ss1.zip(ss2).forall((isSubspace _).tupled)) Empty + else + // `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)` + Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { + case (ri, i) => Fun(tp1, fun1, ss1.updated(i, ri)) + }) + } debug.println(s"${show(a)} - ${show(b)} = ${show(res)}") @@ -263,6 +323,12 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ + private val scalaSomeClass = ctx.requiredClassRef("scala.Some".toTermName).symbol.asClass + private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory".toTypeName) + private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List".toTypeName) + private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil".toTermName) + private val scalaConType = ctx.requiredClassRef("scala.collection.immutable.::".toTypeName) + /** Checks if it's possible to create a trait/class which is a subtype of `tp`. * * - doesn't handle member collisions (will not declare a type unimplementable because of one) @@ -337,11 +403,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } /** Return the space that represents the pattern `pat` - * - * If roundUp is true, approximate extractors to its type, - * otherwise approximate extractors to Empty */ - def project(pat: Tree, roundUp: Boolean = true)(implicit ctx: Context): Space = pat match { + def project(pat: Tree): Space = pat match { case Literal(c) => if (c.value.isInstanceOf[Symbol]) Typ(c.value.asInstanceOf[Symbol].termRef, false) @@ -350,20 +413,41 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case _: BackquotedIdent => Typ(pat.tpe, false) case Ident(_) | Select(_, _) => Typ(pat.tpe.stripAnnots, false) - case Alternative(trees) => Or(trees.map(project(_, roundUp))) + case Alternative(trees) => Or(trees.map(project(_))) case Bind(_, pat) => project(pat) - case UnApply(_, _, pats) => + case UnApply(fun, _, pats) => if (pat.tpe.classSymbol.is(CaseClass)) // FIXME: why dealias is needed here? - Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat, roundUp))) - else if (roundUp) Typ(pat.tpe.stripAnnots, false) - else Empty + Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat))) + else if (fun.symbol.owner == scalaSeqFactoryClass && fun.symbol.name == nme.unapplySeq) + projectList(pats) + else if (fun.symbol.info.resultType.isRef(scalaSomeClass)) + Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat))) + else + Fun(pat.tpe.stripAnnots.dealias, fun.tpe, pats.map(pat => project(pat))) case Typed(pat @ UnApply(_, _, _), _) => project(pat) case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true) case _ => Empty } + + /** Space of the pattern: List(a, b, c: _*) + */ + def projectList(pats: List[Tree]): Space = { + if (pats.isEmpty) return Typ(scalaNilType, false) + + val (items, zero) = if (pats.last.tpe.isRepeatedParam) + (pats.init, Typ(scalaListType.appliedTo(pats.head.tpe.widen), false)) + else + (pats, Typ(scalaNilType, false)) + + items.foldRight[Space](zero) { (pat, acc) => + Kon(scalaConType.appliedTo(pats.head.tpe.widen), project(pat) :: acc :: Nil) + } + } + + /* Erase a type binding according to erasure semantics in pattern matching */ def erase(tp: Type): Type = { def doErase(tp: Type): Type = tp match { @@ -549,6 +633,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (ctx.definitions.isTupleType(tp)) signature(tp).map(_ => "_").mkString("(", ", ", ")") + else if (sym.showFullName == "scala.collection.immutable.List") + if (mergeList) "_*" else "_: List" else if (sym.showFullName == "scala.collection.immutable.::") if (mergeList) "_" else "List(_)" else if (tp.classSymbol.is(CaseClass)) @@ -566,6 +652,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") else showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") + case Fun(tp, fun, params) => + showType(fun) + params.map(doShow(_)).mkString("(", ", ", ")") case Or(_) => throw new Exception("incorrect flatten result " + s) } @@ -664,13 +752,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val patternSpace = cases.map({ x => val space = project(x.pat) - debug.println(s"${x.pat.show} projects to ${show(space)}") + debug.println(s"${x.pat.show} ====> ${show(space)}") space }).reduce((a, b) => Or(List(a, b))) - val uncovered = simplify(minus(Typ(selTyp, true), patternSpace)) + val uncovered = simplify(minus(Typ(selTyp, true), patternSpace), aggressive = true) if (uncovered != Empty) - ctx.warning(PatternMatchExhaustivity(show(uncovered)), _match.pos) + ctx.warning(PatternMatchExhaustivity(show(uncovered)), sel.pos) } def checkRedundancy(_match: Match): Unit = { @@ -678,17 +766,21 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // ignore selector type for now // val selTyp = sel.tpe.widen.deAnonymize.dealias + if (cases.length == 1) return + // starts from the second, the first can't be redundant (1 until cases.length).foreach { i => - // in redundancy check, take guard as false, take extractor as match - // nothing in order to soundly approximate + // in redundancy check, take guard as false in order to soundly approximate val prevs = cases.take(i).map { x => - if (x.guard.isEmpty) project(x.pat, false) + if (x.guard.isEmpty) project(x.pat) else Empty }.reduce((a, b) => Or(List(a, b))) val curr = project(cases(i).pat) + debug.println(s"---------------reachable? ${show(curr)}") + debug.println(s"prev: ${show(prevs)}") + if (isSubspace(curr, prevs)) { ctx.warning(MatchCaseUnreachable(), cases(i).body.pos) } diff --git a/tests/patmat/exhausting.check b/tests/patmat/exhausting.check index b8d1d8408f4d..6cb6db364892 100644 --- a/tests/patmat/exhausting.check +++ b/tests/patmat/exhausting.check @@ -1,6 +1,6 @@ 21: Pattern Match Exhaustivity: List(_), List(_, _, _) 27: Pattern Match Exhaustivity: Nil -32: Pattern Match Exhaustivity: List(_, _) +32: Pattern Match Exhaustivity: List(_, _*) 39: Pattern Match Exhaustivity: Bar3 44: Pattern Match Exhaustivity: (Bar2, Bar2) 53: Pattern Match Exhaustivity: (Bar2, Bar2), (Bar2, Bar1), (Bar1, Bar3), (Bar1, Bar2) diff --git a/tests/patmat/i2363.check b/tests/patmat/i2363.check new file mode 100644 index 000000000000..482144a87e72 --- /dev/null +++ b/tests/patmat/i2363.check @@ -0,0 +1,2 @@ +15: Pattern Match Exhaustivity: List(_, _*) +21: Pattern Match Exhaustivity: _: Expr \ No newline at end of file diff --git a/tests/patmat/i2363.scala b/tests/patmat/i2363.scala new file mode 100644 index 000000000000..e910178ee47e --- /dev/null +++ b/tests/patmat/i2363.scala @@ -0,0 +1,25 @@ +sealed trait Expr +class IntExpr extends Expr +class BooleanExpr extends Expr + +object IntExpr { + def unapply(expr: Expr): Option[IntExpr] = ??? +} + +object BooleanExpr { + def unapply(expr: Expr): Option[BooleanExpr] = ??? +} + + +class Test { + def foo(x: List[Expr]): Int = x match { + case IntExpr(_) :: xs => 1 + case BooleanExpr(_) :: xs => 1 + case Nil => 2 + } + + def bar(x: Expr): Int = x match { + case IntExpr(_) => 1 + case BooleanExpr(_) => 2 + } +} diff --git a/tests/patmat/patmat-adt.check b/tests/patmat/patmat-adt.check index 4adcdc49cd2f..7a938792fee8 100644 --- a/tests/patmat/patmat-adt.check +++ b/tests/patmat/patmat-adt.check @@ -1,5 +1,5 @@ 7: Pattern Match Exhaustivity: Bad(Good(_)), Good(Bad(_)) 19: Pattern Match Exhaustivity: Some(_) -24: Pattern Match Exhaustivity: (None, Some(_)), (_, Some(_)) +24: Pattern Match Exhaustivity: (_, Some(_)) 29: Pattern Match Exhaustivity: (None, None), (Some(_), Some(_)) 50: Pattern Match Exhaustivity: LetL(BooleanLit), LetL(IntLit) diff --git a/tests/patmat/patmat-extractor.check b/tests/patmat/patmat-extractor.check new file mode 100644 index 000000000000..d7ef0e3d2691 --- /dev/null +++ b/tests/patmat/patmat-extractor.check @@ -0,0 +1,2 @@ +13: Pattern Match Exhaustivity: _: Node +15: Match case Unreachable diff --git a/tests/patmat/patmatexhaust.check b/tests/patmat/patmatexhaust.check index 3de93cfdbbd4..e5e7ac397383 100644 --- a/tests/patmat/patmatexhaust.check +++ b/tests/patmat/patmatexhaust.check @@ -2,6 +2,7 @@ 11: Pattern Match Exhaustivity: Bar(_) 23: Pattern Match Exhaustivity: (Qult(), Qult()), (Kult(_), Kult(_)) 49: Pattern Match Exhaustivity: _: Gp +59: Pattern Match Exhaustivity: Nil 75: Pattern Match Exhaustivity: _: B 100: Pattern Match Exhaustivity: _: C1 114: Pattern Match Exhaustivity: D2(), D1 diff --git a/tests/patmat/t6420.check b/tests/patmat/t6420.check index c1570159461a..a96c7b64bd0e 100644 --- a/tests/patmat/t6420.check +++ b/tests/patmat/t6420.check @@ -1 +1 @@ -5: Pattern Match Exhaustivity: (Nil, _), (List(true, _), _), (List(false, _), _), (_, Nil), (_, List(true, _)), (_, List(false, _)) +5: Pattern Match Exhaustivity: (_: List, Nil), (_: List, List(true, _*)), (_: List, List(false, _*)) diff --git a/tests/patmat/t7020.check b/tests/patmat/t7020.check index a44384946184..410b921957fe 100644 --- a/tests/patmat/t7020.check +++ b/tests/patmat/t7020.check @@ -1,4 +1,4 @@ -3: Pattern Match Exhaustivity: List(_, _) -10: Pattern Match Exhaustivity: List(_, _) -17: Pattern Match Exhaustivity: List(_, _) -24: Pattern Match Exhaustivity: List(_, _) +3: Pattern Match Exhaustivity: List(_, _*) +10: Pattern Match Exhaustivity: List(_, _*) +17: Pattern Match Exhaustivity: List(_, _*) +24: Pattern Match Exhaustivity: List(_, _*) diff --git a/tests/patmat/t7466.check b/tests/patmat/t7466.check index af596399b06f..dfd3b3061f03 100644 --- a/tests/patmat/t7466.check +++ b/tests/patmat/t7466.check @@ -1 +1 @@ -8: Pattern Match Exhaustivity: (true, _), (false, _), (_, true), (_, false) +8: Pattern Match Exhaustivity: (_, true), (_, false) diff --git a/tests/patmat/t9129.check b/tests/patmat/t9129.check index 3236bb049a5b..f14f34ec9cfb 100644 --- a/tests/patmat/t9129.check +++ b/tests/patmat/t9129.check @@ -1 +1 @@ -21: Pattern Match Exhaustivity: Two(B2, A2), Two(_, A2) +21: Pattern Match Exhaustivity: Two(_, A2) diff --git a/tests/patmat/t9232.check b/tests/patmat/t9232.check index 36949147a2e7..e2372315727f 100644 --- a/tests/patmat/t9232.check +++ b/tests/patmat/t9232.check @@ -1 +1 @@ -13: Pattern Match Exhaustivity: Node2() +13: Pattern Match Exhaustivity: Node2(), Node1(Foo(_)) diff --git a/tests/patmat/t9351.check b/tests/patmat/t9351.check index bce053c947bd..6a785fa26077 100644 --- a/tests/patmat/t9351.check +++ b/tests/patmat/t9351.check @@ -1,3 +1,3 @@ 8: Pattern Match Exhaustivity: _: A -17: Pattern Match Exhaustivity: (_, _), (_, None), (_, Some(_)) +17: Pattern Match Exhaustivity: (_, None), (_, Some(_)) 28: Pattern Match Exhaustivity: (_, _) From 9453f6d2e820982f021e3c6216accff2e8a4542e Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Sun, 14 May 2017 11:43:36 +0200 Subject: [PATCH 02/11] fix false positives in bootstrap --- .../tools/dotc/transform/patmat/Space.scala | 9 +++--- tests/patmat/dotty.scala | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/patmat/dotty.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0622be154251..dac8885e012c 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -417,14 +417,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case Bind(_, pat) => project(pat) case UnApply(fun, _, pats) => if (pat.tpe.classSymbol.is(CaseClass)) - // FIXME: why dealias is needed here? - Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat))) + Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else if (fun.symbol.owner == scalaSeqFactoryClass && fun.symbol.name == nme.unapplySeq) projectList(pats) else if (fun.symbol.info.resultType.isRef(scalaSomeClass)) - Kon(pat.tpe.stripAnnots.dealias, pats.map(pat => project(pat))) + Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else - Fun(pat.tpe.stripAnnots.dealias, fun.tpe, pats.map(pat => project(pat))) + Fun(pat.tpe.stripAnnots, fun.tpe, pats.map(pat => project(pat))) case Typed(pat @ UnApply(_, _, _), _) => project(pat) case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true) case _ => @@ -470,7 +469,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { // check SI-9657 and tests/patmat/gadt.scala - val res = erase(tp1) <:< erase(tp2) + val res = tp1 <:< erase(tp2) debug.println(s"${tp1.show} <:< ${tp2.show} = $res") res } diff --git a/tests/patmat/dotty.scala b/tests/patmat/dotty.scala new file mode 100644 index 000000000000..509c0a68b4ba --- /dev/null +++ b/tests/patmat/dotty.scala @@ -0,0 +1,30 @@ +object IntEqualityTestTreeMaker { + def unapply(xs: Int): Option[Int] = ??? +} + +class Test { + def isBelow(n: Int, s: String): Boolean = false + + def foo(xs: List[(Int, String)]): Unit = (xs filter (isBelow _).tupled) match { + case Nil => + case matches => + } + + def linkCompanions(xs: List[(Int, Int)]): Unit = { + xs.groupBy(_._1).foreach { + case (_, List(x1, x2)) => + case _ => () + } + } + + def bar(xs: List[(Int, String)]): Unit = xs match { + case (x, s) :: Nil => + case Nil => + case _ => + } + + def patmat(alts: List[List[Int]]): Unit = alts.forall { + case List(IntEqualityTestTreeMaker(_)) => false + case _ => true + } +} \ No newline at end of file From 9d1e94c48d7b42daf0d88d9f9a5e548ffe6b5ef6 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 09:40:26 +0200 Subject: [PATCH 03/11] add missing @unchecked for `for` patmat --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/patmat/for.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2594d95df66b..c2d24608c72e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -859,7 +859,7 @@ object desugar { case IdPattern(named, tpt) => Function(derivedValDef(pat, named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body) case _ => - makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false) + makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil) } /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap diff --git a/tests/patmat/for.scala b/tests/patmat/for.scala index ae9dcf65e9f1..4c2ed262fc30 100644 --- a/tests/patmat/for.scala +++ b/tests/patmat/for.scala @@ -2,4 +2,6 @@ object Test { def foo[A, B](l: List[(A, B)]): List[A] = { for ((a, b) <- l) yield a } + + def bar(xs: List[(Int, List[Int])]): Unit = for ( (_, x :: y :: xs) <- xs) yield x } \ No newline at end of file From 9f8c0892ed1c8c0544b567a872f6a44689eb5c09 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 11:26:32 +0200 Subject: [PATCH 04/11] Fix false warning about unreachable code in Dotty --- .../tools/dotc/transform/patmat/Space.scala | 6 +++--- tests/patmat/dotty-unreachable.scala | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/patmat/dotty-unreachable.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index dac8885e012c..2401796320ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -509,14 +509,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case space => List(space) } case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) - case _ if tp =:= ctx.definitions.BooleanType => + case tp if tp =:= ctx.definitions.BooleanType => List( Typ(ConstantType(Constant(true)), true), Typ(ConstantType(Constant(false)), true) ) - case _ if tp.classSymbol.is(Enum) => + case tp if tp.classSymbol.is(Enum) => children.map(sym => Typ(sym.termRef, true)) - case _ => + case tp => val parts = children.map { sym => if (sym.is(ModuleClass)) refine(tp, sym.sourceModule.termRef) diff --git a/tests/patmat/dotty-unreachable.scala b/tests/patmat/dotty-unreachable.scala new file mode 100644 index 000000000000..f6b5aced15bb --- /dev/null +++ b/tests/patmat/dotty-unreachable.scala @@ -0,0 +1,17 @@ +object Types { + abstract case class TermRef(val prefix: String, name: String) { + type ThisType = TermRef + + def alts: List[TermRef] = ??? + } +} + +class Test { + def foo(tp: Types.TermRef): Unit = { + tp.alts.filter(_.name == "apply") match { + case Nil => + case alt :: Nil => + case alt => + } + } +} \ No newline at end of file From 31ae57b447524fc321f46343d039eed51590e406 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 13:34:56 +0200 Subject: [PATCH 05/11] Fix #2429: fix true exhaustivity warnings --- .../src/dotty/tools/dotc/transform/PatternMatcher.scala | 8 +++++--- .../dotty/tools/dottydoc/model/comment/BodyEntities.scala | 2 +- doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala | 4 ++++ doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 4472c9f5e170..21d9a1040fc7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -351,15 +351,17 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { /* (Nil, body) means that `body` is the default case * It's a bit hacky but it simplifies manipulations. */ - def extractSwitchCase(treeMakers: List[TreeMaker]): (List[Int], BodyTreeMaker) = treeMakers match { + def extractSwitchCase(treeMakers: List[TreeMaker]): (List[Int], BodyTreeMaker) = (treeMakers: @unchecked) match { // case 5 => case List(IntEqualityTestTreeMaker(intValue), body: BodyTreeMaker) => (List(intValue), body) // case 5 | 6 => case List(AlternativesTreeMaker(_, alts, _), body: BodyTreeMaker) => - val intValues = alts.map { - case List(IntEqualityTestTreeMaker(intValue)) => intValue + val intValues = alts.map { alt => + (alt: @unchecked) match { + case List(IntEqualityTestTreeMaker(intValue)) => intValue + } } (intValues, body) diff --git a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala b/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala index 1d49a4f4f12e..c4957b66efff 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/comment/BodyEntities.scala @@ -64,7 +64,7 @@ final case class Text(text: String) extends Inline abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo } object EntityLink { def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo } - def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) + def unapply(el: EntityLink): Some[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala index bc4a74c4fc17..8e82041f386a 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/tags.scala @@ -104,6 +104,10 @@ object tags { title case ConstantReference(title) => title + + case _ => // EmptyReference + ctx.docbase.error(s"invalid reference: $ref") + null } override def render(tctx: TemplateContext, nodes: LNode*): AnyRef = nodes(0).render(tctx) match { case map: JMap[String, AnyRef] @unchecked => diff --git a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala index bb09a709012b..b7bb15167617 100644 --- a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala +++ b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala @@ -77,6 +77,7 @@ trait MemberLookup { case (x :: xs, _) => if (xs.nonEmpty) globalLookup else lookup(entity, packages, "scala." + query) + case (Nil, _) => ??? // impossible as long as query is not empty } } From 20448c8557962473fc5fa9d668acef9512202e5b Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 14:03:14 +0200 Subject: [PATCH 06/11] address review feedback --- doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala index b7bb15167617..69929c7cdafa 100644 --- a/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala +++ b/doc-tool/src/dotty/tools/dottydoc/util/MemberLookup.scala @@ -77,7 +77,8 @@ trait MemberLookup { case (x :: xs, _) => if (xs.nonEmpty) globalLookup else lookup(entity, packages, "scala." + query) - case (Nil, _) => ??? // impossible as long as query is not empty + case (Nil, _) => + throw new IllegalArgumentException("`query` cannot be empty") } } From 30787e5f0eb132105e8573d856f2f11f8dbf02b5 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 14:22:28 +0200 Subject: [PATCH 07/11] support option-less patmat as complete match --- .../tools/dotc/transform/patmat/Space.scala | 4 +-- tests/patmat/optionless.check | 1 + tests/patmat/optionless.scala | 32 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/patmat/optionless.check create mode 100644 tests/patmat/optionless.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2401796320ab..13dd75822a3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -323,7 +323,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ - private val scalaSomeClass = ctx.requiredClassRef("scala.Some".toTermName).symbol.asClass + private val scalaOptionClass = ctx.requiredClassRef("scala.Option".toTypeName).symbol.asClass private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory".toTypeName) private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List".toTypeName) private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil".toTermName) @@ -420,7 +420,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else if (fun.symbol.owner == scalaSeqFactoryClass && fun.symbol.name == nme.unapplySeq) projectList(pats) - else if (fun.symbol.info.resultType.isRef(scalaSomeClass)) + else if (!fun.symbol.info.finalResultType.isRef(scalaOptionClass)) Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else Fun(pat.tpe.stripAnnots, fun.tpe, pats.map(pat => project(pat))) diff --git a/tests/patmat/optionless.check b/tests/patmat/optionless.check new file mode 100644 index 000000000000..c199746c1e0e --- /dev/null +++ b/tests/patmat/optionless.check @@ -0,0 +1 @@ +28: Pattern Match Exhaustivity: _: Tree diff --git a/tests/patmat/optionless.scala b/tests/patmat/optionless.scala new file mode 100644 index 000000000000..9afc43062caa --- /dev/null +++ b/tests/patmat/optionless.scala @@ -0,0 +1,32 @@ +sealed trait Tree +case class Ident(name: String) extends Tree + +object Ident1 { + def unapply(tree: Tree): Ident = ??? +} + +trait Cap +object Ident2 { + def unapply(tree: Tree)(implicit cap: Cap): Ident = ??? +} + +object Ident3 { + def unapply(tree: Tree)(implicit cap: Cap): Option[Ident] = ??? +} + + + +class Test { + def foo(t: Tree): Unit = t match { + case Ident1(t) => + } + + def bar(t: Tree)(implicit c: Cap): Unit = t match { + case Ident2(t) => + } + + def qux(t: Tree)(implicit c: Cap): Unit = t match { + case Ident3(t) => + } + +} \ No newline at end of file From 61414c22f54b3afe9c3a06a34fc0a47f68dd3577 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 15:35:46 +0200 Subject: [PATCH 08/11] retract the option-less commit --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 ++-- tests/patmat/optionless.check | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 13dd75822a3b..22487849b7e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -323,7 +323,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ - private val scalaOptionClass = ctx.requiredClassRef("scala.Option".toTypeName).symbol.asClass + private val scalaSomeClass = ctx.requiredClassRef("scala.Some".toTypeName).symbol.asClass private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory".toTypeName) private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List".toTypeName) private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil".toTermName) @@ -420,7 +420,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else if (fun.symbol.owner == scalaSeqFactoryClass && fun.symbol.name == nme.unapplySeq) projectList(pats) - else if (!fun.symbol.info.finalResultType.isRef(scalaOptionClass)) + else if (fun.symbol.info.finalResultType.isRef(scalaSomeClass)) Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat))) else Fun(pat.tpe.stripAnnots, fun.tpe, pats.map(pat => project(pat))) diff --git a/tests/patmat/optionless.check b/tests/patmat/optionless.check index c199746c1e0e..350b9178c50d 100644 --- a/tests/patmat/optionless.check +++ b/tests/patmat/optionless.check @@ -1 +1,3 @@ +20: Pattern Match Exhaustivity: _: Tree +24: Pattern Match Exhaustivity: _: Tree 28: Pattern Match Exhaustivity: _: Tree From c569f23808e226ae84697f78e3a81e6e20e3c540 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 17:09:40 +0200 Subject: [PATCH 09/11] address review feedback --- .../tools/dotc/transform/patmat/Space.scala | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 22487849b7e7..f761a040c757 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -101,7 +101,13 @@ trait SpaceLogic { /** Display space in string format */ def show(sp: Space): String - /** Simplify space using the laws, there's no nested union after simplify */ + /** Simplify space using the laws, there's no nested union after simplify + * + * @param aggressive if true and OR space has less than 5 components, `simplify` will + * collapse `sp1 | sp2` to `sp1` if `sp2` is a subspace of `sp1`. + * + * This reduces noise in counterexamples. + */ def simplify(space: Space, aggressive: Boolean = false): Space = space match { case Kon(tp, spaces) => val sp = Kon(tp, spaces.map(simplify(_))) @@ -160,7 +166,7 @@ trait SpaceLogic { ss.forall(isSubspace(_, b)) case (Typ(tp1, _), Typ(tp2, _)) => isSubType(tp1, tp2) - case (Typ(tp1, _), Or(ss)) => // optimization + case (Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) case (_, Or(_)) => simplify(minus(a, b)) == Empty @@ -323,11 +329,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ - private val scalaSomeClass = ctx.requiredClassRef("scala.Some".toTypeName).symbol.asClass + private val scalaSomeClass = ctx.requiredClass("scala.Some".toTypeName) private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory".toTypeName) private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List".toTypeName) private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil".toTermName) - private val scalaConType = ctx.requiredClassRef("scala.collection.immutable.::".toTypeName) + private val scalaConsType = ctx.requiredClassRef("scala.collection.immutable.::".toTypeName) /** Checks if it's possible to create a trait/class which is a subtype of `tp`. * @@ -442,7 +448,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { (pats, Typ(scalaNilType, false)) items.foldRight[Space](zero) { (pat, acc) => - Kon(scalaConType.appliedTo(pats.head.tpe.widen), project(pat) :: acc :: Nil) + Kon(scalaConsType.appliedTo(pats.head.tpe.widen), project(pat) :: acc :: Nil) } } @@ -469,6 +475,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { // check SI-9657 and tests/patmat/gadt.scala + // + // `erase` is a walkaround to make the following code pass the check: + // + // def f(e: Either[Int, String]) = e match { + // case Left(i) => i + // case Right(s) => 0 + // } + // + // The problem is that when decompose `Either[Int, String]`, `Type.wrapIfMember` + // only refines the type member inherited from `Either` -- it's complex to refine + // the type members in `Left` and `Right`. + // + // FIXME: remove this hack val res = tp1 <:< erase(tp2) debug.println(s"${tp1.show} <:< ${tp2.show} = $res") res @@ -509,7 +528,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case space => List(space) } case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) - case tp if tp =:= ctx.definitions.BooleanType => + case tp if tp.isRef(defn.BooleanClass) => List( Typ(ConstantType(Constant(true)), true), Typ(ConstantType(Constant(false)), true) @@ -568,7 +587,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val and = dealiasedTp.asInstanceOf[AndType] canDecompose(and.tp1) || canDecompose(and.tp2) }) || - tp =:= ctx.definitions.BooleanType || + tp.isRef(defn.BooleanClass) || tp.classSymbol.is(allOf(Enum, Sealed)) // Enum value doesn't have Sealed flag debug.println(s"decomposable: ${tp.show} = $res") @@ -632,9 +651,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (ctx.definitions.isTupleType(tp)) signature(tp).map(_ => "_").mkString("(", ", ", ")") - else if (sym.showFullName == "scala.collection.immutable.List") + else if (scalaListType.isRef(sym)) if (mergeList) "_*" else "_: List" - else if (sym.showFullName == "scala.collection.immutable.::") + else if (scalaConsType.isRef(sym)) if (mergeList) "_" else "List(_)" else if (tp.classSymbol.is(CaseClass)) // use constructor syntax for case class @@ -646,7 +665,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case Kon(tp, params) => if (ctx.definitions.isTupleType(tp)) "(" + params.map(doShow(_)).mkString(", ") + ")" - else if (tp.widen.classSymbol.showFullName == "scala.collection.immutable.::") + else if (tp.isRef(scalaConsType.symbol)) if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") else @@ -673,7 +692,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val and = tp.asInstanceOf[AndType] isCheckable(and.tp1) || isCheckable(and.tp2) }) || - tp.typeSymbol == ctx.definitions.BooleanType.typeSymbol || + tp.isRef(defn.BooleanClass) || tp.typeSymbol.is(Enum) || canDecompose(tp) || (defn.isTupleType(tp) && tp.dealias.argInfos.exists(isCheckable(_))) From fc20768c7c5d36fd4edf0f34498186d559201cec Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 17:20:26 +0200 Subject: [PATCH 10/11] fix typo in the comment --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index f761a040c757..d4e60cc629dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -474,9 +474,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { - // check SI-9657 and tests/patmat/gadt.scala - // - // `erase` is a walkaround to make the following code pass the check: + // `erase` is a workaround to make the following code pass the check: // // def f(e: Either[Int, String]) = e match { // case Left(i) => i From 1e695eafa2b1830ae52e49691ce7ba1cc19e8741 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 15 May 2017 17:26:04 +0200 Subject: [PATCH 11/11] use decorators to avoid toTypeName and toTermName --- .../tools/dotc/transform/patmat/Space.scala | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index d4e60cc629dd..96c1da50b62e 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -2,16 +2,17 @@ package dotty.tools.dotc package transform package patmat -import core.Types._ -import core.Contexts._ -import core.Flags._ +import core._ +import Types._ +import Contexts._ +import Flags._ import ast.Trees._ import ast.tpd -import core.Decorators._ -import core.Symbols._ -import core.StdNames._ -import core.NameOps._ -import core.Constants._ +import Decorators._ +import Symbols._ +import StdNames._ +import NameOps._ +import Constants._ import reporting.diagnostic.messages._ import config.Printers.{ exhaustivity => debug } @@ -329,11 +330,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { import SpaceEngine._ import tpd._ - private val scalaSomeClass = ctx.requiredClass("scala.Some".toTypeName) - private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory".toTypeName) - private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List".toTypeName) - private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil".toTermName) - private val scalaConsType = ctx.requiredClassRef("scala.collection.immutable.::".toTypeName) + private val scalaSomeClass = ctx.requiredClass("scala.Some") + private val scalaSeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory") + private val scalaListType = ctx.requiredClassRef("scala.collection.immutable.List") + private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil") + private val scalaConsType = ctx.requiredClassRef("scala.collection.immutable.::") /** Checks if it's possible to create a trait/class which is a subtype of `tp`. *