Skip to content

Commit c62f461

Browse files
committed
Support for CapturingTypes
1 parent f6be3f2 commit c62f461

26 files changed

+480
-79
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,16 +261,10 @@ object Trees {
261261
/** Tree's denotation can be derived from its type */
262262
abstract class DenotingTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends Tree[T] {
263263
type ThisTree[-T >: Untyped] <: DenotingTree[T]
264-
override def denot(using Context): Denotation = typeOpt match {
264+
override def denot(using Context): Denotation = typeOpt.stripped match
265265
case tpe: NamedType => tpe.denot
266266
case tpe: ThisType => tpe.cls.denot
267-
case tpe: AnnotatedType => tpe.stripAnnots match {
268-
case tpe: NamedType => tpe.denot
269-
case tpe: ThisType => tpe.cls.denot
270-
case _ => NoDenotation
271-
}
272267
case _ => NoDenotation
273-
}
274268
}
275269

276270
/** Tree's denot/isType/isTerm properties come from a subtree

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ object Printers {
1212

1313
val default = new Printer
1414

15+
val capt = noPrinter
1516
val constr = noPrinter
1617
val core = noPrinter
1718
val checks = noPrinter

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ private sealed trait YSettings:
239239
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects")
240240
val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation")
241241
val Yrecheck: Setting[Boolean] = BooleanSetting("-Yrecheck", "Run type rechecks (test only)")
242+
val Ycc: Setting[Boolean] = BooleanSetting("-Ycc", "Check captured references")
242243

243244
/** Area-specific debug output */
244245
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package dotty.tools
2+
package dotc
3+
package core
4+
5+
import util.*
6+
import Types.*, Symbols.*, Flags.*, Contexts.*, Decorators.*
7+
import config.Printers.capt
8+
import annotation.threadUnsafe
9+
import annotation.internal.sharable
10+
import reporting.trace
11+
import printing.{Showable, Printer}
12+
import printing.Texts.*
13+
14+
case class CaptureSet private (elems: CaptureSet.Refs) extends Showable:
15+
import CaptureSet.*
16+
17+
def isEmpty: Boolean = elems.isEmpty
18+
def nonEmpty: Boolean = !isEmpty
19+
20+
private var myClosure: Refs | Null = null
21+
22+
def closure(using Context): Refs =
23+
if myClosure == null then
24+
var cl = elems
25+
var seen: Refs = SimpleIdentitySet.empty
26+
while
27+
val prev = cl
28+
for ref <- cl do
29+
if !seen.contains(ref) then
30+
seen += ref
31+
cl = cl ++ ref.captureSetOfInfo.elems
32+
prev ne cl
33+
do ()
34+
myClosure = cl
35+
myClosure
36+
37+
def ++ (that: CaptureSet): CaptureSet =
38+
if this.isEmpty then that
39+
else if that.isEmpty then this
40+
else CaptureSet(elems ++ that.elems)
41+
42+
def + (ref: CaptureRef) =
43+
if elems.contains(ref) then this
44+
else CaptureSet(elems + ref)
45+
46+
def intersect (that: CaptureSet): CaptureSet =
47+
CaptureSet(this.elems.intersect(that.elems))
48+
49+
/** {x} <:< this where <:< is subcapturing */
50+
def accountsFor(x: CaptureRef)(using Context) =
51+
elems.contains(x) || !x.isRootCapability && x.captureSetOfInfo <:< this
52+
53+
/** The subcapturing test */
54+
def <:< (that: CaptureSet)(using Context): Boolean =
55+
elems.isEmpty || elems.forall(that.accountsFor)
56+
57+
def flatMap(f: CaptureRef => CaptureSet)(using Context): CaptureSet =
58+
(empty /: elems)((cs, ref) => cs ++ f(ref))
59+
60+
def substParams(tl: BindingType, to: List[Type])(using Context) =
61+
flatMap {
62+
case ref: ParamRef if ref.binder eq tl => to(ref.paramNum).captureSet
63+
case ref => ref.singletonCaptureSet
64+
}
65+
66+
override def toString = elems.toString
67+
68+
override def toText(printer: Printer): Text =
69+
Str("{") ~ Text(elems.toList.map(printer.toTextCaptureRef), ", ") ~ Str("}")
70+
71+
object CaptureSet:
72+
type Refs = SimpleIdentitySet[CaptureRef]
73+
74+
@sharable val empty: CaptureSet = CaptureSet(SimpleIdentitySet.empty)
75+
76+
/** Used as a recursion brake */
77+
@sharable private[core] val Pending = CaptureSet(SimpleIdentitySet.empty)
78+
79+
def apply(elems: CaptureRef*)(using Context): CaptureSet =
80+
if elems.isEmpty then empty
81+
else CaptureSet(SimpleIdentitySet(elems.map(_.normalizedRef)*))
82+
83+
def ofClass(cinfo: ClassInfo, argTypes: List[Type])(using Context): CaptureSet =
84+
def captureSetOf(tp: Type): CaptureSet = tp match
85+
case tp: TypeRef if tp.symbol.is(ParamAccessor) =>
86+
def mapArg(accs: List[Symbol], tps: List[Type]): CaptureSet = accs match
87+
case acc :: accs1 if tps.nonEmpty =>
88+
if acc == tp.symbol then tps.head.captureSet
89+
else mapArg(accs1, tps.tail)
90+
case _ =>
91+
empty
92+
mapArg(cinfo.cls.paramAccessors, argTypes)
93+
case _ =>
94+
tp.captureSet
95+
val css =
96+
for
97+
parent <- cinfo.parents if parent.classSymbol == defn.RetainsClass
98+
arg <- parent.argInfos
99+
yield captureSetOf(arg)
100+
css.foldLeft(empty)(_ ++ _)
101+
102+
def ofType(tp: Type)(using Context): CaptureSet =
103+
def recur(tp: Type): CaptureSet = tp match
104+
case tp: NamedType =>
105+
tp.captureSet
106+
case tp: ParamRef =>
107+
tp.captureSet
108+
case CapturingType(parent, ref) =>
109+
recur(parent) + ref
110+
case AppliedType(tycon, args) =>
111+
val cs = recur(tycon)
112+
tycon.typeParams match
113+
case tparams @ (LambdaParam(tl, _) :: _) => cs.substParams(tl, args)
114+
case _ => cs
115+
case tp: TypeProxy =>
116+
recur(tp.underlying)
117+
case AndType(tp1, tp2) =>
118+
recur(tp1).intersect(recur(tp2))
119+
case OrType(tp1, tp2) =>
120+
recur(tp1) ++ recur(tp2)
121+
case tp: ClassInfo =>
122+
ofClass(tp, Nil)
123+
case _ =>
124+
empty
125+
recur(tp)
126+
.showing(i"capture set of $tp = $result", capt)
127+

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,13 @@ class Definitions {
143143
private def enterMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
144144
newMethod(cls, name, info, flags).entered
145145

146-
private def enterAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = {
147-
val sym = newPermanentSymbol(ScalaPackageClass, name, flags, TypeAlias(tpe))
146+
private def enterType(name: TypeName, info: Type, flags: FlagSet = EmptyFlags): TypeSymbol =
147+
val sym = newPermanentSymbol(ScalaPackageClass, name, flags, info)
148148
ScalaPackageClass.currentPackageDecls.enter(sym)
149149
sym
150-
}
150+
151+
private def enterAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol =
152+
enterType(name, TypeAlias(tpe), flags)
151153

152154
private def enterBinaryAlias(name: TypeName, op: (Type, Type) => Type): TypeSymbol =
153155
enterAliasType(name,
@@ -262,6 +264,7 @@ class Definitions {
262264
*/
263265
@tu lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil), ensureCtor = false)
264266
def AnyType: TypeRef = AnyClass.typeRef
267+
@tu lazy val TopType: Type = CapturingType(AnyType, captureRootType.typeRef)
265268
@tu lazy val MatchableClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Matchable, Trait, AnyType :: Nil), ensureCtor = false)
266269
def MatchableType: TypeRef = MatchableClass.typeRef
267270
@tu lazy val AnyValClass: ClassSymbol =
@@ -440,6 +443,7 @@ class Definitions {
440443

441444
@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
442445
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))
446+
@tu lazy val captureRootType: TypeSymbol = enterType(tpnme.CAPTURE_ROOT, TypeBounds.empty, Deferred)
443447

444448
/** Marker method to indicate an argument to a call-by-name parameter.
445449
* Created by byNameClosures and elimByName, eliminated by Erasure,
@@ -470,6 +474,7 @@ class Definitions {
470474
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
471475
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
472476
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
477+
@tu lazy val Predef_retainsType: Symbol = ScalaPredefModule.requiredType(tpnme.retains)
473478
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass
474479

475480
@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
@@ -874,6 +879,8 @@ class Definitions {
874879
lazy val RuntimeTuples_isInstanceOfEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfEmptyTuple")
875880
lazy val RuntimeTuples_isInstanceOfNonEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfNonEmptyTuple")
876881

882+
@tu lazy val RetainsClass: ClassSymbol = requiredClass("scala.Retains")
883+
877884
// Annotation base classes
878885
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
879886
@tu lazy val ClassfileAnnotationClass: ClassSymbol = requiredClass("scala.annotation.ClassfileAnnotation")
@@ -926,6 +933,7 @@ class Definitions {
926933
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
927934
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
928935
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
936+
@tu lazy val AbilityAnnot: ClassSymbol = requiredClass("scala.annotation.ability")
929937

930938
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")
931939

@@ -1749,6 +1757,7 @@ class Definitions {
17491757
AnyKindClass,
17501758
andType,
17511759
orType,
1760+
captureRootType,
17521761
RepeatedParamClass,
17531762
ByNameParamClass2x,
17541763
AnyValClass,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,6 @@ object Mode {
124124
* This mode forces expansion of inline calls in those positions even during typing.
125125
*/
126126
val ForceInline: Mode = newMode(29, "ForceInline")
127+
128+
val RelaxedCapturing: Mode = newMode(30, "RelaxedCapturing")
127129
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
329329
case tp: AnnotatedType =>
330330
val parent1 = recur(tp.parent, fromBelow)
331331
if parent1 ne tp.parent then tp.derivedAnnotatedType(parent1, tp.annot) else tp
332+
case tp: CapturingType =>
333+
val parent1 = recur(tp.parent, fromBelow)
334+
if parent1 ne tp.parent then tp.derivedCapturingType(parent1, tp.ref) else tp
332335
case _ =>
333336
val tp1 = tp.dealiasKeepAnnots
334337
if tp1 ne tp then

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ object StdNames {
275275

276276
// Compiler-internal
277277
val ANYname: N = "<anyname>"
278+
val CAPTURE_ROOT: N = "*"
278279
val COMPANION: N = "<companion>"
279280
val CONSTRUCTOR: N = "<init>"
280281
val STATIC_CONSTRUCTOR: N = "<clinit>"
@@ -361,6 +362,7 @@ object StdNames {
361362
val AppliedTypeTree: N = "AppliedTypeTree"
362363
val ArrayAnnotArg: N = "ArrayAnnotArg"
363364
val CAP: N = "CAP"
365+
val ClassManifestFactory: N = "ClassManifestFactory"
364366
val Constant: N = "Constant"
365367
val ConstantType: N = "ConstantType"
366368
val Eql: N = "Eql"
@@ -438,7 +440,6 @@ object StdNames {
438440
val canEqualAny : N = "canEqualAny"
439441
val cbnArg: N = "<cbn-arg>"
440442
val checkInitialized: N = "checkInitialized"
441-
val ClassManifestFactory: N = "ClassManifestFactory"
442443
val classOf: N = "classOf"
443444
val clone_ : N = "clone"
444445
val common: N = "common"
@@ -568,6 +569,7 @@ object StdNames {
568569
val reflectiveSelectable: N = "reflectiveSelectable"
569570
val reify : N = "reify"
570571
val releaseFence : N = "releaseFence"
572+
val retains: N = "retains"
571573
val rootMirror : N = "rootMirror"
572574
val run: N = "run"
573575
val runOrElse: N = "runOrElse"

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,8 @@ object SymDenotations {
14971497
case tp: ExprType => hasSkolems(tp.resType)
14981498
case tp: AppliedType => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems)
14991499
case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
1500-
case tp: AndType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
1501-
case tp: OrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
1502-
case tp: AnnotatedType => hasSkolems(tp.parent)
1500+
case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
1501+
case tp: AnnotOrCaptType => hasSkolems(tp.parent)
15031502
case _ => false
15041503
}
15051504

@@ -2151,6 +2150,9 @@ object SymDenotations {
21512150
case tp: TypeParamRef => // uncachable, since baseType depends on context bounds
21522151
recur(TypeComparer.bounds(tp).hi)
21532152

2153+
case tp: CapturingType =>
2154+
tp.derivedCapturingType(recur(tp.parent), tp.ref)
2155+
21542156
case tp: TypeProxy =>
21552157
def computeTypeProxy = {
21562158
val superTp = tp.superType

0 commit comments

Comments
 (0)