Skip to content

Commit 4a8ad7d

Browse files
committed
Track variable insytantiations in capture sets
If a variable in a capture set gets instantiated, the capture set has to update itself as well.
1 parent 59151bd commit 4a8ad7d

File tree

3 files changed

+61
-10
lines changed

3 files changed

+61
-10
lines changed

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

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,36 @@ import reporting.trace
1111
import printing.{Showable, Printer}
1212
import printing.Texts.*
1313

14-
case class CaptureSet private (elems: CaptureSet.Refs) extends Showable:
14+
case class CaptureSet private (elems0: CaptureSet.Refs) extends Showable:
1515
import CaptureSet.*
1616

17-
def isEmpty: Boolean = elems.isEmpty
18-
def nonEmpty: Boolean = !isEmpty
17+
def isEmpty(using Context): Boolean = elems.isEmpty
18+
def nonEmpty(using Context): Boolean = !isEmpty
1919

20-
def ++ (that: CaptureSet): CaptureSet =
20+
private var isProvisional = true
21+
private var myElems: CaptureSet.Refs = elems0
22+
23+
def elems(using Context): CaptureSet.Refs =
24+
if isProvisional then
25+
isProvisional = false
26+
myElems.foreach {
27+
case tv: TypeVar =>
28+
if tv.isInstantiated then myElems = myElems - tv ++ tv.inst.captureSet.elems
29+
else isProvisional = true
30+
case _ =>
31+
}
32+
myElems
33+
34+
def ++ (that: CaptureSet)(using Context): CaptureSet =
2135
if this.isEmpty then that
2236
else if that.isEmpty then this
23-
else CaptureSet(elems ++ that.elems)
37+
else CaptureSet(myElems ++ that.elems)
2438

25-
def + (ref: CaptureRef) =
39+
def + (ref: CaptureRef)(using Context) =
2640
if elems.contains(ref) then this
2741
else CaptureSet(elems + ref)
2842

29-
def intersect (that: CaptureSet): CaptureSet =
43+
def intersect (that: CaptureSet)(using Context): CaptureSet =
3044
CaptureSet(this.elems.intersect(that.elems))
3145

3246
/** {x} <:< this where <:< is subcapturing */
@@ -46,10 +60,10 @@ case class CaptureSet private (elems: CaptureSet.Refs) extends Showable:
4660
case ref => ref.singletonCaptureSet
4761
}
4862

49-
override def toString = elems.toString
63+
override def toString = myElems.toString
5064

5165
override def toText(printer: Printer): Text =
52-
Str("{") ~ Text(elems.toList.map(printer.toTextCaptureRef), ", ") ~ Str("}")
66+
Str("{") ~ Text(myElems.toList.map(printer.toTextCaptureRef), ", ") ~ Str("}")
5367

5468
object CaptureSet:
5569
type Refs = SimpleIdentitySet[CaptureRef]

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4706,7 +4706,11 @@ object Types {
47064706

47074707
override def captureSetOfInfo(using Context): CaptureSet = instanceOpt match
47084708
case ref: CaptureRef => ref.captureSetOfInfo
4709-
case tp => tp.captureSet
4709+
case _ => underlying.captureSet
4710+
4711+
override def captureSet(using Context): CaptureSet =
4712+
if isInstantiated then inst.captureSet
4713+
else super.captureSet
47104714

47114715
// Object members
47124716

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import language.experimental.erasedDefinitions
2+
import annotation.ability
3+
import java.io.IOException
4+
5+
class CanThrow[E] extends Retains[*]
6+
type Top = Any retains *
7+
infix type ==> [A, B] = (A => B) retains *
8+
9+
def handle[E <: Exception, T <: Top](op: (CanThrow[E] ?=> T))(handler: (E => T) retains T): T =
10+
val x: CanThrow[E] = ???
11+
try op(using x)
12+
catch case ex: E => handler(ex)
13+
14+
def raise[E <: Exception](ex: E)(using CanThrow[E]): Nothing =
15+
throw ex
16+
17+
def test2: Int ==> Int =
18+
def f(a: Boolean): Boolean => CanThrow[IOException] ?=> Int ==> Int =
19+
handle {
20+
if !a then raise(IOException())
21+
(b: Boolean) => (_: CanThrow[IOException]) ?=>
22+
if !b then raise(IOException())
23+
(x: Int) => 1
24+
} {
25+
ex => (b: Boolean) => (_: CanThrow[IOException]) ?=> (x: Int) => -1
26+
}
27+
handle { // error: inferred type argument ((Int => Int) retains *) is not allowed to capture the universal capability *
28+
val g = f(true)
29+
g(false) // would raise an uncaught exception
30+
f(true)(false) // would raise an uncaught exception
31+
} {
32+
ex => (x: Int) => -1
33+
}

0 commit comments

Comments
 (0)