Skip to content

Commit 96d1610

Browse files
committed
Refactorings and drop useExistential Config option
1 parent 0676450 commit 96d1610

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
@@ -121,6 +121,9 @@ object CheckCaptures:
121121
def inverse = thisMap
122122
end SubstParamsBiMap
123123

124+
/** A prototype that indicates selection with an immutable value */
125+
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto
126+
124127
/** Check that a @retains annotation only mentions references that can be tracked.
125128
* This check is performed at Typer.
126129
*/
@@ -144,9 +147,9 @@ object CheckCaptures:
144147
case ReachCapabilityApply(arg) => check(arg, elem.srcPos)
145148
case _ => check(elem, elem.srcPos)
146149

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

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

188249
class CheckCaptures extends Recheck, SymTransformer:
189250
thisPhase =>
@@ -269,41 +330,6 @@ class CheckCaptures extends Recheck, SymTransformer:
269330
def showRef(ref: CaptureRef)(using Context): String =
270331
ctx.printer.toTextCaptureRef(ref).show
271332

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

688-
def disallowCapInTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit =
689-
def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
690-
if ccConfig.useSealed && !isExempt then
691-
val paramNames = atPhase(thisPhase.prev):
692-
fn.tpe.widenDealias match
693-
case tl: TypeLambda => tl.paramNames
694-
case ref: AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
695-
case t =>
696-
println(i"parent type: $t")
697-
args.map(_ => EmptyTypeName)
698-
for case (arg: TypeTree, pname) <- args.lazyZip(paramNames) do
699-
def where = if sym.exists then i" in an argument of $sym" else ""
700-
val (addendum, pos) =
701-
if arg.isInferred
702-
then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn.srcPos)
703-
else if arg.span.exists then ("", arg.srcPos)
704-
else ("", fn.srcPos)
705-
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
706-
i"Type variable $pname of $sym", "be instantiated to", addendum, pos)
707-
end disallowCapInTypeArgs
708-
709714
override def recheckTypeApply(tree: TypeApply, pt: Type)(using Context): Type =
710715
val meth = tree.fun match
711716
case fun @ Select(qual, nme.apply) => qual.symbol.orElse(fun.symbol)
712717
case fun => fun.symbol
713-
disallowCapInTypeArgs(tree.fun, meth, tree.args)
718+
disallowCapInTypeArgs(tree.fun, meth, tree.args, thisPhase)
714719
val res = Existential.toCap(super.recheckTypeApply(tree, pt))
715720
includeCallCaptures(tree.symbol, res, tree.srcPos)
716721
checkContains(tree)
@@ -940,7 +945,7 @@ class CheckCaptures extends Recheck, SymTransformer:
940945
for case tpt: TypeTree <- impl.parents do
941946
tpt.tpe match
942947
case AppliedType(fn, args) =>
943-
disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree(_)))
948+
disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree(_)), thisPhase)
944949
case _ =>
945950
inNestedLevelUnless(cls.is(Module)):
946951
super.recheckClassDef(tree, impl, cls)
@@ -1008,40 +1013,12 @@ class CheckCaptures extends Recheck, SymTransformer:
10081013
report.error(ex.getMessage.nn)
10091014
tree.tpe
10101015
finally curEnv = saved
1011-
if tree.isTerm then
1012-
if !ccConfig.useExistentials then
1013-
checkReachCapsIsolated(res.widen, tree.srcPos)
1014-
if !pt.isBoxedCapturing && pt != LhsProto then
1015-
markFree(res.boxedCaptureSet, tree.srcPos)
1016+
if tree.isTerm && !pt.isBoxedCapturing && pt != LhsProto then
1017+
markFree(res.boxedCaptureSet, tree.srcPos)
10161018
res
10171019

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

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)