diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index e44ecc6d8193..823c33212c08 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -236,6 +236,12 @@ object Types { } } + /** Is this type exactly Null (no vars, aliases, refinements etc allowed)? */ + def isNullType(implicit ctx: Context): Boolean = this match { + case tp: TypeRef => tp.symbol eq defn.NullClass + case _ => false + } + /** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */ def isBottomType(implicit ctx: Context): Boolean = this match { case tp: TypeRef => tp.symbol eq defn.NothingClass diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index c2a69e409a03..aab9d5f0e914 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -143,6 +143,7 @@ object PatternMatcher { case class ReturnPlan(var label: TermSymbol) extends Plan case class SeqPlan(var head: Plan, var tail: Plan) extends Plan case class ResultPlan(var tree: Tree) extends Plan + case object NoOpPlan extends Plan object TestPlan { def apply(test: Test, sym: Symbol, pos: Position, ons: Plan): TestPlan = @@ -336,9 +337,19 @@ object PatternMatcher { val mt @ MethodType(_) = extractor.tpe.widen var unapp = extractor.appliedTo(ref(scrutinee).ensureConforms(mt.paramInfos.head)) if (implicits.nonEmpty) unapp = unapp.appliedToArgs(implicits) - val unappPlan = unapplyPlan(unapp, args) - if (scrutinee.info.isNotNull || nonNull(scrutinee)) unappPlan - else TestPlan(NonNullTest, scrutinee, tree.pos, unappPlan) + val scrutineeTpe = scrutinee.info.widenDealias + if (scrutineeTpe.isBottomType || scrutineeTpe.isNullType) { + // If the value being matched against has type `Nothing`, then the unapply code + // will never execute. + // If it has type `Null`, then the `NonNullTest` below guarantees that the unapply code + // won't execute either. + // So we don't need a plan in this case. + NoOpPlan + } else { + val unappPlan = unapplyPlan(unapp, args) + if (scrutinee.info.isNotNull || nonNull(scrutinee)) unappPlan + else TestPlan(NonNullTest, scrutinee, tree.pos, unappPlan) + } case Bind(name, body) => if (name == nme.WILDCARD) patternPlan(scrutinee, body, onSuccess) else { @@ -418,6 +429,7 @@ object PatternMatcher { case plan: ReturnPlan => apply(plan) case plan: SeqPlan => apply(plan) case plan: ResultPlan => plan + case NoOpPlan => NoOpPlan } } @@ -694,6 +706,7 @@ object PatternMatcher { private def canFallThrough(plan: Plan): Boolean = plan match { case _:ReturnPlan | _:ResultPlan => false case _:TestPlan | _:LabeledPlan => true + case NoOpPlan => true case LetPlan(_, body) => canFallThrough(body) case SeqPlan(_, tail) => canFallThrough(tail) } @@ -852,6 +865,9 @@ object PatternMatcher { case ResultPlan(tree) => if (tree.tpe <:< defn.NothingType) tree // For example MatchError else Return(tree, ref(resultLabel)) + case NoOpPlan => + // TODO(abeln): optimize away + Literal(Constant(true)) } } @@ -890,6 +906,8 @@ object PatternMatcher { showPlan(tail) case ResultPlan(tree) => sb.append(tree.show) + case NoOpPlan => + sb.append("NoOp") } } showPlan(plan) diff --git a/tests/pos/i5067.scala b/tests/pos/i5067.scala new file mode 100644 index 000000000000..6128a9acaa27 --- /dev/null +++ b/tests/pos/i5067.scala @@ -0,0 +1,8 @@ +class Foo { + val Some(_) = ??? + val (_, _, _) = ??? + ??? match { + case Some(_) => () + case (_, _, _) => () + } +} diff --git a/tests/run/i5067.check b/tests/run/i5067.check new file mode 100644 index 000000000000..ee69e2a9b9f7 --- /dev/null +++ b/tests/run/i5067.check @@ -0,0 +1 @@ +matches null literal diff --git a/tests/run/i5067b.check b/tests/run/i5067b.check new file mode 100644 index 000000000000..be60186f7c25 --- /dev/null +++ b/tests/run/i5067b.check @@ -0,0 +1,3 @@ +match error +match error nested +not implemented error diff --git a/tests/run/i5067b.scala b/tests/run/i5067b.scala new file mode 100644 index 000000000000..36ff84478248 --- /dev/null +++ b/tests/run/i5067b.scala @@ -0,0 +1,33 @@ +object Test { + def main(args: Array[String]): Unit = { + class B[T] {} + object B { + def unapply[T](x: Any): Option[B[T]] = None + } + try { + val B(_) = null + } catch { + case e: MatchError => println("match error") + } + + null match { + case null => + try { + null match { + case Some(_) => () + } + } catch { + case e: MatchError => println("match error nested") + } + } + + try { + ??? match { + case (_, _) => () + case _ => () + } + } catch { + case e: NotImplementedError => println("not implemented error") + } + } +}