Skip to content

Commit a158840

Browse files
committed
Polishing some cc classes
1 parent c091c33 commit a158840

File tree

5 files changed

+59
-27
lines changed

5 files changed

+59
-27
lines changed

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

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,23 @@ import config.Printers.capt
1111
import printing.Printer
1212
import printing.Texts.Text
1313

14-
14+
/** An annotation representing a capture set and whether it is boxed.
15+
* It simulates a normal @retains annotation except that it is more efficient,
16+
* supports variables as capture sets, and adds a `boxed` flag.
17+
* These annotations are created during capture checking. Before that
18+
* there are only regular @retains and @retainsByName annotations.
19+
* @param refs the capture set
20+
* @param boxed whether the type carrying the annotation is boxed
21+
* @param cls the underlying class (either annotation.retains or annotation.retainsByName)
22+
*/
1523
case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) extends Annotation:
1624
import CaptureAnnotation.*
1725
import tpd.*
1826

27+
/** A cache for boxed version of a capturing type with this annotation */
1928
val boxedType = BoxedTypeCache()
2029

30+
/** Reconstitute annotation tree from capture set */
2131
override def tree(using Context) =
2232
val elems = refs.elems.toList.map {
2333
case cr: TermRef => ref(cr)
@@ -30,8 +40,7 @@ case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) exte
3040
override def symbol(using Context) = cls
3141

3242
override def derivedAnnotation(tree: Tree)(using Context): Annotation =
33-
if refs == CaptureSet.universal then this
34-
else unsupported("derivedAnnotation(Tree)")
43+
unsupported(i"derivedAnnotation(Tree), $tree, $refs")
3544

3645
def derivedAnnotation(refs: CaptureSet, boxed: Boolean)(using Context): Annotation =
3746
if (this.refs eq refs) && (this.boxed == boxed) then this
@@ -42,9 +51,9 @@ case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) exte
4251
this.refs == refs && this.boxed == boxed && this.symbol == that.symbol
4352
case _ => false
4453

45-
override def mapWith(tp: TypeMap)(using Context) =
54+
override def mapWith(tm: TypeMap)(using Context) =
4655
val elems = refs.elems.toList
47-
val elems1 = elems.mapConserve(tp)
56+
val elems1 = elems.mapConserve(tm)
4857
if elems1 eq elems then this
4958
else if elems1.forall(_.isInstanceOf[CaptureRef])
5059
then derivedAnnotation(CaptureSet(elems1.asInstanceOf[List[CaptureRef]]*), boxed)
@@ -66,16 +75,3 @@ case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) exte
6675
case _ => false
6776

6877
end CaptureAnnotation
69-
70-
/** A one-element cache for the boxed version of an unboxed capturing type */
71-
class BoxedTypeCache:
72-
private var boxed: Type = compiletime.uninitialized
73-
private var unboxed: Type = NoType
74-
75-
def apply(tp: AnnotatedType)(using Context): Type =
76-
if tp ne unboxed then
77-
unboxed = tp
78-
val CapturingType(parent, refs) = tp: @unchecked
79-
boxed = CapturingType(parent, refs, boxed = true)
80-
boxed
81-
end BoxedTypeCache

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,24 @@ import tpd.*
1313
private val Captures: Key[CaptureSet] = Key()
1414
private val BoxedType: Key[BoxedTypeCache] = Key()
1515

16-
def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
16+
/** The arguments of a @retains or @retainsByName annotation */
17+
private[cc] def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
1718
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
1819
case _ => Nil
1920

21+
/** An exception thrown if a @retains argument is not syntactically a CaptureRef */
2022
class IllegalCaptureRef(tpe: Type) extends Exception
2123

2224
extension (tree: Tree)
2325

26+
/** Map tree with CaptureRef type to its type, throw IllegalCaptureRef otherwise */
2427
def toCaptureRef(using Context): CaptureRef = tree.tpe match
2528
case ref: CaptureRef => ref
2629
case tpe => throw IllegalCaptureRef(tpe)
2730

31+
/** Convert a @retains or @retainsByName annotation tree to the capture set it represents.
32+
* For efficience, the result is cached as an Attachment on the tree.
33+
*/
2834
def toCaptureSet(using Context): CaptureSet =
2935
tree.getAttachment(Captures) match
3036
case Some(refs) => refs
@@ -36,11 +42,16 @@ extension (tree: Tree)
3642

3743
extension (tp: Type)
3844

45+
/** @pre `tp` is a CapturingType */
3946
def derivedCapturingType(parent: Type, refs: CaptureSet)(using Context): Type = tp match
4047
case tp @ CapturingType(p, r) =>
4148
if (parent eq p) && (refs eq r) then tp
4249
else CapturingType(parent, refs, tp.isBoxed)
4350

51+
/** If this is a unboxed capturing type with nonempty capture set, its boxed version.
52+
* Or, if type is a TypeBounds of capturing types, the version where the bounds are boxed.
53+
* The identity for all other types.
54+
*/
4455
def boxed(using Context): Type = tp.dealias match
4556
case tp @ CapturingType(parent, refs) if !tp.isBoxed && !refs.isAlwaysEmpty =>
4657
tp.annot match
@@ -56,12 +67,16 @@ extension (tp: Type)
5667
case _ =>
5768
tp
5869

70+
/** The boxed version of `tp`, unless `tycon` is a function symbol */
5971
def boxedUnlessFun(tycon: Type)(using Context) =
60-
if ctx.phase != Phases.checkCapturesPhase || defn.isFunctionClass(tycon.typeSymbol)
72+
if ctx.phase != Phases.checkCapturesPhase || defn.isFunctionSymbol(tycon.typeSymbol)
6173
then tp
6274
else tp.boxed
6375

64-
/** The boxed capture set of a type */
76+
/** The capture set of `tp` consisting of all top-level captures under a box.
77+
* Unlike for `boxed` this also considers parents of capture types, unions and
78+
* intersections, and type proxies other than abstract types.
79+
*/
6580
def boxedCaptureSet(using Context): CaptureSet =
6681
def getBoxed(tp: Type): CaptureSet = tp match
6782
case tp @ CapturingType(parent, refs) =>

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,41 @@ package cc
55
import core.*
66
import Types.*, Symbols.*, Contexts.*
77

8-
/** A capturing type. This is internally represented as an annotated type with a `retains`
9-
* annotation, but the extractor will succeed only at phase CheckCaptures.
8+
/** A (possibly boxed) capturing type. This is internally represented as an annotated type with a @retains
9+
* or @retainsByName annotation, but the extractor will succeed only at phase CheckCaptures.
10+
* That way, we can ignore caturing information until phase CheckCaptures since it is
11+
* wrapped in a plain annotation.
12+
*
13+
* The same trick does not work for the boxing information. Boxing is context dependent, so
14+
* we have to add that information in the Setup step preceding CheckCaptures. Boxes are
15+
* added for all type arguments of methods. For type arguments of applied types a different
16+
* strategy is used where we box arguments of applied types that are not functions when
17+
* accessing the argument.
18+
*
19+
* An alternative strategy would add boxes also to arguments of applied types during setup.
20+
* But this would have to be done for all possibly accessibly types from the compiled units
21+
* as well as their dependencies. It's difficult to do this in a DenotationTransformer without
22+
* accidentally forcing symbol infos. That's why this alternative was not implemented.
23+
* If we would go back on this it would make sense to also treat captuyring types different
24+
* from annotations and to generate them all during Setup and in DenotationTransformers.
1025
*/
1126
object CapturingType:
1227

28+
/** Smart constructor that drops empty capture sets and fuses compatible capturiong types.
29+
* An outer type capturing type A can be fused with an inner capturing type B if their
30+
* boxing status is the same or if A is boxed.
31+
*/
1332
def apply(parent: Type, refs: CaptureSet, boxed: Boolean = false)(using Context): Type =
1433
if refs.isAlwaysEmpty then parent
1534
else parent match
1635
case parent @ CapturingType(parent1, refs1) if boxed || !parent.isBoxed =>
17-
// Fuse types except if nested type is boxed and current one isn't.
1836
apply(parent1, refs ++ refs1, boxed)
1937
case _ =>
2038
AnnotatedType(parent, CaptureAnnotation(refs, boxed)(defn.RetainsAnnot))
2139

40+
/** An extractor that succeeds only during CheckCapturingPhase. Boxing statis is
41+
* returned separately by CaptureOps.isBoxed.
42+
*/
2243
def unapply(tp: AnnotatedType)(using Context): Option[(Type, CaptureSet)] =
2344
if ctx.phase == Phases.checkCapturesPhase && tp.annot.symbol == defn.RetainsAnnot then
2445
EventuallyCapturingType.unapply(tp)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import config.Printers.{core, typr, matchTypes}
3636
import reporting.{trace, Message}
3737
import java.lang.ref.WeakReference
3838
import compiletime.uninitialized
39-
import cc.{CapturingType, CaptureSet, derivedCapturingType, retainedElems, isBoxedCapturing, EventuallyCapturingType, boxedUnlessFun}
39+
import cc.{CapturingType, CaptureSet, derivedCapturingType, isBoxedCapturing, EventuallyCapturingType, boxedUnlessFun}
4040
import CaptureSet.{CompareResult, IdempotentCaptRefMap, IdentityCaptRefMap}
4141

4242
import scala.annotation.internal.sharable

tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ val experimentalDefinitionInLibrary = Set(
5050
"scala.annotation.capability",
5151
"scala.annotation.internal.CaptureChecked",
5252
"scala.annotation.internal.requiresCapability",
53-
"scala.retains",
54-
"scala.retainsByName",
53+
"scala.annotation.retains",
54+
"scala.annotation.retainsByName",
5555

5656
//// New APIs: Mirror
5757
// Can be stabilized in 3.3.0 or later.

0 commit comments

Comments
 (0)