Skip to content

Commit 60c0960

Browse files
committed
Refactorings and drop useExistential Config option
1 parent f5229fd commit 60c0960

File tree

3 files changed

+76
-122
lines changed

3 files changed

+76
-122
lines changed

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ object ccConfig:
3838
*/
3939
inline val deferredReaches = false
4040

41-
/** If true, use existential capture set variables */
42-
def useExistentials(using Context) =
43-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.5`)
44-
4541
/** If true, use "sealed" as encapsulation mechanism, meaning that we
4642
* check that type variable instantiations don't have `cap` in any of
4743
* their capture sets. This is an alternative of the original restriction
@@ -479,17 +475,6 @@ extension (tp: Type)
479475
* occurrences of cap are allowed in instance types of type variables.
480476
*/
481477
def withReachCaptures(ref: Type)(using Context): Type =
482-
class CheckContraCaps extends TypeTraverser:
483-
var ok = true
484-
def traverse(t: Type): Unit =
485-
if ok then
486-
t.dealias match
487-
case CapturingType(_, cs) if cs.isUniversal && variance <= 0 =>
488-
ok = false
489-
case _ =>
490-
traverseChildren(t)
491-
end CheckContraCaps
492-
493478
object narrowCaps extends TypeMap:
494479
def apply(t: Type) =
495480
if variance <= 0 then t
@@ -512,15 +497,9 @@ extension (tp: Type)
512497

513498
ref match
514499
case ref: CaptureRef if ref.isTrackableRef =>
515-
val checker = new CheckContraCaps
516-
if !ccConfig.useExistentials then checker.traverse(tp)
517-
if checker.ok then
518-
val tp1 = narrowCaps(tp)
519-
if tp1 ne tp then capt.println(i"narrow $tp of $ref to $tp1")
520-
tp1
521-
else
522-
capt.println(i"cannot narrow $tp of $ref")
523-
tp
500+
val tp1 = narrowCaps(tp)
501+
if tp1 ne tp then capt.println(i"narrow $tp of $ref to $tp1")
502+
tp1
524503
case _ =>
525504
tp
526505

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

Lines changed: 71 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ object CheckCaptures:
118118
def inverse = thisMap
119119
end SubstParamsBiMap
120120

121+
/** A prototype that indicates selection with an immutable value */
122+
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto
123+
121124
/** Check that a @retains annotation only mentions references that can be tracked.
122125
* This check is performed at Typer.
123126
*/
@@ -141,9 +144,9 @@ object CheckCaptures:
141144
case ReachCapabilityApply(arg) => check(arg, elem.srcPos)
142145
case _ => check(elem, elem.srcPos)
143146

144-
/** Report an error if some part of `tp` contains the root capability in its capture set
145-
* or if it refers to an unsealed type parameter that could possibly be instantiated with
146-
* cap in a way that's visible at the type.
147+
/** Under the sealed policy, report an error if some part of `tp` contains the
148+
* root capability in its capture set or if it refers to a type parameter that
149+
* could possibly be instantiated with cap in a way that's visible at the type.
147150
*/
148151
private def disallowRootCapabilitiesIn(tp: Type, carrier: Symbol, what: String, have: String, addendum: String, pos: SrcPos)(using Context) =
149152
val check = new TypeTraverser:
@@ -179,8 +182,66 @@ object CheckCaptures:
179182
if ccConfig.useSealed then check.traverse(tp)
180183
end disallowRootCapabilitiesIn
181184

182-
/** A prototype that indicates selection with an immutable value */
183-
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto
185+
/** Under the sealed policy, disallow the root capability in type arguments.
186+
* Type arguments come either from a TypeApply node or from an AppliedType
187+
* which represents a trait parent in a template.
188+
* @param fn the type application, of type TypeApply or TypeTree
189+
* @param sym the constructor symbol (could be a method or a val or a class)
190+
* @param args the type arguments
191+
*/
192+
private def disallowCapInTypeArgs(fn: Tree, sym: Symbol, args: List[Tree], thisPhase: Phase)(using Context): Unit =
193+
def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
194+
if ccConfig.useSealed && !isExempt then
195+
val paramNames = atPhase(thisPhase.prev):
196+
fn.tpe.widenDealias match
197+
case tl: TypeLambda => tl.paramNames
198+
case ref: AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
199+
case t =>
200+
println(i"parent type: $t")
201+
args.map(_ => EmptyTypeName)
202+
for case (arg: TypeTree, pname) <- args.lazyZip(paramNames) do
203+
def where = if sym.exists then i" in an argument of $sym" else ""
204+
val (addendum, pos) =
205+
if arg.isInferred
206+
then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn.srcPos)
207+
else if arg.span.exists then ("", arg.srcPos)
208+
else ("", fn.srcPos)
209+
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
210+
i"Type variable $pname of $sym", "be instantiated to", addendum, pos)
211+
end disallowCapInTypeArgs
212+
213+
/** If we are not under the sealed policy, and a tree is an application that unboxes
214+
* its result or is a try, check that the tree's type does not have covariant universal
215+
* capabilities.
216+
*/
217+
private def checkNotUniversalInUnboxedResult(tpe: Type, tree: Tree)(using Context): Unit =
218+
def needsUniversalCheck = tree match
219+
case _: RefTree | _: Apply | _: TypeApply => tree.symbol.unboxesResult
220+
case _: Try => true
221+
case _ => false
222+
223+
object checkNotUniversal extends TypeTraverser:
224+
def traverse(tp: Type) =
225+
tp.dealias match
226+
case wtp @ CapturingType(parent, refs) =>
227+
if variance > 0 then
228+
refs.disallowRootCapability: () =>
229+
def part = if wtp eq tpe.widen then "" else i" in its part $wtp"
230+
report.error(
231+
em"""The expression's type ${tpe.widen} is not allowed to capture the root capability `cap`$part.
232+
|This usually means that a capability persists longer than its allowed lifetime.""",
233+
tree.srcPos)
234+
if !wtp.isBoxed then traverse(parent)
235+
case tp =>
236+
traverseChildren(tp)
237+
238+
if !ccConfig.useSealed
239+
&& !tpe.hasAnnotation(defn.UncheckedCapturesAnnot)
240+
&& needsUniversalCheck
241+
&& tpe.widen.isValueType
242+
then
243+
checkNotUniversal.traverse(tpe.widen)
244+
end checkNotUniversalInUnboxedResult
184245

185246
class CheckCaptures extends Recheck, SymTransformer:
186247
thisPhase =>
@@ -264,41 +325,6 @@ class CheckCaptures extends Recheck, SymTransformer:
264325
def showRef(ref: CaptureRef)(using Context): String =
265326
ctx.printer.toTextCaptureRef(ref).show
266327

267-
// Uses 4-space indent as a trial
268-
private def checkReachCapsIsolated(tpe: Type, pos: SrcPos)(using Context): Unit =
269-
270-
object checker extends TypeTraverser:
271-
var refVariances: Map[Boolean, Int] = Map.empty
272-
var seenReach: CaptureRef | Null = null
273-
def traverse(tp: Type) =
274-
tp.dealias match
275-
case CapturingType(parent, refs) =>
276-
traverse(parent)
277-
for ref <- refs.elems do
278-
if ref.isReach && !ref.stripReach.isInstanceOf[TermParamRef]
279-
|| ref.isRootCapability
280-
then
281-
val isReach = ref.isReach
282-
def register() =
283-
refVariances = refVariances.updated(isReach, variance)
284-
seenReach = ref
285-
refVariances.get(isReach) match
286-
case None => register()
287-
case Some(v) => if v != 0 && variance == 0 then register()
288-
case _ =>
289-
traverseChildren(tp)
290-
291-
checker.traverse(tpe)
292-
if checker.refVariances.size == 2
293-
&& checker.refVariances(true) >= 0
294-
&& checker.refVariances(false) <= 0
295-
then
296-
report.error(
297-
em"""Reach capability ${showRef(checker.seenReach.nn)} and universal capability cap cannot both
298-
|appear in the type $tpe of this expression""",
299-
pos)
300-
end checkReachCapsIsolated
301-
302328
/** The current environment */
303329
private val rootEnv: Env = inContext(ictx):
304330
Env(defn.RootClass, EnvKind.Regular, CaptureSet.empty, null)
@@ -680,32 +706,11 @@ class CheckCaptures extends Recheck, SymTransformer:
680706
else ownType
681707
end instantiate
682708

683-
def disallowCapInTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit =
684-
def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
685-
if ccConfig.useSealed && !isExempt then
686-
val paramNames = atPhase(thisPhase.prev):
687-
fn.tpe.widenDealias match
688-
case tl: TypeLambda => tl.paramNames
689-
case ref: AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
690-
case t =>
691-
println(i"parent type: $t")
692-
args.map(_ => EmptyTypeName)
693-
for case (arg: TypeTree, pname) <- args.lazyZip(paramNames) do
694-
def where = if sym.exists then i" in an argument of $sym" else ""
695-
val (addendum, pos) =
696-
if arg.isInferred
697-
then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn.srcPos)
698-
else if arg.span.exists then ("", arg.srcPos)
699-
else ("", fn.srcPos)
700-
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
701-
i"Type variable $pname of $sym", "be instantiated to", addendum, pos)
702-
end disallowCapInTypeArgs
703-
704709
override def recheckTypeApply(tree: TypeApply, pt: Type)(using Context): Type =
705710
val meth = tree.fun match
706711
case fun @ Select(qual, nme.apply) => qual.symbol.orElse(fun.symbol)
707712
case fun => fun.symbol
708-
disallowCapInTypeArgs(tree.fun, meth, tree.args)
713+
disallowCapInTypeArgs(tree.fun, meth, tree.args, thisPhase)
709714
val res = Existential.toCap(super.recheckTypeApply(tree, pt))
710715
includeCallCaptures(tree.symbol, res, tree.srcPos)
711716
checkContains(tree)
@@ -935,7 +940,7 @@ class CheckCaptures extends Recheck, SymTransformer:
935940
for case tpt: TypeTree <- impl.parents do
936941
tpt.tpe match
937942
case AppliedType(fn, args) =>
938-
disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree(_)))
943+
disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree(_)), thisPhase)
939944
case _ =>
940945
inNestedLevelUnless(cls.is(Module)):
941946
super.recheckClassDef(tree, impl, cls)
@@ -1003,40 +1008,12 @@ class CheckCaptures extends Recheck, SymTransformer:
10031008
report.error(ex.getMessage.nn)
10041009
tree.tpe
10051010
finally curEnv = saved
1006-
if tree.isTerm then
1007-
if !ccConfig.useExistentials then
1008-
checkReachCapsIsolated(res.widen, tree.srcPos)
1009-
if !pt.isBoxedCapturing && pt != LhsProto then
1010-
markFree(res.boxedCaptureSet, tree.srcPos)
1011+
if tree.isTerm && !pt.isBoxedCapturing && pt != LhsProto then
1012+
markFree(res.boxedCaptureSet, tree.srcPos)
10111013
res
10121014

10131015
override def recheckFinish(tpe: Type, tree: Tree, pt: Type)(using Context): Type =
1014-
def needsUniversalCheck = tree match
1015-
case _: RefTree | _: Apply | _: TypeApply => tree.symbol.unboxesResult
1016-
case _: Try => true
1017-
case _ => false
1018-
1019-
object checkNotUniversal extends TypeTraverser:
1020-
def traverse(tp: Type) =
1021-
tp.dealias match
1022-
case wtp @ CapturingType(parent, refs) =>
1023-
if variance > 0 then
1024-
refs.disallowRootCapability: () =>
1025-
def part = if wtp eq tpe.widen then "" else i" in its part $wtp"
1026-
report.error(
1027-
em"""The expression's type ${tpe.widen} is not allowed to capture the root capability `cap`$part.
1028-
|This usually means that a capability persists longer than its allowed lifetime.""",
1029-
tree.srcPos)
1030-
if !wtp.isBoxed then traverse(parent)
1031-
case tp =>
1032-
traverseChildren(tp)
1033-
1034-
if !ccConfig.useSealed
1035-
&& !tpe.hasAnnotation(defn.UncheckedCapturesAnnot)
1036-
&& needsUniversalCheck
1037-
&& tpe.widen.isValueType
1038-
then
1039-
checkNotUniversal.traverse(tpe.widen)
1016+
checkNotUniversalInUnboxedResult(tpe, tree)
10401017
super.recheckFinish(tpe, tree, pt)
10411018
end recheckFinish
10421019

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,8 @@ object Existential:
333333
override def toString = "Wrap.inverse"
334334
end Wrap
335335

336-
if ccConfig.useExistentials then
337-
val wrapped = apply(Wrap(_)(tp))
338-
if needsWrap then wrapped else tp
339-
else tp
336+
val wrapped = apply(Wrap(_)(tp))
337+
if needsWrap then wrapped else tp
340338
end mapCap
341339

342340
def mapCapInResults(fail: Message => Unit)(using Context): TypeMap = new:

0 commit comments

Comments
 (0)