Skip to content

Commit ecf174b

Browse files
committed
refactor box inference again
1 parent 3750dc6 commit ecf174b

File tree

1 file changed

+54
-52
lines changed

1 file changed

+54
-52
lines changed

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

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -595,84 +595,86 @@ class CheckCaptures extends Recheck, SymTransformer:
595595
* to `expected` type.
596596
* @param reconstruct how to rebuild the adapted function type
597597
*/
598-
def adaptFun(actual: Type, aargs: List[Type], ares: Type, expected: Type,
598+
def adaptFun(actualTp: (Type, CaptureSet), aargs: List[Type], ares: Type, expected: Type,
599599
covariant: Boolean, boxed: Boolean,
600-
reconstruct: (List[Type], Type) => Type): (Type, Some[CaptureSet]) =
600+
reconstruct: (List[Type], Type) => Type): (Type, CaptureSet) =
601+
val (actual, cs0) = actualTp
601602
val saved = curEnv
602603
curEnv = Env(curEnv.owner, CaptureSet.Var(), isBoxed = false, if boxed then null else curEnv)
603604

604605
try
605-
val (eargs, eres) = expected.dealias match
606+
val (eargs, eres) = trace(i"trying to dealias expected $expected", show = true) {expected.dealias} match
606607
case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres)
607608
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)
609+
val aargs1 = aargs.zipWithConserve(eargs){ (aarg, earg) => adapt(aarg, earg, !covariant) }
610+
val ares1 = adapt(ares, eres, covariant)
610611

611612
val resTp =
612613
if (ares1 eq ares) && (aargs1 eq aargs) then actual
613614
else reconstruct(aargs1, ares1)
614615

615616
curEnv.captured.asVar.markSolved()
616-
(resTp, Some(curEnv.captured))
617+
(resTp, curEnv.captured ++ cs0)
617618
finally
618619
curEnv = saved
619620

620621
// def adaptInfo(actual: Type, expected: Type, covariant: Boolean): String =
621622
// val (l, r) = if covariant then (actual, expected) else (expected, actual)
622623
// i"adapting $l ~~> $r"
623624

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)
661-
else
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)
625+
def adapt(actual: Type, expected: Type, covariant: Boolean): Type =
626+
val actualTp = actual match
627+
case actual @ CapturingType(parent, cs) =>
628+
(parent, cs, actual.isBoxed)
629+
case actual =>
630+
(actual, CaptureSet(), false)
631+
632+
val (parent1, cs1, isBoxed1) = adaptCapturingType(actualTp, expected, covariant)
633+
634+
CapturingType(parent1, cs1, isBoxed1)
635+
636+
def adaptCapturingType(actual: (Type, CaptureSet, Boolean), expected: Type, covariant: Boolean): (Type, CaptureSet, Boolean) =
637+
val (parent, cs, actualIsBoxed) = actual
638+
639+
val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing
640+
val insertBox = needsAdaptation && covariant != actualIsBoxed
641+
642+
val (parent1, cs1) = parent match {
666643
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
667-
adaptFun(actual, args.init, args.last, expected, covariant, boxed,
644+
adaptFun((parent, cs), args.init, args.last, expected, covariant, insertBox,
668645
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
669646
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
670647
// 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,
648+
adaptFun((parent, cs), rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox,
672649
(aargs1, ares1) =>
673650
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
674651
.toFunctionType(isJava = false, alwaysDependent = true))
675-
case _ => (actual, None)
652+
case _ => (parent, cs)
653+
}
654+
655+
if needsAdaptation then
656+
val criticalSet = // the set which is not allowed to have `*`
657+
if covariant then cs1 // can't box with `*`
658+
else expected.captureSet // can't unbox with `*`
659+
if criticalSet.isUniversal then
660+
// We can't box/unbox the universal capability. Leave `actual` as it is
661+
// so we get an error in checkConforms. This tends to give better error
662+
// messages than disallowing the root capability in `criticalSet`.
663+
capt.println(i"cannot box/unbox $actual vs $expected")
664+
actual
665+
else
666+
// Disallow future addition of `*` to `criticalSet`.
667+
criticalSet.disallowRootCapability { () =>
668+
report.error(
669+
em"""$actual cannot be box-converted to $expected
670+
|since one of their capture sets contains the root capability `*`""",
671+
pos)
672+
}
673+
if !insertBox then // unboxing
674+
markFree(cs1, pos)
675+
(parent1, cs1, !actualIsBoxed)
676+
else
677+
(parent1, cs1, actualIsBoxed)
676678

677679

678680
var actualw = actual.widenDealias
@@ -684,7 +686,7 @@ class CheckCaptures extends Recheck, SymTransformer:
684686
// given `a: C T`, improve `C T` to `{a} T`
685687
case _ =>
686688
case _ =>
687-
val adapted = adapt(actualw, expected, covariant = true, boxed = false)
689+
val adapted = adapt(actualw, expected, covariant = true)
688690
if adapted ne actualw then
689691
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
690692
adapted

0 commit comments

Comments
 (0)