Skip to content

Commit 7e2f80b

Browse files
authored
Simplify box adaptation (#16845)
This PR simplifies the box adaptation implementation. 1. Instead of adding a `destructCapturingType` function, use the existing methods to analyze and decompose the input type. It should work well with annotated types. 2. Add explanation on the implementation.
2 parents 4b0fa97 + 790af81 commit 7e2f80b

File tree

1 file changed

+27
-36
lines changed

1 file changed

+27
-36
lines changed

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

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ class CheckCaptures extends Recheck, SymTransformer:
656656
expected
657657

658658
/** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions
659-
*
659+
*
660660
* @param alwaysConst always make capture set variables constant after adaptation
661661
*/
662662
def adaptBoxed(actual: Type, expected: Type, pos: SrcPos, alwaysConst: Boolean = false)(using Context): Type =
@@ -721,61 +721,52 @@ class CheckCaptures extends Recheck, SymTransformer:
721721
val arrow = if covariant then "~~>" else "<~~"
722722
i"adapting $actual $arrow $expected"
723723

724-
/** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed),
725-
* where `tp0` is not a capturing type.
726-
*
727-
* If `tp` is a nested capturing type, the return tuple always represents
728-
* the innermost capturing type. The outer capture annotations can be
729-
* reconstructed with the returned function.
730-
*/
731-
def destructCapturingType(tp: Type, reconstruct: Type => Type = x => x): ((Type, CaptureSet, Boolean), Type => Type) =
732-
tp.dealias match
733-
case tp @ CapturingType(parent, cs) =>
734-
if parent.dealias.isCapturingType then
735-
destructCapturingType(parent, res => reconstruct(tp.derivedCapturingType(res, cs)))
736-
else
737-
((parent, cs, tp.isBoxed), reconstruct)
738-
case actual =>
739-
val res = if tp.isFromJavaObject then tp else actual
740-
((res, CaptureSet(), false), reconstruct)
741-
742724
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) {
743725
if expected.isInstanceOf[WildcardType] then actual
744726
else
745-
val ((parent, cs, actualIsBoxed), recon) = destructCapturingType(actual)
746-
747-
val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing
748-
val insertBox = needsAdaptation && covariant != actualIsBoxed
749-
750-
val (parent1, cs1) = parent match {
727+
// Decompose the actual type into the inner shape type, the capture set and the box status
728+
val styp = if actual.isFromJavaObject then actual else actual.stripCapturing
729+
val cs = actual.captureSet
730+
val boxed = actual.isBoxedCapturing
731+
732+
// A box/unbox should be inserted, if the actual box status mismatches with the expectation
733+
val needsAdaptation = boxed != expected.isBoxedCapturing
734+
// Whether to insert a box or an unbox?
735+
val insertBox = needsAdaptation && covariant != boxed
736+
737+
// Adapt the inner shape type: get the adapted shape type, and the capture set leaked during adaptation
738+
val (styp1, leaked) = styp match {
751739
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
752-
val (parent1, leaked) = adaptFun(parent, args.init, args.last, expected, covariant, insertBox,
740+
adaptFun(actual, args.init, args.last, expected, covariant, insertBox,
753741
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
754-
(parent1, leaked ++ cs)
755742
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
756743
// TODO Find a way to combine handling of generic and dependent function types (here and elsewhere)
757-
val (parent1, leaked) = adaptFun(parent, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox,
744+
adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox,
758745
(aargs1, ares1) =>
759746
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
760747
.toFunctionType(isJava = false, alwaysDependent = true))
761-
(parent1, leaked ++ cs)
762748
case actual: MethodType =>
763-
val (parent1, leaked) = adaptFun(parent, actual.paramInfos, actual.resType, expected, covariant, insertBox,
749+
adaptFun(actual, actual.paramInfos, actual.resType, expected, covariant, insertBox,
764750
(aargs1, ares1) =>
765751
actual.derivedLambdaType(paramInfos = aargs1, resType = ares1))
766-
(parent1, leaked ++ cs)
767752
case actual @ RefinedType(p, nme, rinfo: PolyType) if defn.isFunctionOrPolyType(actual) =>
768-
val (parent1, leaked) = adaptTypeFun(parent, rinfo.resType, expected, covariant, insertBox,
753+
adaptTypeFun(actual, rinfo.resType, expected, covariant, insertBox,
769754
ares1 =>
770755
val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1)
771756
val actual1 = actual.derivedRefinedType(p, nme, rinfo1)
772757
actual1
773758
)
774-
(parent1, leaked ++ cs)
775759
case _ =>
776-
(parent, cs)
760+
(styp, CaptureSet())
777761
}
778762

763+
// Capture set of the term after adaptation
764+
val cs1 = cs ++ leaked
765+
766+
// Compute the adapted type
767+
def adaptedType(resultBoxed: Boolean) =
768+
styp1.capturing(if alwaysConst then CaptureSet(cs1.elems) else cs1).forceBoxStatus(resultBoxed)
769+
779770
if needsAdaptation then
780771
val criticalSet = // the set which is not allowed to have `*`
781772
if covariant then cs1 // can't box with `*`
@@ -797,9 +788,9 @@ class CheckCaptures extends Recheck, SymTransformer:
797788
}
798789
if !insertBox then // unboxing
799790
markFree(criticalSet, pos)
800-
recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, !actualIsBoxed))
791+
adaptedType(!boxed)
801792
else
802-
recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, actualIsBoxed))
793+
adaptedType(boxed)
803794
}
804795

805796
var actualw = actual.widenDealias

0 commit comments

Comments
 (0)