Skip to content

Commit c2b805a

Browse files
committed
Add adapation to convert global to local root
1 parent ad43c66 commit c2b805a

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ end CCState
8383
def ccState(using Context) =
8484
Phases.checkCapturesPhase.asInstanceOf[CheckCaptures].ccState
8585

86+
class NoCommonRoot(rs: Symbol*)(using Context) extends Exception(
87+
i"No common capture root nested in ${rs.mkString(" and ")}"
88+
)
89+
8690
trait FollowAliases extends TypeMap:
8791
def mapOverFollowingAliases(t: Type): Type = t match
8892
case t: LazyRef =>
@@ -323,7 +327,7 @@ extension (tp: Type)
323327
tp.isCapabilityClassRef
324328
end hasUniversalRootOf
325329

326-
extension (cls: Symbol)
330+
extension (cls: ClassSymbol)
327331

328332
def pureBaseClass(using Context): Option[Symbol] =
329333
if cls.isClass then cls.asClass.baseClasses.find: bc =>
@@ -391,6 +395,13 @@ extension (sym: Symbol)
391395
case _ =>
392396
false
393397

398+
def isTrackedSomewhere(using Context): Boolean =
399+
val search = new TypeAccumulator[Boolean]:
400+
def apply(found: Boolean, tp: Type) =
401+
def isTrackedHere = variance >= 0 && !tp.captureSet.isAlwaysEmpty
402+
found || isTrackedHere || foldOver(found, tp)
403+
search(false, sym.info)
404+
394405
// TODO Also include vals (right now they are manually entered in levelOwners by Setup)
395406
def isLevelOwner(using Context): Boolean =
396407
val symd = sym.denot

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

+7
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ sealed abstract class CaptureSet extends Showable:
9090
case ref: TermRef => ref.isRootCapability
9191
case _ => false
9292

93+
final def disallowsUniversal(using Context) =
94+
if isConst then !isUniversal && elems.exists(_.isLocalRootCapability)
95+
else asVar.noUniversal
96+
9397
/** Add new elements to this capture set if allowed.
9498
* @pre `newElems` is not empty and does not overlap with `this.elems`.
9599
* Constant capture sets never allow to add new elements.
@@ -476,6 +480,8 @@ object CaptureSet:
476480
/** A handler to be invoked if the root reference `cap` is added to this set */
477481
var rootAddedHandler: () => Context ?=> Unit = () => ()
478482

483+
private[CaptureSet] var noUniversal = false
484+
479485
/** A handler to be invoked when new elems are added to this set */
480486
var newElemAddedHandler: CaptureRef => Context ?=> Unit = _ => ()
481487

@@ -545,6 +551,7 @@ object CaptureSet:
545551
CompareResult.Fail(this :: Nil)
546552

547553
override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type =
554+
noUniversal = true
548555
rootAddedHandler = handler
549556
super.disallowRootCapability(handler)
550557

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

+35-4
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ class CheckCaptures extends Recheck, SymTransformer:
730730
case _ =>
731731
val res =
732732
try super.recheck(tree, pt)
733-
catch case ex: CaptureRoot.NoCommonRoot =>
733+
catch case ex: NoCommonRoot =>
734734
report.error(ex.getMessage.nn)
735735
tree.tpe
736736
finally curEnv = saved
@@ -760,9 +760,13 @@ class CheckCaptures extends Recheck, SymTransformer:
760760
}
761761
checkNotUniversal(parent)
762762
case _ =>
763-
if !allowUniversalInBoxed && needsUniversalCheck then
764-
checkNotUniversal(tpe)
765-
super.recheckFinish(tpe, tree, pt)
763+
val adapted =
764+
if allowUniversalInBoxed then
765+
adaptUniversal(tpe, pt, tree)
766+
else
767+
if needsUniversalCheck then checkNotUniversal(tpe)
768+
tpe
769+
super.recheckFinish(adapted, tree, pt)
766770
end recheckFinish
767771

768772
// ------------------ Adaptation -------------------------------------
@@ -776,6 +780,32 @@ class CheckCaptures extends Recheck, SymTransformer:
776780
// - Adapt box status and environment capture sets by simulating box/unbox operations.
777781
// - Instantiate `cap` in actual as needed to a local root.
778782

783+
def impliedRoot(tree: Tree)(using Context) =
784+
val acc = new TreeAccumulator[Symbol]:
785+
val locals = mutable.Set[Symbol]()
786+
private def max(sym1: Symbol, sym2: Symbol)(using Context) =
787+
sym1.maxNested(sym2, onConflict = (_, _) => throw NoCommonRoot(sym1, sym2))
788+
def apply(s: Symbol, t: Tree)(using Context) = t match
789+
case t: (Ident | This)
790+
if !locals.contains(t.symbol) && t.symbol.isTrackedSomewhere =>
791+
max(s, t.symbol.levelOwner)
792+
case t: DefTree =>
793+
locals += t.symbol
794+
foldOver(s, t)
795+
case _ =>
796+
foldOver(s, t)
797+
acc(defn.RootClass, tree).localRoot
798+
799+
def adaptUniversal(actual: Type, expected: Type, tree: Tree)(using Context): Type =
800+
if expected.captureSet.disallowsUniversal && actual.captureSet.isUniversal then
801+
val localRoot = impliedRoot(tree)
802+
CapturingType(
803+
actual.stripCapturing,
804+
localRoot.termRef.singletonCaptureSet,
805+
actual.isBoxedCapturing)
806+
.showing(i"adapt universal $actual vs $expected = $result")
807+
else actual
808+
779809
/** Massage `actual` and `expected` types before checking conformance.
780810
* Massaging is done by the methods following this one:
781811
* - align dependent function types and add outer references in the expected type
@@ -865,6 +895,7 @@ class CheckCaptures extends Recheck, SymTransformer:
865895
expected
866896
end addOuterRefs
867897

898+
868899
/** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions
869900
*
870901
* @param alwaysConst always make capture set variables constant after adaptation

0 commit comments

Comments
 (0)