Skip to content

Commit f830274

Browse files
committed
CapturingType extractors
1 parent 965a73b commit f830274

18 files changed

+168
-159
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package dotc
33

44
import core._
55
import Contexts._
6-
import typer.{TyperPhase, RefChecks, CheckCaptures}
6+
import typer.{TyperPhase, RefChecks}
7+
import cc.CheckCaptures
78
import parsing.Parser
89
import Phases.Phase
910
import transform._

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

Lines changed: 73 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -4,105 +4,80 @@ package cc
44

55
import core.*
66
import Types.*, Symbols.*, Contexts.*, Annotations.*
7-
import ast.Trees.*
87
import ast.{tpd, untpd}
98
import Decorators.*
109
import config.Printers.capt
1110
import util.Property.Key
12-
13-
object CaptureOps:
14-
import tpd.*
15-
16-
private val Captures: Key[CaptureSet] = Key()
17-
18-
def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
19-
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
20-
case _ => Nil
21-
22-
extension (tree: Tree)
23-
24-
def toCaptureRef(using Context): CaptureRef = tree.tpe.asInstanceOf[CaptureRef]
25-
26-
def toCaptureSet(using Context): CaptureSet =
27-
tree.getAttachment(Captures) match
28-
case Some(refs) => refs
29-
case None =>
30-
val refs = CaptureSet(retainedElems(tree).map(_.toCaptureRef)*)
31-
.showing(i"toCaptureSet $tree --> $result", capt)
32-
tree.putAttachment(Captures, refs)
33-
refs
34-
35-
extension (tp: Type)
36-
37-
def derivedCapturingType(parent: Type, refs: CaptureSet)(using Context): Type = tp match
38-
case CapturingType(p, r) =>
39-
if (parent eq p) && (refs eq r) then tp
40-
else CapturingType(parent, refs)
41-
42-
/** If this is type variable instantiated or upper bounded with a capturing type,
43-
* the capture set associated with that type. Extended to and-or types and
44-
* type proxies in the obvious way. If a term has a type with a boxed captureset,
45-
* that captureset counts towards the capture variables of the envirionment.
46-
*/
47-
def boxedCaptured(using Context): CaptureSet =
48-
def getBoxed(tp: Type, enabled: Boolean): CaptureSet = tp match
49-
case tp: CapturingType if enabled => tp.refs
50-
case tp: TypeVar => getBoxed(tp.underlying, enabled = true)
51-
case tp: TypeRef if tp.symbol == defn.AnyClass && enabled => CaptureSet.universal
52-
case tp: TypeProxy => getBoxed(tp.superType, enabled)
53-
case tp: AndType => getBoxed(tp.tp1, enabled) ++ getBoxed(tp.tp2, enabled)
54-
case tp: OrType => getBoxed(tp.tp1, enabled) ** getBoxed(tp.tp2, enabled)
55-
case _ => CaptureSet.empty
56-
getBoxed(tp, enabled = false)
57-
58-
/** If this type appears as an expected type of a term, does it imply
59-
* that the term should be boxed?
60-
* ^^^ Special treat Any? - but the current status is more conservative in that
61-
* it counts free variables in expressions that have Any as expected type.
62-
*/
63-
def needsBox(using Context): Boolean = tp match
64-
case _: TypeVar => true
65-
case tp: TypeRef =>
66-
tp.info match
67-
case TypeBounds(lo, _) => lo.needsBox
68-
case _ => false
69-
case tp: RefinedOrRecType => tp.parent.needsBox
70-
case tp: AnnotatedType => tp.parent.needsBox
71-
case tp: LazyRef => tp.ref.needsBox
72-
case tp: AndType => tp.tp1.needsBox || tp.tp2.needsBox
73-
case tp: OrType => tp.tp1.needsBox && tp.tp2.needsBox
74-
case _ => false
75-
76-
def canHaveInferredCapture(using Context): Boolean = tp match
77-
case tp: TypeRef if tp.symbol.isClass =>
78-
!tp.symbol.isValueClass && tp.symbol != defn.AnyClass
79-
case tp: TypeProxy =>
80-
tp.superType.canHaveInferredCapture
81-
case tp: AndType =>
82-
tp.tp1.canHaveInferredCapture && tp.tp2.canHaveInferredCapture
83-
case tp: OrType =>
84-
tp.tp1.canHaveInferredCapture || tp.tp2.canHaveInferredCapture
85-
case _ =>
86-
false
87-
88-
object CapturingAnnotType:
89-
90-
def apply(parent: Type, refs: CaptureSet)(using Context): Type =
91-
AnnotatedType(parent, CaptureAnnotation(refs))
92-
93-
def unapply(tp: AnnotatedType)(using Context) = tp.annot match
94-
case ann: CaptureAnnotation =>
95-
Some((tp.parent, ann.refs))
96-
case ann =>
97-
if ann.symbol == defn.RetainsAnnot
98-
then Some((tp.parent, ann.tree.toCaptureSet))
99-
else None
100-
end CapturingAnnotType
101-
102-
object PreCapturingType:
103-
def unapply(tp: AnnotatedType)(using Context) = tp.annot match
104-
case ann: CaptureAnnotation => Some((tp.parent, ann.refs))
105-
case _ =>
106-
None
107-
108-
end CaptureOps
11+
import tpd.*
12+
13+
private val Captures: Key[CaptureSet] = Key()
14+
15+
def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
16+
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
17+
case _ => Nil
18+
19+
extension (tree: Tree)
20+
21+
def toCaptureRef(using Context): CaptureRef = tree.tpe.asInstanceOf[CaptureRef]
22+
23+
def toCaptureSet(using Context): CaptureSet =
24+
tree.getAttachment(Captures) match
25+
case Some(refs) => refs
26+
case None =>
27+
val refs = CaptureSet(retainedElems(tree).map(_.toCaptureRef)*)
28+
.showing(i"toCaptureSet $tree --> $result", capt)
29+
tree.putAttachment(Captures, refs)
30+
refs
31+
32+
extension (tp: Type)
33+
34+
def derivedCapturingType(parent: Type, refs: CaptureSet)(using Context): Type = tp match
35+
case CapturingType(p, r) =>
36+
if (parent eq p) && (refs eq r) then tp
37+
else CapturingType(parent, refs)
38+
39+
/** If this is type variable instantiated or upper bounded with a capturing type,
40+
* the capture set associated with that type. Extended to and-or types and
41+
* type proxies in the obvious way. If a term has a type with a boxed captureset,
42+
* that captureset counts towards the capture variables of the envirionment.
43+
*/
44+
def boxedCaptured(using Context): CaptureSet =
45+
def getBoxed(tp: Type, enabled: Boolean): CaptureSet = tp match
46+
case CapturingType(_, refs) if enabled => refs
47+
case tp: TypeVar => getBoxed(tp.underlying, enabled = true)
48+
case tp: TypeRef if tp.symbol == defn.AnyClass && enabled => CaptureSet.universal
49+
case tp: TypeProxy => getBoxed(tp.superType, enabled)
50+
case tp: AndType => getBoxed(tp.tp1, enabled) ++ getBoxed(tp.tp2, enabled)
51+
case tp: OrType => getBoxed(tp.tp1, enabled) ** getBoxed(tp.tp2, enabled)
52+
case _ => CaptureSet.empty
53+
getBoxed(tp, enabled = false)
54+
55+
/** If this type appears as an expected type of a term, does it imply
56+
* that the term should be boxed?
57+
* ^^^ Special treat Any? - but the current status is more conservative in that
58+
* it counts free variables in expressions that have Any as expected type.
59+
*/
60+
def needsBox(using Context): Boolean = tp match
61+
case _: TypeVar => true
62+
case tp: TypeRef =>
63+
tp.info match
64+
case TypeBounds(lo, _) => lo.needsBox
65+
case _ => false
66+
case tp: RefinedOrRecType => tp.parent.needsBox
67+
case tp: AnnotatedType => tp.parent.needsBox
68+
case tp: LazyRef => tp.ref.needsBox
69+
case tp: AndType => tp.tp1.needsBox || tp.tp2.needsBox
70+
case tp: OrType => tp.tp1.needsBox && tp.tp2.needsBox
71+
case _ => false
72+
73+
def canHaveInferredCapture(using Context): Boolean = tp match
74+
case tp: TypeRef if tp.symbol.isClass =>
75+
!tp.symbol.isValueClass && tp.symbol != defn.AnyClass
76+
case tp: TypeProxy =>
77+
tp.superType.canHaveInferredCapture
78+
case tp: AndType =>
79+
tp.tp1.canHaveInferredCapture && tp.tp2.canHaveInferredCapture
80+
case tp: OrType =>
81+
tp.tp1.canHaveInferredCapture || tp.tp2.canHaveInferredCapture
82+
case _ =>
83+
false
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dotty.tools
2+
package dotc
3+
package cc
4+
5+
import core.*
6+
import Types.*, Symbols.*, Contexts.*
7+
8+
object CapturingType:
9+
10+
private val old = true
11+
12+
def apply(parent: Type, refs: CaptureSet)(using Context): Type =
13+
if old then CapturingTypeMaker(parent, refs)
14+
else if refs.isAlwaysEmpty then parent
15+
else AnnotatedType(parent, CaptureAnnotation(refs))
16+
17+
def unapply(tp: Type)(using Context) = tp match
18+
case tp: CapturingType => Some((tp.parent, tp.captureSet))
19+
case tp: AnnotatedType => tp.annot match
20+
case ann: CaptureAnnotation =>
21+
Some((tp.parent, ann.refs))
22+
case ann =>
23+
if ann.symbol == defn.RetainsAnnot && ctx.phase == Phases.checkCapturesPhase
24+
then Some((tp.parent, ann.tree.toCaptureSet))
25+
else None
26+
case _ => None
27+
28+
end CapturingType

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import printing.{Showable, Printer}
1212
import printing.Texts.*
1313
import util.SimpleIdentitySet
1414
import util.common.alwaysTrue
15+
import cc.CapturingType
1516

1617
/** A class for capture sets. Capture sets can be constants or variables.
1718
* Capture sets support inclusion constraints <:< where <:< is subcapturing.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import typer.ImportInfo.RootRef
1414
import Comments.CommentsContext
1515
import Comments.Comment
1616
import util.Spans.NoSpan
17+
import cc.CapturingType
1718

1819
import scala.annotation.tailrec
1920

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import config.Printers.constr
1212
import reflect.ClassTag
1313
import annotation.tailrec
1414
import annotation.internal.sharable
15+
import cc.{CapturingType, derivedCapturingType}
1516

1617
object OrderingConstraint {
1718

@@ -328,12 +329,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
328329
case tp: TypeVar =>
329330
val underlying1 = recur(tp.underlying, fromBelow)
330331
if underlying1 ne tp.underlying then underlying1 else tp
332+
case CapturingType(parent, refs) =>
333+
val parent1 = recur(parent, fromBelow)
334+
if parent1 ne parent then tp.derivedCapturingType(parent1, refs) else tp
331335
case tp: AnnotatedType =>
332336
val parent1 = recur(tp.parent, fromBelow)
333337
if parent1 ne tp.parent then tp.derivedAnnotatedType(parent1, tp.annot) else tp
334-
case tp: CapturingType =>
335-
val parent1 = recur(tp.parent, fromBelow)
336-
if parent1 ne tp.parent then tp.derivedCapturingType(parent1, tp.refs) else tp
337338
case _ =>
338339
val tp1 = tp.dealiasKeepAnnots
339340
if tp1 ne tp then

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import dotty.tools.dotc.transform._
1515
import Periods._
1616
import parsing.Parser
1717
import printing.XprintMode
18-
import typer.{TyperPhase, RefChecks, CheckCaptures}
18+
import typer.{TyperPhase, RefChecks}
19+
import cc.CheckCaptures
1920
import typer.ImportInfo.withRootImports
2021
import ast.{tpd, untpd}
2122
import scala.annotation.internal.sharable

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import config.Config
2424
import reporting._
2525
import collection.mutable
2626
import transform.TypeUtils._
27+
import cc.{CapturingType, derivedCapturingType}
2728

2829
import scala.annotation.internal.sharable
2930

@@ -2165,8 +2166,8 @@ object SymDenotations {
21652166
case tp: TypeParamRef => // uncachable, since baseType depends on context bounds
21662167
recur(TypeComparer.bounds(tp).hi)
21672168

2168-
case tp: CapturingType =>
2169-
tp.derivedCapturingType(recur(tp.parent), tp.refs)
2169+
case CapturingType(parent, refs) =>
2170+
tp.derivedCapturingType(recur(parent), refs)
21702171

21712172
case tp: TypeProxy =>
21722173
def computeTypeProxy = {

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import reporting.trace
2525
import NullOpsDecorator._
2626
import CaptureSet.CompareResult as CaptCompareResult
2727
import annotation.constructorOnly
28+
import cc.{CapturingType, derivedCapturingType}
2829

2930
/** Provides methods to compare types.
3031
*/
@@ -445,8 +446,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
445446
// See i859.scala for an example where we hit this case.
446447
tp2.isRef(AnyClass, skipRefined = false)
447448
|| !tp1.evaluating && recur(tp1.ref, tp2)
448-
case tp1: AnnotatedType if !tp1.isRefining =>
449-
recur(tp1.parent, tp2)
450449
case AndType(tp11, tp12) =>
451450
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
452451
else thirdTry
@@ -490,9 +489,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
490489
// and then need to check that they are indeed supertypes of the original types
491490
// under -Ycheck. Test case is i7965.scala.
492491

493-
case tp1: CapturingType =>
494-
if tp1.refs <:< tp2.captureSet == CaptCompareResult.OK then recur(tp1.parent, tp2)
492+
case CapturingType(parent1, refs1) =>
493+
if refs1 <:< tp2.captureSet == CaptCompareResult.OK then recur(parent1, tp2)
495494
else thirdTry
495+
case tp1: AnnotatedType if !tp1.isRefining =>
496+
recur(tp1.parent, tp2)
496497
case tp1: MatchType =>
497498
val reduced = tp1.reduced
498499
if (reduced.exists) recur(reduced, tp2) else thirdTry
@@ -2379,11 +2380,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23792380
}
23802381
case tp1: TypeVar if tp1.isInstantiated =>
23812382
tp1.underlying & tp2
2383+
case CapturingType(parent1, refs1) =>
2384+
if tp2.captureSet <:< refs1 == CaptCompareResult.OK then parent1 & tp2
2385+
else tp1.derivedCapturingType(parent1 & tp2, refs1)
23822386
case tp1: AnnotatedType if !tp1.isRefining =>
23832387
tp1.underlying & tp2
2384-
case tp1: CapturingType =>
2385-
if tp2.captureSet <:< tp1.refs == CaptCompareResult.OK then tp1.parent & tp2
2386-
else tp1.derivedCapturingType(tp1.parent & tp2, tp1.refs)
23872388
case _ =>
23882389
NoType
23892390
}

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import typer.Inferencing._
2020
import typer.IfBottom
2121
import reporting.TestingReporter
2222
import CaptureSet.CompareResult
23+
import cc.{CapturingType, derivedCapturingType}
2324

2425
import scala.annotation.internal.sharable
2526
import scala.annotation.threadUnsafe
@@ -163,6 +164,11 @@ object TypeOps:
163164
// with Nulls (which have no base classes). Under -Yexplicit-nulls, we take
164165
// corrective steps, so no widening is wanted.
165166
simplify(l, theMap) | simplify(r, theMap)
167+
case CapturingType(parent, refs) =>
168+
if !ctx.mode.is(Mode.Type) && refs <:< parent.captureSet == CompareResult.OK then
169+
simplify(parent, theMap)
170+
else
171+
mapOver
166172
case tp @ AnnotatedType(parent, annot) =>
167173
val parent1 = simplify(parent, theMap)
168174
if annot.symbol == defn.UncheckedVarianceAnnot
@@ -173,9 +179,6 @@ object TypeOps:
173179
case _: MatchType =>
174180
val normed = tp.tryNormalize
175181
if (normed.exists) normed else mapOver
176-
case tp: CapturingType
177-
if !ctx.mode.is(Mode.Type) && tp.refs <:< tp.parent.captureSet == CompareResult.OK =>
178-
simplify(tp.parent, theMap)
179182
case tp: MethodicType =>
180183
tp // See documentation of `Types#simplified`
181184
case tp: SkolemType =>
@@ -279,17 +282,17 @@ object TypeOps:
279282
tp1 match {
280283
case tp1: RecType =>
281284
return tp1.rebind(approximateOr(tp1.parent, tp2))
282-
case tp1: CapturingType =>
283-
return tp1.derivedCapturingType(approximateOr(tp1.parent, tp2), tp1.refs)
285+
case CapturingType(parent1, refs1) =>
286+
return tp1.derivedCapturingType(approximateOr(parent1, tp2), refs1)
284287
case err: ErrorType =>
285288
return err
286289
case _ =>
287290
}
288291
tp2 match {
289292
case tp2: RecType =>
290293
return tp2.rebind(approximateOr(tp1, tp2.parent))
291-
case tp2: CapturingType =>
292-
return tp2.derivedCapturingType(approximateOr(tp1, tp2.parent), tp2.refs)
294+
case CapturingType(parent2, refs2) =>
295+
return tp2.derivedCapturingType(approximateOr(tp1, parent2), refs2)
293296
case err: ErrorType =>
294297
return err
295298
case _ =>

0 commit comments

Comments
 (0)