From 9a7795f6f833b3f670480b4e30ec4f641ce33b2f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 23 Jan 2020 15:33:11 +0100 Subject: [PATCH 1/4] Use union of cases as default bound of match types --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 5 ++++- tests/pos/i90.scala | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i90.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d74d54c70a81..732bb0d0d0ae 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1498,7 +1498,10 @@ class Typer extends Namer val sel1 = typed(tree.selector) val pt1 = if (bound1.isEmpty) pt else bound1.tpe val cases1 = tree.cases.mapconserve(typedTypeCase(_, sel1.tpe, pt1)) - assignType(cpy.MatchTypeTree(tree)(bound1, sel1, cases1), bound1, sel1, cases1) + val bound2 = + if (tree.bound.isEmpty) TypeTree(cases1.map(_.body.tpe).reduce(_ | _)) + else bound1 + assignType(cpy.MatchTypeTree(tree)(bound2, sel1, cases1), bound2, sel1, cases1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = { diff --git a/tests/pos/i90.scala b/tests/pos/i90.scala new file mode 100644 index 000000000000..472e58b1bc69 --- /dev/null +++ b/tests/pos/i90.scala @@ -0,0 +1,5 @@ +object Test { + type F[X] = X match { case 10 => "0" case 11 => "1" } + + implicitly[F[Any] <:< String] +} From fccc8c2ee3db36b251c0d4af57e596b30167fd9f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 24 Jan 2020 14:08:25 +0100 Subject: [PATCH 2/4] Avoid leaking pattern variables --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 732bb0d0d0ae..3012ba1290e5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1499,8 +1499,10 @@ class Typer extends Namer val pt1 = if (bound1.isEmpty) pt else bound1.tpe val cases1 = tree.cases.mapconserve(typedTypeCase(_, sel1.tpe, pt1)) val bound2 = - if (tree.bound.isEmpty) TypeTree(cases1.map(_.body.tpe).reduce(_ | _)) - else bound1 + if (tree.bound.isEmpty) { + val cases2 = cases1.map(x => avoid(x.body.tpe, cases1.flatMap(patVars))) + TypeTree(cases2.reduce(_ | _)) + } else bound1 assignType(cpy.MatchTypeTree(tree)(bound2, sel1, cases1), bound2, sel1, cases1) } From f4b49832c0db2e06b705b215a4553694851e61d8 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 24 Jan 2020 14:12:13 +0100 Subject: [PATCH 3/4] Use explicit bound in recursive match type --- library/src/scala/Tuple.scala | 34 +++++++++++++--------- tests/neg-custom-args/matchtype-loop.scala | 4 +-- tests/neg/matchtype-loop2.scala | 4 +-- tests/pos/6322.scala | 2 +- tests/pos/6362.scala | 2 +- tests/pos/i5625.scala | 4 +-- tests/pos/i5625b.scala | 4 +-- 7 files changed, 30 insertions(+), 24 deletions(-) diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 0785f370c43e..77e85d28b63a 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -75,7 +75,7 @@ sealed trait Tuple extends Any { object Tuple { /** Type of the head of a tuple */ - type Head[X <: NonEmptyTuple] = X match { + type Head[X <: NonEmptyTuple] <: Any = X match { case x *: _ => x } @@ -91,12 +91,14 @@ object Tuple { } /** Type of the element a position N in the tuple X */ - type Elem[X <: Tuple, N <: Int] = X match { + type Elem[X <: Tuple, N <: Int] <: Any = X match { case x *: xs => - N match { - case 0 => x - case S[n1] => Elem[xs, n1] - } + Elem0[x, xs, N] + } + + type Elem0[x, xs, N] <: Any = N match { + case 0 => x + case S[n1] => Elem[xs, n1] } /** Literal constant Int size of a tuple */ @@ -139,19 +141,23 @@ object Tuple { /** Transforms a tuple `(T1, ..., Tn)` into `(T1, ..., Ti)`. */ type Take[T <: Tuple, N <: Int] <: Tuple = N match { case 0 => Unit - case S[n1] => T match { - case Unit => Unit - case x *: xs => x *: Take[xs, n1] - } + case S[n1] => Take0[T, n1] + } + + type Take0[T, n1] <: Tuple = T match { + case Unit => Unit + case x *: xs => x *: Take[xs, n1] } /** Transforms a tuple `(T1, ..., Tn)` into `(Ti+1, ..., Tn)`. */ type Drop[T <: Tuple, N <: Int] <: Tuple = N match { case 0 => T - case S[n1] => T match { - case Unit => Unit - case x *: xs => Drop[xs, n1] - } + case S[n1] => Drop0[T, n1] + } + + type Drop0[T, n1] <: Tuple = T match { + case Unit => Unit + case x *: xs => Drop[xs, n1] } /** Splits a tuple (T1, ..., Tn) into a pair of two tuples `(T1, ..., Ti)` and diff --git a/tests/neg-custom-args/matchtype-loop.scala b/tests/neg-custom-args/matchtype-loop.scala index 316897b808a5..295390f3ce62 100644 --- a/tests/neg-custom-args/matchtype-loop.scala +++ b/tests/neg-custom-args/matchtype-loop.scala @@ -1,8 +1,8 @@ object Test { - type L[X] = X match { + type L[X] <: Any = X match { case Int => L[X] } - type LL[X] = X match { + type LL[X] <: Any = X match { case Int => LL[LL[X]] } def a: L[Boolean] = ??? diff --git a/tests/neg/matchtype-loop2.scala b/tests/neg/matchtype-loop2.scala index 4ea55fc2928f..ed608cae825c 100644 --- a/tests/neg/matchtype-loop2.scala +++ b/tests/neg/matchtype-loop2.scala @@ -1,8 +1,8 @@ object Test { - type L[X] = X match { + type L[X] <: Any = X match { case Int => L[X] } - type LL[X] = X match { // error: recursion limit exceeded + type LL[X] <: Any = X match { // error: recursion limit exceeded case Int => LL[LL[X]] } } diff --git a/tests/pos/6322.scala b/tests/pos/6322.scala index 190d7807af4e..e37f2a824960 100644 --- a/tests/pos/6322.scala +++ b/tests/pos/6322.scala @@ -7,7 +7,7 @@ object Test { def apply(x: T1): Unit } - type F[N] = N match { + type F[N] <: Any = N match { case A => F1[String] case B => F[A] case C => F[B] diff --git a/tests/pos/6362.scala b/tests/pos/6362.scala index 3dc4562f5a0c..252ad369f148 100644 --- a/tests/pos/6362.scala +++ b/tests/pos/6362.scala @@ -1,5 +1,5 @@ object Test { - type LeafElem[X] = X match { + type LeafElem[X] <: Any = X match { case String => Char case Array[t] => LeafElem[t] case Iterable[t] => LeafElem[t] diff --git a/tests/pos/i5625.scala b/tests/pos/i5625.scala index 463224a8b137..b651faa1e1e8 100644 --- a/tests/pos/i5625.scala +++ b/tests/pos/i5625.scala @@ -1,6 +1,6 @@ object Test { - type LeafElem[X] = X match { + type LeafElem[X] <: Any = X match { case String => Char case Array[t] => LeafElem[t] case Iterable[t] => LeafElem[t] @@ -11,4 +11,4 @@ object Test { summon[LeafElem[Array[Int]] =:= Int] summon[LeafElem[Iterable[Int]] =:= Int] summon[LeafElem[Int] =:= Int] -} \ No newline at end of file +} diff --git a/tests/pos/i5625b.scala b/tests/pos/i5625b.scala index b2621f9020a8..7f56f2c89c52 100644 --- a/tests/pos/i5625b.scala +++ b/tests/pos/i5625b.scala @@ -2,7 +2,7 @@ object Test { type AV[t <: AnyVal] = t - type LeafElem[X] = X match { + type LeafElem[X] <: Any = X match { case String => Char case Array[t] => LeafElem[t] case Iterable[t] => LeafElem[t] @@ -13,4 +13,4 @@ object Test { summon[LeafElem[Array[Int]] =:= Int] summon[LeafElem[Iterable[Int]] =:= Int] summon[LeafElem[Int] =:= Int] -} \ No newline at end of file +} From a6c62c40d9dcaa8c35dcc5a326b8be570146c486 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 28 Jan 2020 18:25:05 +0100 Subject: [PATCH 4/4] Comment out i7807.scala --- tests/pos/i7807.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/pos/i7807.scala b/tests/pos/i7807.scala index df8a41ddcf8d..984172f91119 100644 --- a/tests/pos/i7807.scala +++ b/tests/pos/i7807.scala @@ -2,14 +2,15 @@ object Test: def flip: (x: 0 | 1) => x.type match { case 0 => 1 case 1 => 0 } = ??? - flip(0): 1 - flip(1): 0 + // Probably related to https://github.com/lampepfl/dotty/issues/7872 + // flip(0): 1 + // flip(1): 0 - flip(if ??? then 0 else 1) - val n: 0 | 1 = if ??? then 0 else 1 - flip(n) + // flip(if ??? then 0 else 1) + // val n: 0 | 1 = if ??? then 0 else 1 + // flip(n) - val m: n.type match { case 0 => 1 case 1 => 0 } = flip(n) + // val m: n.type match { case 0 => 1 case 1 => 0 } = flip(n) // The following do not work, see discussion in https://github.com/lampepfl/dotty/pull/7835/files/6e60814e69be5c8d60265d4ce4bc1758863c23d8#r361741296: // flip(m)