From 31ade7807f6ac3a7a3c4afa4ee0bd9579fef21d2 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 2 Jul 2024 23:27:52 +0200 Subject: [PATCH 1/7] Approximate MatchTypes with lub of case bodies, if non-recursive [Cherry-picked 8f2b6cf0dc2d681be6c585572e7cf0680e627d2e][modified] --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 11 +++++++++++ tests/pos/i19710.scala | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/pos/i19710.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 7f7c167c865a..1727e0b1c5ba 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -978,6 +978,17 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (tp1 ne tp1norm) recur(tp1norm, tp2) else either(recur(tp11, tp2), recur(tp12, tp2)) case tp1: MatchType => + def compareUpper = + val lub1 = tp1.cases.foldLeft(defn.NothingType: Type): (acc, case1) => + if acc.exists then + val rhs = case1.resultType match { case defn.MatchCase(_, body) => body } + val isRecursive = rhs.existsPart(_.isInstanceOf[LazyRef]) + if isRecursive then NoType else lub(acc, rhs) + else acc + if lub1.exists then + recur(lub1, tp2) + else + recur(tp1.underlying, tp2) def compareMatch = tp2 match { case tp2: MatchType => isSameType(tp1.scrutinee, tp2.scrutinee) && diff --git a/tests/pos/i19710.scala b/tests/pos/i19710.scala new file mode 100644 index 000000000000..03fd1e2d80b3 --- /dev/null +++ b/tests/pos/i19710.scala @@ -0,0 +1,11 @@ +import scala.util.NotGiven + +type HasName1 = [n] =>> [x] =>> x match { + case n => true + case _ => false + } +@main def Test = { + summon[HasName1["foo"]["foo"] =:= true] + summon[NotGiven[HasName1["foo"]["bar"] =:= true]] + summon[Tuple.Filter[(1, "foo", 2, "bar"), HasName1["foo"]] =:= Tuple1["foo"]] // error +} From 3929246c54d08828ae6b78a562392a2923c55d3a Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 2 Jul 2024 23:28:45 +0200 Subject: [PATCH 2/7] Lub MatchTypeTree case body types and store as the inferred bound [Cherry-picked c25eddec7f4340fc8fa39b9985d4f3e7929af067][modified] --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 11 ----------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 10 +++++++++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 1727e0b1c5ba..7f7c167c865a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -978,17 +978,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (tp1 ne tp1norm) recur(tp1norm, tp2) else either(recur(tp11, tp2), recur(tp12, tp2)) case tp1: MatchType => - def compareUpper = - val lub1 = tp1.cases.foldLeft(defn.NothingType: Type): (acc, case1) => - if acc.exists then - val rhs = case1.resultType match { case defn.MatchCase(_, body) => body } - val isRecursive = rhs.existsPart(_.isInstanceOf[LazyRef]) - if isRecursive then NoType else lub(acc, rhs) - else acc - if lub1.exists then - recur(lub1, tp2) - else - recur(tp1.underlying, tp2) def compareMatch = tp2 match { case tp2: MatchType => isSameType(tp1.scrutinee, tp2.scrutinee) && diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bdb900971b48..6aaf942a0295 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2278,7 +2278,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer report.error(MatchTypeScrutineeCannotBeHigherKinded(sel1Tpe), sel1.srcPos) val pt1 = if (bound1.isEmpty) pt else bound1.tpe val cases1 = tree.cases.mapconserve(typedTypeCase(_, sel1Tpe, pt1)) - assignType(cpy.MatchTypeTree(tree)(bound1, sel1, cases1), bound1, sel1, cases1) + val bound2 = if tree.bound.isEmpty then + val lub = cases1.foldLeft(defn.NothingType: Type): (acc, case1) => + if !acc.exists then NoType + else if case1.body.tpe.existsPart(_.isInstanceOf[LazyRef]) then NoType + else acc | TypeOps.avoid(case1.body.tpe, patVars(case1)) + if lub.exists then TypeTree(lub, inferred = true) + else bound1 + else bound1 + assignType(cpy.MatchTypeTree(tree)(bound2, sel1, cases1), bound2, sel1, cases1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(using Context): ByNameTypeTree = tree.result match From 1436b1e0fb7bf96981a5de23ae4e447679450064 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Feb 2024 13:48:48 +0000 Subject: [PATCH 3/7] Use isProvisional instead of has LazyRef [Cherry-picked 55ecbfca4057908025b3551a29d3b6130647a725] --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- tests/pos/Tuple.Drop.scala | 7 +++++++ tests/pos/Tuple.Elem.scala | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/pos/Tuple.Drop.scala create mode 100644 tests/pos/Tuple.Elem.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6aaf942a0295..9bf7fe8bc247 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2281,8 +2281,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val bound2 = if tree.bound.isEmpty then val lub = cases1.foldLeft(defn.NothingType: Type): (acc, case1) => if !acc.exists then NoType - else if case1.body.tpe.existsPart(_.isInstanceOf[LazyRef]) then NoType - else acc | TypeOps.avoid(case1.body.tpe, patVars(case1)) + else if case1.body.tpe.isProvisional then NoType + else acc | case1.body.tpe if lub.exists then TypeTree(lub, inferred = true) else bound1 else bound1 diff --git a/tests/pos/Tuple.Drop.scala b/tests/pos/Tuple.Drop.scala new file mode 100644 index 000000000000..9b88cc227966 --- /dev/null +++ b/tests/pos/Tuple.Drop.scala @@ -0,0 +1,7 @@ +import compiletime.ops.int.* + +type Drop[T <: Tuple, N <: Int] <: Tuple = N match + case 0 => T + case S[n1] => T match + case EmptyTuple => EmptyTuple + case x *: xs => Drop[xs, n1] diff --git a/tests/pos/Tuple.Elem.scala b/tests/pos/Tuple.Elem.scala new file mode 100644 index 000000000000..81494485c321 --- /dev/null +++ b/tests/pos/Tuple.Elem.scala @@ -0,0 +1,7 @@ +import compiletime.ops.int.* + +type Elem[T <: Tuple, I <: Int] = T match + case h *: tail => + I match + case 0 => h + case S[j] => Elem[tail, j] From 68e67053d3a14b9e675dcff1ad6b213bc4b40bd1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Feb 2024 15:09:38 +0000 Subject: [PATCH 4/7] Fix PlusTri in pos/13633 [Cherry-picked 3419aae7d548ac9a74c4139852bf23a66302696d] --- tests/pos/13633.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/13633.scala b/tests/pos/13633.scala index ca0f7e68e81e..8883ef98d0be 100644 --- a/tests/pos/13633.scala +++ b/tests/pos/13633.scala @@ -21,7 +21,7 @@ object Sums extends App: type Reverse[A] = ReverseLoop[A, EmptyTuple] - type PlusTri[A, B, C] = (A, B, C) match + type PlusTri[A, B, C] <: Tuple = (A, B, C) match case (false, false, false) => (false, false) case (true, false, false) | (false, true, false) | (false, false, true) => (false, true) case (true, true, false) | (true, false, true) | (false, true, true) => (true, false) From ef56f159b8fd8438922d56ffdfb3a97456df6cdf Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 2 Jul 2024 23:30:12 +0200 Subject: [PATCH 5/7] Handle NoType in disjointnessBoundary [Cherry-picked 2423198a8b68e870a42890bf325866e7acfa0942][modified] From 59ab8494bcf9f4f1206b80e92a6f5116d8ad12a4 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 2 Jul 2024 23:45:47 +0200 Subject: [PATCH 6/7] Fix run-macros/type-show [Cherry-picked db1dcdc0e11516a61c4edc25776a6ea0d5dacf78][modified] --- tests/run-macros/type-show/Test_2.scala | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/run-macros/type-show/Test_2.scala b/tests/run-macros/type-show/Test_2.scala index d741a426cd69..ce0f71729efb 100644 --- a/tests/run-macros/type-show/Test_2.scala +++ b/tests/run-macros/type-show/Test_2.scala @@ -1,18 +1,34 @@ object Test { import TypeToolbox.* + + def assertEql[A](obt: A, exp: A): Unit = + assert(obt == exp, s"\nexpected: $exp\nobtained: $obt") + def main(args: Array[String]): Unit = { val x = 5 - assert(show[x.type] == "x.type") - assert(show[Nil.type] == "scala.Nil.type") - assert(show[Int] == "scala.Int") - assert(show[Int => Int] == "scala.Function1[scala.Int, scala.Int]") - assert(show[(Int, String)] == "scala.Tuple2[scala.Int, scala.Predef.String]") - assert(show[[X] =>> X match { case Int => Int }] == + assertEql(show[x.type], "x.type") + assertEql(show[Nil.type], "scala.Nil.type") + assertEql(show[Int], "scala.Int") + assertEql(show[Int => Int], "scala.Function1[scala.Int, scala.Int]") + assertEql(show[(Int, String)], "scala.Tuple2[scala.Int, scala.Predef.String]") + assertEql(show[[X] =>> X match { case Int => Int }], """[X >: scala.Nothing <: scala.Any] => X match { | case scala.Int => scala.Int |}""".stripMargin) - assert(showStructure[[X] =>> X match { case Int => Int }] == """TypeLambda(List(X), List(TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))), MatchType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"), ParamRef(binder, 0), List(MatchCase(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")))))""") + assertEql(showStructure[[X] =>> X match { case Int => Int }], + """TypeLambda("""+ + """List(X), """+ + """List(TypeBounds("""+ + """TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), """+ + """TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))), """+ + """MatchType("""+ + """TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int"), """+ // match type bound + """ParamRef(binder, 0), """+ + """List("""+ + """MatchCase("""+ + """TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int"), """+ + """TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")))))""") // TODO: more complex types: // - implicit function types From e60c5ca1e514b41cb454a5ec67b51125e4c39149 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 2 Jul 2024 23:46:34 +0200 Subject: [PATCH 7/7] Rework ParamRef#underlying handling in disjointnessBoundary [Cherry-picked d687dee2edac1078526af7e14837c742d458bb4d][modified]