Skip to content

Commit 1391da7

Browse files
committed
Drop @unbox
Convert remaining occurrences in tests to @use
1 parent 1746091 commit 1391da7

File tree

15 files changed

+44
-126
lines changed

15 files changed

+44
-126
lines changed

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

Lines changed: 23 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -402,15 +402,16 @@ class CheckCaptures extends Recheck, SymTransformer:
402402
&& (!ccConfig.useSealed || refSym.is(Param))
403403
&& refOwner == env.owner
404404
then
405-
if refSym.hasAnnotation(defn.UnboxAnnot)
406-
|| ref.info.hasAnnotation(defn.UseAnnot)
407-
|| c.isUnderUse
408-
then
405+
if ref.info.hasAnnotation(defn.UseAnnot)|| c.isUnderUse then
409406
capt.println(i"exempt: $ref in $refOwner")
410407
else
411408
// Reach capabilities that go out of scope have to be approximated
412409
// by their underlying capture set, which cannot be universal.
413-
// Reach capabilities of @unboxed parameters are exempted.
410+
// Reach capabilities are exempted if
411+
// - they are under-use capabilties that were generated from a cap
412+
// appearing under a @use, or
413+
// - their reference has a type thar carries a @use annotation. In that
414+
// case callers will charge the deep capture set of the argument.
414415
val cs = CaptureSet.ofInfo(c)
415416
cs.disallowRootCapability: () =>
416417
def kind = if c.isReach then "reach capability" else "capture set variable"
@@ -428,48 +429,14 @@ class CheckCaptures extends Recheck, SymTransformer:
428429
end markFree
429430

430431
/** Include references captured by the called method in the current environment stack */
431-
def includeCallCaptures(sym: Symbol, pos: SrcPos)(using Context): Unit =
432-
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
433-
434-
private val prefixCalls = util.EqHashSet[GenericApply]()
435-
private val unboxedArgs = util.EqHashSet[Tree]()
436-
437-
def handleCall(meth: Symbol, call: GenericApply, eval: () => Type)(using Context): Type =
438-
if prefixCalls.remove(call) then return eval()
439-
440-
val unboxedParamNames =
441-
meth.rawParamss.flatMap: params =>
442-
params.collect:
443-
case param if param.hasAnnotation(defn.UnboxAnnot) =>
444-
param.name
445-
.toSet
446-
447-
def markUnboxedArgs(call: GenericApply): Unit = call.fun.tpe.widen match
448-
case MethodType(pnames) =>
449-
for (pname, arg) <- pnames.lazyZip(call.args) do
450-
if unboxedParamNames.contains(pname) then
451-
unboxedArgs.add(arg)
452-
case _ =>
453-
454-
def markPrefixCalls(tree: Tree): Unit = tree match
455-
case tree: GenericApply =>
456-
prefixCalls.add(tree)
457-
markUnboxedArgs(tree)
458-
markPrefixCalls(tree.fun)
459-
case _ =>
460-
461-
markUnboxedArgs(call)
462-
markPrefixCalls(call.fun)
463-
val res = eval()
464-
includeCallCaptures(meth, call.srcPos)
465-
res
466-
end handleCall
432+
def includeCallCaptures(sym: Symbol, resType: Type, pos: SrcPos)(using Context): Unit = resType match
433+
case _: MethodOrPoly => // wait until method is fully applied
434+
case _ =>
435+
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
467436

468437
override def recheckIdent(tree: Ident, pt: Type)(using Context): Type =
469438
if tree.symbol.is(Method) then
470-
if tree.symbol.info.isParameterless then
471-
// there won't be an apply; need to include call captures now
472-
includeCallCaptures(tree.symbol, tree.srcPos)
439+
includeCallCaptures(tree.symbol, tree.symbol.info, tree.srcPos)
473440
else if !tree.symbol.isStatic then
474441
//debugShowEnvs()
475442
def addSelects(ref: TermRef, pt: Type): TermRef = pt match
@@ -574,16 +541,15 @@ class CheckCaptures extends Recheck, SymTransformer:
574541
tp.derivedCapturingType(forceBox(parent), refs)
575542
mapArgUsing(forceBox)
576543
else
577-
handleCall(meth, tree, () => super.recheckApply(tree, pt))
544+
val res = super.recheckApply(tree, pt)
545+
includeCallCaptures(meth, res, tree.srcPos)
546+
res
578547
end recheckApply
579548

580549
protected override
581550
def recheckArg(arg: Tree, formal: Type)(using Context): Type =
582551
val argType = recheck(arg, formal)
583552
accountForUses(arg, argType, formal)
584-
if unboxedArgs.contains(arg) then
585-
capt.println(i"charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}")
586-
markFree(argType.deepCaptureSet, arg.srcPos)
587553
argType
588554

589555
class MapUses(deep: Boolean)(using Context) extends TypeMap:
@@ -620,27 +586,18 @@ class CheckCaptures extends Recheck, SymTransformer:
620586
* ---------------------
621587
* E |- f(a): Tr^C
622588
*
623-
* If the function `f` does not have an `@unboxed` parameter, then
624-
* any unboxing it does would be charged to the environment of the function
625-
* so they have to appear in Cq. Since any capabilities of the result of the
626-
* application must already be present in the application, an upper
627-
* approximation of the result capture set is Cq \union Ca, where `Ca`
628-
* is the capture set of the argument.
629-
* If the function `f` does have an `@unboxed` parameter, then it could in addition
630-
* unbox reach capabilities over its formal parameter. Therefore, the approximation
631-
* would be `Cq \union dcs(Ca)` instead.
589+
* If the type of the function `f` does not mention any formal parameters
590+
* any capabilities of the result of the application must already be present in
591+
* the application. So an upper approximation of the result capture set is Cq \union Ca,
592+
* where `Ca` is the capture set of the argument.
632593
* If the approximation is known to subcapture the declared result Cr, we pick it for C
633594
* otherwise we pick Cr.
634595
*/
635596
protected override
636597
def recheckApplication(tree: Apply, qualType: Type, funType: MethodType, argTypes: List[Type])(using Context): Type =
637598
val appType = Existential.toCap(super.recheckApplication(tree, qualType, funType, argTypes))
638599
val qualCaptures = qualType.captureSet
639-
val argCaptures =
640-
for (arg, argType) <- tree.args.lazyZip(argTypes) yield
641-
if unboxedArgs.remove(arg) // need to ensure the remove happens, that's why argCaptures is computed even if not needed.
642-
then argType.deepCaptureSet
643-
else argType.captureSet
600+
val argCaptures = argTypes.map(_.captureSet)
644601
appType match
645602
case appType @ CapturingType(appType1, refs)
646603
if qualType.exists
@@ -735,8 +692,10 @@ class CheckCaptures extends Recheck, SymTransformer:
735692
i"Sealed type variable $pname", "be instantiated to",
736693
i"This is often caused by a local capability$where\nleaking as part of its result.",
737694
tree.srcPos)
738-
try handleCall(meth, tree, () => Existential.toCap(super.recheckTypeApply(tree, pt)))
739-
finally checkContains(tree)
695+
val res = Existential.toCap(super.recheckTypeApply(tree, pt))
696+
includeCallCaptures(meth, res, tree.srcPos)
697+
checkContains(tree)
698+
res
740699
end recheckTypeApply
741700

742701
/** Faced with a tree of form `caps.contansImpl[CS, r.type]`, check that `R` is a tracked
@@ -1423,21 +1382,6 @@ class CheckCaptures extends Recheck, SymTransformer:
14231382
!setup.isPreCC(overriding) && !setup.isPreCC(overridden)
14241383

14251384
override def checkInheritedTraitParameters: Boolean = false
1426-
1427-
/** Check that overrides don't change the @unbox status of their parameters */
1428-
override def additionalChecks(member: Symbol, other: Symbol)(using Context): Unit =
1429-
for
1430-
(params1, params2) <- member.rawParamss.lazyZip(other.rawParamss)
1431-
(param1, param2) <- params1.lazyZip(params2)
1432-
do
1433-
if param1.hasAnnotation(defn.UnboxAnnot) != param2.hasAnnotation(defn.UnboxAnnot) then
1434-
report.error(
1435-
OverrideError(
1436-
i"has a parameter ${param1.name} with different @unbox status than the corresponding parameter in the overridden definition",
1437-
self, member, other, self.memberInfo(member), self.memberInfo(other)
1438-
),
1439-
if member.owner == clazz then member.srcPos else clazz.srcPos
1440-
)
14411385
end OverridingPairsCheckerCC
14421386

14431387
def traverse(t: Tree)(using Context) =

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ class Definitions {
552552
ScalaPackageClass, tpnme.maybeCapability, Final, List(StaticAnnotationClass.typeRef)))
553553

554554
/** A type `type <use>[+T] <: T` used locally in capture checking. At certain points
555-
* `T @use` types are converted to `<use>[T]` types. These types are handled as
555+
* `T @use` types are converted to `<use>[T]` types. These types are handled as
556556
* compile-time applied types by TypeComparer.
557557
*/
558558
@tu lazy val UseType: TypeSymbol =
@@ -1068,7 +1068,6 @@ class Definitions {
10681068
@tu lazy val ExperimentalAnnot: ClassSymbol = requiredClass("scala.annotation.experimental")
10691069
@tu lazy val ThrowsAnnot: ClassSymbol = requiredClass("scala.throws")
10701070
@tu lazy val TransientAnnot: ClassSymbol = requiredClass("scala.transient")
1071-
@tu lazy val UnboxAnnot: ClassSymbol = requiredClass("scala.caps.unbox")
10721071
@tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked")
10731072
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
10741073
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1796,7 +1796,7 @@ object Types extends TypeUtils {
17961796

17971797
/** Is this either not a method at all, or a parameterless method? */
17981798
final def isParameterless(using Context): Boolean = stripPoly match {
1799-
case mt: MethodType => false
1799+
case mt: MethodOrPoly => false
18001800
case _ => true
18011801
}
18021802

library/src/scala/caps.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ import annotation.{experimental, compileTimeOnly, retainsCap}
5555
/** This should go into annotations. For now it is here, so that we
5656
* can experiment with it quickly between minor releases
5757
*/
58-
final class unbox extends annotation.StaticAnnotation
5958
final class use extends annotation.StaticAnnotation
6059

6160
object unsafe:

tests/neg-custom-args/captures/spread-problem.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import language.experimental.captureChecking
22

33
trait Source[+T]
44

5-
def race[T](@caps.unbox sources: (Source[T]^)*): Source[T]^{sources*} = ???
5+
def race[T](sources: (Source[T]^ @caps.use)*): Source[T]^{sources*} = ???
66

77
def raceTwo[T](src1: Source[T]^, src2: Source[T]^): Source[T]^{} =
88
race(Seq(src1, src2)*) // error

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ trait Buffer[A]:
88
def flatMapInPlace(f: A => IterableOnce[A]^): this.type = {
99
val g = f
1010
val s = 10
11-
// capture checking: we need the copy since we box/unbox on g* on the next line
11+
// capture checking: we need the copy since we box/unbox on g* on the next line
1212
// TODO: This looks fishy, need to investigate
13-
// Alternative would be to mark `f` as @unbox. It's not inferred
14-
// since `^ appears in a function result, not under a box.
1513
val newElems = new Array[(IterableOnce[A]^{f})](s)
1614
var i = 0
1715
while i < s do

tests/pos-custom-args/captures/dep-reach.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import caps.unbox
1+
import caps.use
22
object Test:
33
class C
44
type Proc = () => Unit
55

66
def f(c: C^, d: C^): () ->{c, d} Unit =
7-
def foo(@unbox xs: Proc*): () ->{xs*} Unit =
7+
def foo(xs: Proc @use *): () ->{xs*} Unit =
88
xs.head
99
val a: () ->{c} Unit = () => ()
1010
val b: () ->{d} Unit = () => ()
@@ -13,7 +13,7 @@ object Test:
1313

1414
def g(c: C^, d: C^): () ->{c, d} Unit =
1515

16-
def foo(@unbox xs: Seq[() => Unit]): () ->{xs*} Unit =
16+
def foo(xs: Seq[(() => Unit) @use]): () ->{xs*} Unit =
1717
xs.head
1818

1919
val a: () ->{c} Unit = () => ()

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import caps.unbox
1+
import caps.use
22

33
class C
44
def f(xs: List[C^]) =
@@ -22,7 +22,7 @@ extension [A](x: A) def :: (xs: List[A]): List[A] = ???
2222

2323
object Nil extends List[Nothing]
2424

25-
def runAll(@unbox xs: List[Proc]): Unit =
25+
def runAll(xs: List[Proc] @use): Unit =
2626
var cur: List[() ->{xs*} Unit] = xs // OK, by revised VAR
2727
while cur.nonEmpty do
2828
val next: () ->{xs*} Unit = cur.head

tests/pos/Buffer.scala

Lines changed: 0 additions & 22 deletions
This file was deleted.

tests/pos/cc-poly-source-capability.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import language.experimental.captureChecking
22
import annotation.experimental
33
import caps.{CapSet, Capability}
4-
import caps.unbox
4+
import caps.use
55

66
@experimental object Test:
77

@@ -18,7 +18,7 @@ import caps.unbox
1818

1919
def allListeners: Set[Listener^{X^}] = listeners
2020

21-
def test1(async1: Async, @unbox others: List[Async]) =
21+
def test1(async1: Async, others: List[Async @use]) =
2222
val src = Source[CapSet^{async1, others*}]
2323
val lst1 = listener(async1)
2424
val lsts = others.map(listener)

0 commit comments

Comments
 (0)