Skip to content

Commit 3750dc6

Browse files
committed
refactor box adaptation
1 parent 9d574ce commit 3750dc6

File tree

1 file changed

+78
-45
lines changed

1 file changed

+78
-45
lines changed

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

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -596,51 +596,84 @@ class CheckCaptures extends Recheck, SymTransformer:
596596
* @param reconstruct how to rebuild the adapted function type
597597
*/
598598
def adaptFun(actual: Type, aargs: List[Type], ares: Type, expected: Type,
599-
covariant: Boolean,
600-
reconstruct: (List[Type], Type) => Type): Type =
601-
val (eargs, eres) = expected.dealias match
602-
case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres)
603-
case _ => (aargs.map(_ => WildcardType), WildcardType)
604-
val aargs1 = aargs.zipWithConserve(eargs)(adapt(_, _, !covariant))
605-
val ares1 = adapt(ares, eres, covariant)
606-
if (ares1 eq ares) && (aargs1 eq aargs) then actual
607-
else reconstruct(aargs1, ares1)
608-
609-
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = actual.dealias match
610-
case actual @ CapturingType(parent, refs) =>
611-
val parent1 = adapt(parent, expected, covariant)
612-
if actual.isBoxed != expected.isBoxedCapturing then
613-
val criticalSet = // the set which is not allowed to have `*`
614-
if covariant then refs // can't box with `*`
615-
else expected.captureSet // can't unbox with `*`
616-
if criticalSet.isUniversal then
617-
// We can't box/unbox the universal capability. Leave `actual` as it is
618-
// so we get an error in checkConforms. This tends to give better error
619-
// messages than disallowing the root capability in `criticalSet`.
620-
capt.println(i"cannot box/unbox $actual vs $expected")
621-
actual
599+
covariant: Boolean, boxed: Boolean,
600+
reconstruct: (List[Type], Type) => Type): (Type, Some[CaptureSet]) =
601+
val saved = curEnv
602+
curEnv = Env(curEnv.owner, CaptureSet.Var(), isBoxed = false, if boxed then null else curEnv)
603+
604+
try
605+
val (eargs, eres) = expected.dealias match
606+
case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres)
607+
case _ => (aargs.map(_ => WildcardType), WildcardType)
608+
val aargs1 = aargs.zipWithConserve(eargs){ (aarg, earg) => adapt(aarg, earg, !covariant, boxed = false) }
609+
val ares1 = adapt(ares, eres, covariant, boxed = false)
610+
611+
val resTp =
612+
if (ares1 eq ares) && (aargs1 eq aargs) then actual
613+
else reconstruct(aargs1, ares1)
614+
615+
curEnv.captured.asVar.markSolved()
616+
(resTp, Some(curEnv.captured))
617+
finally
618+
curEnv = saved
619+
620+
// def adaptInfo(actual: Type, expected: Type, covariant: Boolean): String =
621+
// val (l, r) = if covariant then (actual, expected) else (expected, actual)
622+
// i"adapting $l ~~> $r"
623+
624+
def adapt(actual: Type, expected: Type, covariant: Boolean, boxed: Boolean): Type =
625+
val (actual1, cs1) = adapt1(actual, expected, covariant, boxed)
626+
cs1 map { cs1 =>
627+
actual1 match
628+
case CapturingType(parent, cs0) =>
629+
parent.derivedCapturingType(parent, cs0 ++ cs1.asConst)
630+
case _ =>
631+
CapturingType(actual1, cs1.asConst)
632+
} getOrElse actual1
633+
634+
def adapt1(actual: Type, expected: Type, covariant: Boolean, boxed: Boolean): (Type, Option[CaptureSet]) =
635+
actual.dealias match
636+
case actual @ CapturingType(parent, refs) =>
637+
if actual.isBoxed != expected.isBoxedCapturing then
638+
val isUnbox = covariant == actual.isBoxed
639+
val (parent1, cs1) = adapt1(parent, expected, covariant, boxed = !isUnbox)
640+
641+
val criticalSet = // the set which is not allowed to have `*`
642+
if covariant then refs // can't box with `*`
643+
else expected.captureSet // can't unbox with `*`
644+
if criticalSet.isUniversal then
645+
// We can't box/unbox the universal capability. Leave `actual` as it is
646+
// so we get an error in checkConforms. This tends to give better error
647+
// messages than disallowing the root capability in `criticalSet`.
648+
capt.println(i"cannot box/unbox $actual vs $expected")
649+
(actual, None)
650+
else
651+
// Disallow future addition of `*` to `criticalSet`.
652+
criticalSet.disallowRootCapability { () =>
653+
report.error(
654+
em"""$actual cannot be box-converted to $expected
655+
|since one of their capture sets contains the root capability `*`""",
656+
pos)
657+
}
658+
if isUnbox then markFree(refs, pos)
659+
val tp1 = CapturingType(parent1, cs1 map { refs ++ _ } getOrElse refs, boxed = !actual.isBoxed)
660+
(tp1, None)
622661
else
623-
// Disallow future addition of `*` to `criticalSet`.
624-
criticalSet.disallowRootCapability { () =>
625-
report.error(
626-
em"""$actual cannot be box-converted to $expected
627-
|since one of their capture sets contains the root capability `*`""",
628-
pos)
629-
}
630-
if covariant == actual.isBoxed then markFree(refs, pos)
631-
CapturingType(parent1, refs, boxed = !actual.isBoxed)
632-
else
633-
actual.derivedCapturingType(parent1, refs)
634-
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
635-
adaptFun(actual, args.init, args.last, expected, covariant,
636-
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
637-
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
638-
// TODO Find a way to combine handling of generic and dependent function types (here and elsewhere)
639-
adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant,
640-
(aargs1, ares1) =>
641-
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
642-
.toFunctionType(isJava = false, alwaysDependent = true))
643-
case _ => actual
662+
val (parent1, cs1) = adapt1(parent, expected, covariant, boxed = false)
663+
val refs1 = cs1.map(refs ++ _.asConst).getOrElse(refs)
664+
val tp1 = actual.derivedCapturingType(parent1, refs1)
665+
(tp1, None)
666+
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
667+
adaptFun(actual, args.init, args.last, expected, covariant, boxed,
668+
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
669+
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
670+
// TODO Find a way to combine handling of generic and dependent function types (here and elsewhere)
671+
adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, boxed,
672+
(aargs1, ares1) =>
673+
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
674+
.toFunctionType(isJava = false, alwaysDependent = true))
675+
case _ => (actual, None)
676+
644677

645678
var actualw = actual.widenDealias
646679
actual match
@@ -651,7 +684,7 @@ class CheckCaptures extends Recheck, SymTransformer:
651684
// given `a: C T`, improve `C T` to `{a} T`
652685
case _ =>
653686
case _ =>
654-
val adapted = adapt(actualw, expected, covariant = true)
687+
val adapted = adapt(actualw, expected, covariant = true, boxed = false)
655688
if adapted ne actualw then
656689
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
657690
adapted

0 commit comments

Comments
 (0)