Skip to content

Commit d8ace30

Browse files
committed
Revert "Fix special capture set handling in recheckApply, Step 2"
This reverts commit f1f5a05. # Conflicts: # compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
1 parent 3667ab8 commit d8ace30

File tree

7 files changed

+20
-44
lines changed

7 files changed

+20
-44
lines changed

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

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -554,37 +554,30 @@ class CheckCaptures extends Recheck, SymTransformer:
554554

555555
/** A specialized implementation of the apply rule.
556556
*
557-
* E |- q: Tq^Cq
558-
* E |- q.f: Ta ->Cf Tr^Cr
559-
* E |- a: Ta
557+
* E |- f: Ra ->Cf Rr^Cr
558+
* E |- a: Ra^Ca
560559
* ---------------------
561-
* E |- f(a): Tr^C
560+
* E |- f a: Rr^C
562561
*
563-
* The implementation picks `C` as `Cq` instead of `Cr`, if
564-
* 1. The argument(s) Ta are always pure
565-
* 2. `Cq` might subcapture `Cr`.
566-
* TODO: We could generalize this as follows:
567-
*
568-
* If the function `f` does not have an `@unboxed` parameter, then
569-
* any unboxing it does would be charged to the environment of the function
570-
* so they have to appear in Cq. So another approximation of the
571-
* result capability set is `Cq u Ca` where `Ca` is the capture set of the
572-
* argument.
573-
* If the function `f` does have an `@unboxed` parameter, then it could in addition
574-
* unbox reach capabilities over its formal parameter. Therefore, the approximation
575-
* would be `Cq u dcs(Ca)` instead.
576-
* If the approximation is known to subcapture the declared result Cr, we pick it.
562+
* The implementation picks as `C` one of `{f, a}` or `Cr`, depending on the
563+
* outcome of a `mightSubcapture` test. It picks `{f, a}` if this might subcapture Cr
564+
* and Cr otherwise.
577565
*/
578566
protected override
579567
def recheckApplication(tree: Apply, qualType: Type, funType: MethodType, argTypes: List[Type])(using Context): Type =
580568
Existential.toCap(super.recheckApplication(tree, qualType, funType, argTypes)) match
581569
case appType @ CapturingType(appType1, refs)
582570
if qualType.exists
583571
&& !tree.fun.symbol.isConstructor
584-
&& argTypes.forall(_.isAlwaysPure)
572+
&& !qualType.isBoxedCapturing // TODO: This is not strng enough, we also have
573+
// to exclude existentials in function results
574+
&& !argTypes.exists(_.isBoxedCapturing)
585575
&& qualType.captureSet.mightSubcapture(refs)
576+
&& argTypes.forall(_.captureSet.mightSubcapture(refs))
586577
=>
587-
appType.derivedCapturingType(appType1, qualType.captureSet)
578+
val callCaptures = tree.args.foldLeft(qualType.captureSet): (cs, arg) =>
579+
cs ++ arg.tpe.captureSet
580+
appType.derivedCapturingType(appType1, callCaptures)
588581
.showing(i"narrow $tree: $appType, refs = $refs, qual-cs = ${qualType.captureSet} = $result", capt)
589582
case appType =>
590583
appType

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.annotation.tailrec
1717
import scala.annotation.unchecked.uncheckedVariance
1818
import scala.runtime.Statics
1919
import language.experimental.captureChecking
20-
import caps.unsafe.unsafeAssumePure
20+
2121

2222
/** Iterators are data structures that allow to iterate over a sequence
2323
* of elements. They have a `hasNext` method for checking
@@ -595,8 +595,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
595595

596596
private[this] def nextCur(): Unit = {
597597
cur = null
598-
cur = f(self.next()).iterator.unsafeAssumePure
599-
// !!! see explanation in colltest5.CollectionStrawManCC5_1.flatMap why the unsafeAssumePure is needed
598+
cur = f(self.next()).iterator
600599
_hasNext = -1
601600
}
602601

scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import scala.runtime.Statics
2525
import language.experimental.captureChecking
2626
import annotation.unchecked.uncheckedCaptures
2727
import caps.untrackedCaptures
28-
import caps.unsafe.unsafeAssumePure
2928

3029
/** This class implements an immutable linked list. We call it "lazy"
3130
* because it computes its elements only when they are needed.
@@ -1042,8 +1041,7 @@ object LazyListIterable extends IterableFactory[LazyListIterable] {
10421041
var itHasNext = false
10431042
var rest = restRef // var rest = restRef.elem
10441043
while (!itHasNext && !rest.isEmpty) {
1045-
it = f(rest.head).iterator.unsafeAssumePure
1046-
// !!! see explanation in colltest5.CollectionStrawManCC5_1.flatMap why the unsafeAssumePure is needed
1044+
it = f(rest.head).iterator
10471045
itHasNext = it.hasNext
10481046
if (!itHasNext) { // wait to advance `rest` because `it.next()` can throw
10491047
rest = rest.tail

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package mutable
1616
import scala.annotation.nowarn
1717
import language.experimental.captureChecking
1818
import caps.unboxed
19-
import caps.unsafe.unsafeAssumePure
2019

2120
/** A `Buffer` is a growable and shrinkable `Seq`. */
2221
trait Buffer[A]
@@ -186,10 +185,7 @@ trait IndexedBuffer[A] extends IndexedSeq[A]
186185
var i = 0
187186
val s = size
188187
val newElems = new Array[(IterableOnce[A]^{f*})](s)
189-
while i < s do
190-
newElems(i) = f(this(i)).unsafeAssumePure
191-
// !!! see explanation in colltest5.CollectionStrawManCC5_1.flatMap why the unsafeAssumePure is needed
192-
i += 1
188+
while (i < s) { newElems(i) = f(this(i)); i += 1 }
193189
clear()
194190
i = 0
195191
while (i < s) { ++=(newElems(i)); i += 1 }

tests/neg-custom-args/captures/reaches.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:64:27 --------------------------------------
4848
64 | val f1: File^{id*} = id(f) // error, since now id(f): File^
4949
| ^^^^^
50-
| Found: File^
50+
| Found: File^{id, f}
5151
| Required: File^{id*}
5252
|
5353
| longer explanation available when compiling with `-explain`

tests/pos-custom-args/captures/nested-classes-2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ def test2(x1: (() => Unit), x2: (() => Unit) => Unit) =
2020
def test3(y1: (() => Unit), y2: (() => Unit) => Unit) =
2121
val cc1: C1^{y1, y2} = C1(y1, y2)
2222
val cc2 = cc1.c2(x1, x2)
23-
val cc3: cc1.C2^{cc2} = cc2
23+
val cc3: cc1.C2^{cc1, x1, x2} = cc2
2424

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Predef.{augmentString as _, wrapString as _, *}
55
import scala.reflect.ClassTag
66
import annotation.unchecked.{uncheckedVariance, uncheckedCaptures}
77
import annotation.tailrec
8-
import caps.unsafe.unsafeAssumePure
98

109
/** A strawman architecture for new collections. It contains some
1110
* example collection classes and methods with the intent to expose
@@ -556,16 +555,7 @@ object CollectionStrawMan5 {
556555
private var myCurrent: Iterator[B]^{this, f} = Iterator.empty
557556
private def current = {
558557
while (!myCurrent.hasNext && self.hasNext)
559-
myCurrent = f(self.next()).iterator.unsafeAssumePure
560-
// !!! This is unsafe since the iterator's result could return a capability
561-
// depending on the argument self.next() of type A. To exclude that we'd have
562-
// to define f to be of type EX c. A ->{c} IterableOnce[B]^{c}, i.e. `c` may
563-
// not depend on A. But to get there we have to
564-
// - improve the way we express existentials using `^`
565-
// - rework the recheckApplication code to cater for this. Right now
566-
// we cannot do anything since `A` is not always pure. But if we took
567-
// the existential scope of the result into account, this could provide
568-
// a solution.
558+
myCurrent = f(self.next()).iterator
569559
myCurrent
570560
}
571561
def hasNext = current.hasNext

0 commit comments

Comments
 (0)