Skip to content

Commit b65b0f2

Browse files
authored
Space: Refine isSubspace property & an example (#16574)
2 parents a127160 + 848152b commit b65b0f2

File tree

2 files changed

+78
-9
lines changed

2 files changed

+78
-9
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,15 @@ trait SpaceLogic {
116116
/** Simplify space such that a space equal to `Empty` becomes `Empty` */
117117
def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match {
118118
case Prod(tp, fun, spaces) =>
119-
val sps = spaces.map(simplify(_))
119+
val sps = spaces.mapconserve(simplify)
120120
if (sps.contains(Empty)) Empty
121121
else if (canDecompose(tp) && decompose(tp).isEmpty) Empty
122-
else Prod(tp, fun, sps)
122+
else if sps eq spaces then space else Prod(tp, fun, sps)
123123
case Or(spaces) =>
124-
val spaces2 = spaces.map(simplify(_)).filter(_ != Empty)
124+
val spaces2 = spaces.map(simplify).filter(_ != Empty)
125125
if spaces2.isEmpty then Empty
126-
else if spaces2.lengthCompare(1) == 0 then spaces2.head
127-
else Or(spaces2)
126+
else if spaces2.lengthIs == 1 then spaces2.head
127+
else if spaces2.corresponds(spaces)(_ eq _) then space else Or(spaces2)
128128
case Typ(tp, _) =>
129129
if (canDecompose(tp) && decompose(tp).isEmpty) Empty
130130
else space
@@ -164,12 +164,15 @@ trait SpaceLogic {
164164
List(space)
165165
}
166166

167-
/** Is `a` a subspace of `b`? Equivalent to `a - b == Empty`, but faster */
167+
/** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */
168168
def isSubspace(a: Space, b: Space)(using Context): Boolean = trace(s"isSubspace(${show(a)}, ${show(b)})", debug) {
169169
def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b)
170170
def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp)))
171171

172-
(simplify(a), simplify(b)) match {
172+
val a2 = simplify(a)
173+
val b2 = simplify(b)
174+
if (a ne a2) || (b ne b2) then isSubspace(a2, b2)
175+
else (a, b) match {
173176
case (Empty, _) => true
174177
case (_, Empty) => false
175178
case (Or(ss), _) =>
@@ -266,9 +269,11 @@ trait SpaceLogic {
266269
tryDecompose2(tp2)
267270
else
268271
a
272+
case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2))
273+
if (!isSameUnapply(fun1, fun2)) => a
274+
case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2))
275+
if (fun1.symbol.name == nme.unapply && ss1.length != ss2.length) => a
269276
case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) =>
270-
if (!isSameUnapply(fun1, fun2)) return a
271-
if (fun1.symbol.name == nme.unapply && ss1.length != ss2.length) return a
272277

273278
val range = (0 until ss1.size).toList
274279
val cache = Array.fill[Space | Null](ss2.length)(null)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dotty.tools
2+
package dotc
3+
package transform
4+
package patmat
5+
6+
import core.*, Annotations.*, Contexts.*, Decorators.*, Flags.*, Names.*, StdNames.*, Symbols.*, Types.*
7+
import ast.*, tpd.*
8+
9+
import vulpix.TestConfiguration, TestConfiguration.basicClasspath
10+
11+
import org.junit, junit.Test, junit.Assert.*
12+
13+
class SpaceEngineTest:
14+
@Test def isSubspaceTest1: Unit = inCompilerContext(basicClasspath) {
15+
// Testing the property of `isSubspace` that:
16+
// isSubspace(a, b) <=> simplify(simplify(a) - simplify(a)) == Empty
17+
// Previously there were no simplify calls,
18+
// and this is a counter-example,
19+
// for which you need either to simplify(b) or simplify the minus result.
20+
val engine = patmat.SpaceEngine()
21+
import engine.*
22+
23+
val tp = defn.ConsClass.typeRef.appliedTo(defn.AnyType)
24+
val unappTp = requiredMethod("scala.collection.immutable.::.unapply").termRef
25+
val params = List(Empty, Typ(tp))
26+
27+
val a = Prod(tp, unappTp, params)
28+
val b = Empty
29+
30+
val res1 = isSubspace(a, b)
31+
32+
val a2 = simplify(a)
33+
val b2 = simplify(b)
34+
val rem1 = minus(a2, b2)
35+
val rem2 = simplify(rem1)
36+
val res2 = rem2 == Empty
37+
38+
assertEquals(
39+
i"""|isSubspace:
40+
|
41+
|isSubspace(a, b) = $res1
42+
|
43+
|Should be equivalent to:
44+
|simplify(simplify(a) - simplify(b)) == Empty
45+
|simplify(a2 - b2) == Empty
46+
|simplify(rem1) == Empty
47+
|rem2 == Empty
48+
|
49+
|a = ${show(a)}
50+
|b = ${show(b)}
51+
|a2 = ${show(a2)}
52+
|b2 = ${show(b2)}
53+
|rem1 = ${show(rem1)}
54+
|rem2 = ${show(rem2)}
55+
|
56+
|a = ${a.toString}
57+
|b = ${b.toString}
58+
|a2 = ${a2.toString}
59+
|b2 = ${b2.toString}
60+
|rem1 = ${rem1.toString}
61+
|rem2 = ${rem2.toString}
62+
|
63+
|""".stripMargin, res1, res2)
64+
}

0 commit comments

Comments
 (0)