Skip to content

Commit ebacbb3

Browse files
committed
Distribute capture set into and-type on RHS of comparison
Implements ``` A <: (B & C)^cs if A <: B^cs and A <: C^cs ``` This is needed if A is a singleton type whose capture set gets unwrapped later. Fixes scala#25460
1 parent 5b0f56d commit ebacbb3

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -873,26 +873,29 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
873873
try
874874
if refs1.isAlwaysEmpty && refs1.mutability == CaptureSet.Mutability.Ignored then
875875
recur(tp1, parent2)
876-
else
877-
// The singletonOK branch is because we sometimes have a larger capture set in a singleton
878-
// than in its underlying type. An example is `f: () -> () ->{x} T`, which might be
879-
// the type of a closure (in one of the variants we are considering). In that case the
880-
// capture set of `f.type` is `{x}` but the capture set of the underlying type is `{}`.
881-
// So without the `singletonOK` test, a singleton might not be a subtype of its underlying type.
882-
// Eamples where this arises is capt-capibility.scala and function-combinators.scala
883-
val singletonOK = tp1 match
884-
case tp1: SingletonType
885-
if subCaptures(tp1.underlying.captureSet, refs2, CaptureSet.VarState.Separate) =>
886-
recur(tp1.widen, tp2)
887-
case _ =>
888-
false
889-
singletonOK
890-
|| compareCaptures(tp1, refs1, tp2, refs2)
891-
&& (recur(tp1.widen.stripCapturing, parent2)
892-
|| tp1.isInstanceOf[SingletonType] && recur(tp1, parent2)
893-
// this alternative is needed in case the right hand side is a
894-
// capturing type that contains the lhs as an alternative of a union type.
895-
)
876+
else parent2 match
877+
case AndType(p1, p2) =>
878+
recur(tp1, p1.capturing(refs2)) && recur(tp1, p2.capturing(refs2))
879+
case _ =>
880+
// The singletonOK branch is because we sometimes have a larger capture set in a singleton
881+
// than in its underlying type. An example is `f: () -> () ->{x} T`, which might be
882+
// the type of a closure (in one of the variants we are considering). In that case the
883+
// capture set of `f.type` is `{x}` but the capture set of the underlying type is `{}`.
884+
// So without the `singletonOK` test, a singleton might not be a subtype of its underlying type.
885+
// Examples where this arises is capt-capibility.scala and function-combinators.scala
886+
val singletonOK = tp1 match
887+
case tp1: SingletonType
888+
if subCaptures(tp1.underlying.captureSet, refs2, CaptureSet.VarState.Separate) =>
889+
recur(tp1.widen, tp2)
890+
case _ =>
891+
false
892+
singletonOK
893+
|| compareCaptures(tp1, refs1, tp2, refs2)
894+
&& (recur(tp1.widen.stripCapturing, parent2)
895+
|| tp1.isInstanceOf[SingletonType] && recur(tp1, parent2)
896+
// this alternative is needed in case the right hand side is a
897+
// capturing type that contains the lhs as an alternative of a union type.
898+
)
896899
catch case ex: AssertionError =>
897900
println(i"assertion failed while compare captured $tp1 <:< $tp2")
898901
throw ex
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import language.experimental.captureChecking
2+
import language.experimental.modularity
3+
4+
sealed class Region() extends caps.SharedCapability
5+
final class MutRegion extends Region
6+
7+
sealed trait MyList[+A]:
8+
def head: A
9+
object MyNil extends MyList[Nothing]:
10+
def head = throw new NoSuchElementException
11+
12+
class MyCons[A](val head: A, private var _next: MyList[A]) extends MyList[A]:
13+
type SetterCapability <: Region
14+
def next: MyList[A] = _next
15+
def next_=(v: MyList[A])(using SetterCapability & MutRegion): Unit = _next = v
16+
17+
object MyCons:
18+
def apply[A](head: A, next: MyList[A])(using ev: Region)
19+
: MyCons[A] { type SetterCapability = ev.type } =
20+
new MyCons(head, next) { type SetterCapability = ev.type }
21+
22+
class MyListBuffer[A](tracked val heap: MutRegion = new MutRegion):
23+
private var last0: MyCons[A] { type SetterCapability = heap.type } = null
24+
25+
def assign(f: A): this.type =
26+
given this.heap.type = this.heap
27+
val v = MyCons(f, MyNil)
28+
last0.next = v // error
29+
this

0 commit comments

Comments
 (0)