Skip to content

Commit a3e9f6d

Browse files
committed
Handle this in capture sets
1 parent cecf206 commit a3e9f6d

File tree

9 files changed

+196
-11
lines changed

9 files changed

+196
-11
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,18 @@ sealed abstract class CaptureSet extends Showable:
8888
if accountsFor(elem) then CompareResult.OK
8989
else addNewElems(elem.singletonCaptureSet.elems, origin)
9090

91+
extension (x: CaptureRef) private def subsumes(y: CaptureRef) =
92+
(x eq y)
93+
|| y.match
94+
case y: TermRef => y.prefix eq x // ^^^ y.prefix.subsumes(x) ?
95+
case _ => false
96+
9197
/** {x} <:< this where <:< is subcapturing, but treating all variables
9298
* as frozen.
9399
*/
94100
def accountsFor(x: CaptureRef)(using ctx: Context): Boolean =
95101
reporting.trace(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true) {
96-
elems.contains(x)
102+
elems.exists(_.subsumes(x))
97103
|| !x.isRootCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK
98104
}
99105

@@ -502,6 +508,14 @@ object CaptureSet:
502508
yield captureSetOf(arg)
503509
css.foldLeft(empty)(_ ++ _)
504510
*/
511+
def ofInfo(ref: CaptureRef)(using Context): CaptureSet = ref match
512+
case ref: ThisType =>
513+
val declaredCaptures = ref.cls.givenSelfType.captureSet
514+
ref.cls.paramAccessors.foldLeft(declaredCaptures) ((cs, acc) =>
515+
cs ++ acc.termRef.captureSetOfInfo) // ^^^ need to also include outer references of inner classes
516+
.showing(i"cc info $ref with ${ref.cls.paramAccessors.map(_.termRef)}%, % = $result", capt)
517+
case ref: TermRef if ref.isRootCapability => ref.singletonCaptureSet
518+
case _ => ofType(ref.underlying)
505519

506520
def ofType(tp: Type)(using Context): CaptureSet =
507521
def recur(tp: Type): CaptureSet = tp.dealias match

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,9 +2066,7 @@ object Types {
20662066
else if myCaptureSet eq CaptureSet.Pending then CaptureSet.empty
20672067
else
20682068
myCaptureSet = CaptureSet.Pending
2069-
val computed =
2070-
if isRootCapability then singletonCaptureSet
2071-
else CaptureSet.ofType(underlying)
2069+
val computed = CaptureSet.ofInfo(this)
20722070
if ctx.phase != Phases.checkCapturesPhase || underlying.isProvisional then
20732071
myCaptureSet = null
20742072
else
@@ -2874,11 +2872,7 @@ object Types {
28742872
// can happen in IDE if `cls` is stale
28752873
}
28762874

2877-
override def canBeTracked(using Context) = cls.owner.isTerm
2878-
2879-
override def captureSetOfInfo(using Context): CaptureSet =
2880-
super.captureSetOfInfo
2881-
++ CaptureSet.ofClass(cls.classInfo, cls.paramAccessors.map(_.info))
2875+
def canBeTracked(using Context) = true
28822876

28832877
override def computeHash(bs: Binders): Int = doHash(bs, tref)
28842878

@@ -4498,7 +4492,7 @@ object Types {
44984492
*/
44994493
abstract case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef, CaptureRef {
45004494
type BT = TermLambda
4501-
override def canBeTracked(using Context) = true
4495+
def canBeTracked(using Context) = true
45024496
def kindString: String = "Term"
45034497
def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum)
45044498
}

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ object Parsers {
893893
def followingIsCaptureSet(): Boolean =
894894
val lookahead = in.LookaheadScanner()
895895
def recur(): Boolean =
896-
lookahead.isIdent && {
896+
(lookahead.isIdent || lookahead.token == THIS) && {
897897
lookahead.nextToken()
898898
if lookahead.token == COMMA then
899899
lookahead.nextToken()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:35:29 -------------------------------------
2+
35 | val ref1c: LazyList[Int] = ref1 // error
3+
| ^^^^
4+
| Found: (ref1 : {cap1} lazylists.LazyCons[Int]{xs: {cap1} () => {*} lazylists.LazyList[Int]})
5+
| Required: lazylists.LazyList[Int]
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:37:36 -------------------------------------
9+
37 | val ref2c: {ref1} LazyList[Int] = ref2 // error
10+
| ^^^^
11+
| Found: (ref2 : {cap2, ref1} lazylists.LazyList[Int])
12+
| Required: {ref1} lazylists.LazyList[Int]
13+
14+
longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:39:36 -------------------------------------
16+
39 | val ref3c: {cap2} LazyList[Int] = ref3 // error
17+
| ^^^^
18+
| Found: (ref3 : {cap2, ref1} lazylists.LazyList[Int])
19+
| Required: {cap2} lazylists.LazyList[Int]
20+
21+
longer explanation available when compiling with `-explain`
22+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:41:48 -------------------------------------
23+
41 | val ref4c: {cap1, ref3, cap3} LazyList[Int] = ref4 // error
24+
| ^^^^
25+
| Found: (ref4 : {cap3, cap2, ref1, cap1} lazylists.LazyList[Int])
26+
| Required: {cap1, ref3, cap3} lazylists.LazyList[Int]
27+
28+
longer explanation available when compiling with `-explain`
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package lazylists
2+
3+
abstract class LazyList[+T]:
4+
this: ({*} LazyList[T]) =>
5+
6+
def isEmpty: Boolean
7+
def head: T
8+
def tail: LazyList[T]
9+
10+
def map[U](f: {*} T => U): {f, this} LazyList[U] =
11+
if isEmpty then LazyNil
12+
else LazyCons(f(head), () => tail.map(f))
13+
14+
class LazyCons[+T](val x: T, val xs: {*} () => {*} LazyList[T]) extends LazyList[T]:
15+
def isEmpty = false
16+
def head = x
17+
def tail = xs()
18+
19+
object LazyNil extends LazyList[Nothing]:
20+
def isEmpty = true
21+
def head = ???
22+
def tail = ???
23+
24+
def map[A, B](xs: {*} LazyList[A], f: {*} A => B): {f, xs} LazyList[B] =
25+
xs.map(f)
26+
27+
class CC
28+
type Cap = {*} CC
29+
30+
def test(cap1: Cap, cap2: Cap, cap3: Cap) =
31+
def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil
32+
def g(x: Int) = if cap2 == cap2 then x else 0
33+
def h(x: Int) = if cap3 == cap3 then x else 0
34+
val ref1 = LazyCons(1, () => f(LazyNil))
35+
val ref1c: LazyList[Int] = ref1 // error
36+
val ref2 = map(ref1, g)
37+
val ref2c: {ref1} LazyList[Int] = ref2 // error
38+
val ref3 = ref1.map(g)
39+
val ref3c: {cap2} LazyList[Int] = ref3 // error
40+
val ref4 = (if cap1 == cap2 then ref1 else ref2).map(h)
41+
val ref4c: {cap1, ref3, cap3} LazyList[Int] = ref4 // error
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:19:28 --------------------------------------
2+
19 | val ref1c: LazyRef[Int] = ref1 // error
3+
| ^^^^
4+
| Found: (ref1 : {cap1} LazyRef[Int]{elem: {cap1} () => Int})
5+
| Required: LazyRef[Int]
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:21:35 --------------------------------------
9+
21 | val ref2c: {cap2} LazyRef[Int] = ref2 // error
10+
| ^^^^
11+
| Found: (ref2 : {cap2, ref1} LazyRef[Int]{elem: {*} () => Int})
12+
| Required: {cap2} LazyRef[Int]
13+
14+
longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:23:35 --------------------------------------
16+
23 | val ref3c: {ref1} LazyRef[Int] = ref3 // error
17+
| ^^^^
18+
| Found: (ref3 : {cap2, ref1} LazyRef[Int]{elem: {*} () => Int})
19+
| Required: {ref1} LazyRef[Int]
20+
21+
longer explanation available when compiling with `-explain`
22+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:25:35 --------------------------------------
23+
25 | val ref4c: {cap1} LazyRef[Int] = ref4 // error
24+
| ^^^^
25+
| Found: (ref4 : {cap2, cap1} LazyRef[Int]{elem: {*} () => Int})
26+
| Required: {cap1} LazyRef[Int]
27+
28+
longer explanation available when compiling with `-explain`
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class CC
2+
type Cap = {*} CC
3+
4+
class LazyRef[T](val elem: {*} () => T):
5+
val get = elem
6+
def map[U](f: {*} T => U): {f, this} LazyRef[U] =
7+
new LazyRef(() => f(elem()))
8+
9+
def map[A, B](ref: {*} LazyRef[A], f: {*} A => B): {f, ref} LazyRef[B] =
10+
new LazyRef(() => f(ref.elem()))
11+
12+
def mapc[A, B]: (ref: {*} LazyRef[A], f: {*} A => B) => {f, ref} LazyRef[B] =
13+
(ref1, f1) => map[A, B](ref1, f1)
14+
15+
def test(cap1: Cap, cap2: Cap) =
16+
def f(x: Int) = if cap1 == cap1 then x else 0
17+
def g(x: Int) = if cap2 == cap2 then x else 0
18+
val ref1 = LazyRef(() => f(0))
19+
val ref1c: LazyRef[Int] = ref1 // error
20+
val ref2 = map(ref1, g)
21+
val ref2c: {cap2} LazyRef[Int] = ref2 // error
22+
val ref3 = ref1.map(g)
23+
val ref3c: {ref1} LazyRef[Int] = ref3 // error
24+
val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g)
25+
val ref4c: {cap1} LazyRef[Int] = ref4 // error
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package lazylists
2+
3+
abstract class LazyList[+T]:
4+
this: ({*} LazyList[T]) =>
5+
6+
def isEmpty: Boolean
7+
def head: T
8+
def tail: LazyList[T]
9+
10+
def map[U](f: {*} T => U): {f, this} LazyList[U] =
11+
if isEmpty then LazyNil
12+
else LazyCons(f(head), () => tail.map(f))
13+
14+
class LazyCons[+T](val x: T, val xs: {*} () => {*} LazyList[T]) extends LazyList[T]:
15+
def isEmpty = false
16+
def head = x
17+
def tail = xs()
18+
19+
object LazyNil extends LazyList[Nothing]:
20+
def isEmpty = true
21+
def head = ???
22+
def tail = ???
23+
24+
def map[A, B](xs: {*} LazyList[A], f: {*} A => B): {f, xs} LazyList[B] =
25+
xs.map(f)
26+
27+
class CC
28+
type Cap = {*} CC
29+
30+
def test(cap1: Cap, cap2: Cap, cap3: Cap) =
31+
def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil
32+
def g(x: Int) = if cap2 == cap2 then x else 0
33+
def h(x: Int) = if cap3 == cap3 then x else 0
34+
val ref1 = LazyCons(1, () => f(LazyNil))
35+
val ref1c: {cap1} LazyList[Int] = ref1
36+
val ref2 = map(ref1, g)
37+
val ref2c: {cap2, ref1} LazyList[Int] = ref2
38+
val ref3 = ref1.map(g)
39+
val ref3c: {cap2, ref1} LazyList[Int] = ref3
40+
val ref4 = (if cap1 == cap2 then ref1 else ref2).map(h)
41+
val ref4c: {cap1, cap2, cap3} LazyList[Int] = ref4

tests/pos-custom-args/captures/lazyref.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,23 @@ type Cap = {*} CC
33

44
class LazyRef[T](val elem: {*} () => T):
55
val get = elem
6+
def map[U](f: {*} T => U): {f, this} LazyRef[U] =
7+
new LazyRef(() => f(elem()))
68

79
def map[A, B](ref: {*} LazyRef[A], f: {*} A => B): {f, ref} LazyRef[B] =
810
new LazyRef(() => f(ref.elem()))
911

1012
def mapc[A, B]: (ref: {*} LazyRef[A], f: {*} A => B) => {f, ref} LazyRef[B] =
1113
(ref1, f1) => map[A, B](ref1, f1)
14+
15+
def test(cap1: Cap, cap2: Cap) =
16+
def f(x: Int) = if cap1 == cap1 then x else 0
17+
def g(x: Int) = if cap2 == cap2 then x else 0
18+
val ref1 = LazyRef(() => f(0))
19+
val ref1c: {cap1} LazyRef[Int] = ref1
20+
val ref2 = map(ref1, g)
21+
val ref2c: {cap2, ref1} LazyRef[Int] = ref2
22+
val ref3 = ref1.map(g)
23+
val ref3c: {cap2, ref1} LazyRef[Int] = ref3
24+
val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g)
25+
val ref4c: {cap1, cap2} LazyRef[Int] = ref4

0 commit comments

Comments
 (0)