Skip to content

Commit 3349444

Browse files
authored
Simple enhancement for pattern matching with capturing types (#23524)
The bind variable should carry the capture set from the selector during pattern matching. ```scala import language.experimental.captureChecking trait A case class B(x: AnyRef^) extends A def test = val x: AnyRef^ = new AnyRef val a: A^{x} = B(x) val y1: A = a match case b: B => b // error: (b: B) becomes B^{x} implicitly val y2: A^{x} = a match case b: B => b // ok val x3: AnyRef = a match case B(x2: AnyRef) => x2 // error: we lose some information about field x, but it still cannot be pure val x4: AnyRef = a match case b: B => b.x // error ```
2 parents f00c987 + d140083 commit 3349444

File tree

6 files changed

+38
-12
lines changed

6 files changed

+38
-12
lines changed

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import Flags.*, Constants.*
1414
import Decorators.*
1515
import NameKinds.{PatMatStdBinderName, PatMatAltsName, PatMatResultName}
1616
import config.Printers.patmatch
17+
import config.Feature
1718
import reporting.*
1819
import ast.*
1920
import util.Property.*
21+
import cc.{CapturingType, Capabilities}
2022

2123
import scala.annotation.tailrec
2224
import scala.collection.mutable
@@ -427,8 +429,11 @@ object PatternMatcher {
427429
&& !hasExplicitTypeArgs(extractor)
428430
case _ => false
429431
}
432+
val castTp = if Feature.ccEnabled
433+
then CapturingType(tpt.tpe, scrutinee.termRef.singletonCaptureSet)
434+
else tpt.tpe
430435
TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span,
431-
letAbstract(ref(scrutinee).cast(tpt.tpe)) { casted =>
436+
letAbstract(ref(scrutinee).cast(castTp)) { casted =>
432437
nonNull += casted
433438
patternPlan(casted, pat, onSuccess)
434439
})

scala2-library-cc/src/scala/collection/IndexedSeqView.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ object IndexedSeqView {
160160

161161
@SerialVersionUID(3L)
162162
class Reverse[A](underlying: SomeIndexedSeqOps[A]^) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] {
163-
override def reverse: IndexedSeqView[A] = underlying match {
163+
override def reverse: IndexedSeqView[A]^{underlying} = underlying match {
164164
case x: IndexedSeqView[A] => x
165165
case _ => super.reverse
166166
}

scala2-library-cc/src/scala/collection/View.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,13 @@ object View extends IterableFactory[View] {
152152
def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} =
153153
underlying match {
154154
case filter: Filter[A] if filter.isFlipped == isFlipped =>
155-
new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
156-
.asInstanceOf[Filter[A]^{underlying, p}]
155+
unsafeAssumeSeparate:
156+
new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
157+
.asInstanceOf[Filter[A]^{underlying, p}]
157158
// !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization
158-
//case filter: Filter[A]^{underlying} if filter.isFlipped == isFlipped =>
159-
// unsafeAssumeSeparate:
160159
// See filter-iterable.scala for a test where a variant of Filter
161160
// works without the unsafeAssumeSeparate. But it requires significant
162161
// changes compared to the version here. See also Filter in colltest5.CollectionStrawManCC5_1.
163-
// new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
164162
case _ => new Filter(underlying, p, isFlipped)
165163
}
166164
}

scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private[mutable] object CheckedIndexedSeqView {
101101
@SerialVersionUID(3L)
102102
class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int)
103103
extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] {
104-
override def reverse: IndexedSeqView[A] = underlying match {
104+
override def reverse: IndexedSeqView[A]^{underlying} = underlying match {
105105
case x: IndexedSeqView[A] => x
106106
case _ => super.reverse
107107
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import language.experimental.captureChecking
2+
3+
trait A
4+
5+
case class B(x: AnyRef^) extends A
6+
7+
def test =
8+
val x: AnyRef^ = new AnyRef
9+
val a: A^{x} = B(x)
10+
11+
val y1: A = a match
12+
case b: B => b // error: (b: B) becomes B^{x} implicitly
13+
14+
val y2: A^{x} = a match
15+
case b: B =>
16+
val bb: B^{b} = b
17+
val aa: A^{a} = bb
18+
b // ok
19+
20+
val x3: AnyRef = a match
21+
case B(x2: AnyRef) => x2 // error: we lose some information about field x, but it still cannot be pure
22+
23+
val x4: AnyRef = a match
24+
case b: B => b.x // error

tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,12 @@ object CollectionStrawMan5 {
457457
def apply[A](underlying: Iterable[A]^, pp: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, pp} =
458458
underlying match
459459
case filter: Filter[A] =>
460-
new Filter(filter.underlying, a => filter.p(a) && pp(a))
461-
.asInstanceOf[Filter[A]^{underlying, pp}]
462-
//unsafeAssumeSeparate:
460+
unsafeAssumeSeparate:
461+
new Filter(filter.underlying, a => filter.p(a) && pp(a))
462+
.asInstanceOf[Filter[A]^{underlying, pp}]
463463
// See filter-iterable.scala for a test where a variant of Filter
464464
// works without the unsafeAssumeSeparate. But it requires significant
465465
// changes compared to the version here.
466-
//new Filter(filter.underlying, a => filter.p(a) && pp(a))
467466
case _ => new Filter(underlying, pp)
468467

469468
case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) {

0 commit comments

Comments
 (0)