Skip to content

Commit d071bf2

Browse files
committed
Simplify healTypeParam
1 parent af631d5 commit d071bf2

File tree

2 files changed

+33
-40
lines changed

2 files changed

+33
-40
lines changed

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

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,9 +1032,9 @@ class CheckCaptures extends Recheck, SymTransformer:
10321032
* that this type parameter can't see.
10331033
* For example, when capture checking the following expression:
10341034
*
1035-
* def usingLogFile[T](op: (f: {cap} File) => T): T = ...
1035+
* def usingLogFile[T](op: File^ => T): T = ...
10361036
*
1037-
* usingLogFile[box ?1 () -> Unit] { (f: {cap} File) => () => { f.write(0) } }
1037+
* usingLogFile[box ?1 () -> Unit] { (f: File^) => () => { f.write(0) } }
10381038
*
10391039
* We may propagate `f` into ?1, making ?1 ill-formed.
10401040
* This also causes soundness issues, since `f` in ?1 should be widened to `cap`,
@@ -1046,34 +1046,26 @@ class CheckCaptures extends Recheck, SymTransformer:
10461046
*/
10471047
private def healTypeParam(tree: Tree)(using Context): Unit =
10481048
val checker = new TypeTraverser:
1049+
private var allowed: SimpleIdentitySet[TermParamRef] = SimpleIdentitySet.empty
1050+
10491051
private def isAllowed(ref: CaptureRef): Boolean = ref match
10501052
case ref: TermParamRef => allowed.contains(ref)
10511053
case _ => true
10521054

1053-
// Widen the given term parameter refs x₁ : C₁ S₁ , ⋯ , xₙ : Cₙ Sₙ to their capture sets C₁ , ⋯ , Cₙ.
1054-
//
1055-
// If in these capture sets there are any capture references that are term parameter references we should avoid,
1056-
// we will widen them recursively.
1057-
private def widenParamRefs(refs: List[TermParamRef]): List[CaptureSet] =
1058-
@scala.annotation.tailrec
1059-
def recur(todos: List[TermParamRef], acc: List[CaptureSet]): List[CaptureSet] =
1060-
todos match
1061-
case Nil => acc
1062-
case ref :: rem =>
1063-
val cs = ref.captureSetOfInfo
1064-
val nextAcc = cs.filter(isAllowed(_)) :: acc
1065-
val nextRem: List[TermParamRef] = (cs.elems.toList.filter(!isAllowed(_)) ++ rem).asInstanceOf
1066-
recur(nextRem, nextAcc)
1067-
recur(refs, Nil)
1068-
10691055
private def healCaptureSet(cs: CaptureSet): Unit =
1070-
def avoidance(elems: List[CaptureRef])(using Context): Unit =
1071-
val toInclude = widenParamRefs(elems.filter(!isAllowed(_)).asInstanceOf)
1072-
//println(i"HEAL $cs by widening to $toInclude")
1073-
toInclude.foreach(checkSubset(_, cs, tree.srcPos))
1074-
cs.ensureWellformed(avoidance)
1075-
1076-
private var allowed: SimpleIdentitySet[TermParamRef] = SimpleIdentitySet.empty
1056+
cs.ensureWellformed: elems =>
1057+
ctx ?=>
1058+
var seen = new util.HashSet[CaptureRef]
1059+
def recur(elems: List[CaptureRef]): Unit =
1060+
for ref <- elems do
1061+
if !isAllowed(ref) && !seen.contains(ref) then
1062+
seen += ref
1063+
val widened = ref.captureSetOfInfo
1064+
val added = widened.filter(isAllowed(_))
1065+
capt.println(i"heal $ref in $cs by widening to $added")
1066+
checkSubset(added, cs, tree.srcPos)
1067+
recur(widened.elems.toList)
1068+
recur(elems)
10771069

10781070
def traverse(tp: Type) =
10791071
tp match

tests/neg-custom-args/captures/heal-tparam-cs.scala

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,33 @@ import language.experimental.captureChecking
22

33
trait Cap { def use(): Unit }
44

5-
def localCap[sealed T](op: (cap: Cap^{cap}) => T): T = ???
5+
def localCap[sealed T](op: (c: Cap^{cap}) => T): T = ???
66

77
def main(io: Cap^{cap}, net: Cap^{cap}): Unit = {
8-
val test1 = localCap { cap => // error
9-
() => { cap.use() }
8+
9+
val test1 = localCap { c => // error
10+
() => { c.use() }
1011
}
1112

12-
val test2: (cap: Cap^{cap}) -> () ->{cap} Unit =
13-
localCap { cap => // should work
14-
(cap1: Cap^{cap}) => () => { cap1.use() }
13+
val test2: (c: Cap^{cap}) -> () ->{cap} Unit =
14+
localCap { c => // should work
15+
(c1: Cap^{cap}) => () => { c1.use() }
1516
}
1617

17-
val test3: (cap: Cap^{io}) -> () ->{io} Unit =
18-
localCap { cap => // should work
19-
(cap1: Cap^{io}) => () => { cap1.use() }
18+
val test3: (c: Cap^{io}) -> () ->{io} Unit =
19+
localCap { c => // should work
20+
(c1: Cap^{io}) => () => { c1.use() }
2021
}
2122

22-
val test4: (cap: Cap^{io}) -> () ->{net} Unit =
23-
localCap { cap => // error
24-
(cap1: Cap^{io}) => () => { cap1.use() }
23+
val test4: (c: Cap^{io}) -> () ->{net} Unit =
24+
localCap { c => // error
25+
(c1: Cap^{io}) => () => { c1.use() }
2526
}
2627

27-
def localCap2[sealed T](op: (cap: Cap^{io}) => T): T = ???
28+
def localCap2[sealed T](op: (c: Cap^{io}) => T): T = ???
2829

2930
val test5: () ->{io} Unit =
30-
localCap2 { cap => // ok
31-
() => { cap.use() }
31+
localCap2 { c => // ok
32+
() => { c.use() }
3233
}
3334
}

0 commit comments

Comments
 (0)