diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 077d345d792d..77363a165f64 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -656,7 +656,7 @@ class CheckCaptures extends Recheck, SymTransformer: expected /** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions - * + * * @param alwaysConst always make capture set variables constant after adaptation */ def adaptBoxed(actual: Type, expected: Type, pos: SrcPos, alwaysConst: Boolean = false)(using Context): Type = @@ -721,61 +721,52 @@ class CheckCaptures extends Recheck, SymTransformer: val arrow = if covariant then "~~>" else "<~~" i"adapting $actual $arrow $expected" - /** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed), - * where `tp0` is not a capturing type. - * - * If `tp` is a nested capturing type, the return tuple always represents - * the innermost capturing type. The outer capture annotations can be - * reconstructed with the returned function. - */ - def destructCapturingType(tp: Type, reconstruct: Type => Type = x => x): ((Type, CaptureSet, Boolean), Type => Type) = - tp.dealias match - case tp @ CapturingType(parent, cs) => - if parent.dealias.isCapturingType then - destructCapturingType(parent, res => reconstruct(tp.derivedCapturingType(res, cs))) - else - ((parent, cs, tp.isBoxed), reconstruct) - case actual => - val res = if tp.isFromJavaObject then tp else actual - ((res, CaptureSet(), false), reconstruct) - def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) { if expected.isInstanceOf[WildcardType] then actual else - val ((parent, cs, actualIsBoxed), recon) = destructCapturingType(actual) - - val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing - val insertBox = needsAdaptation && covariant != actualIsBoxed - - val (parent1, cs1) = parent match { + // Decompose the actual type into the inner shape type, the capture set and the box status + val styp = if actual.isFromJavaObject then actual else actual.stripCapturing + val cs = actual.captureSet + val boxed = actual.isBoxedCapturing + + // A box/unbox should be inserted, if the actual box status mismatches with the expectation + val needsAdaptation = boxed != expected.isBoxedCapturing + // Whether to insert a box or an unbox? + val insertBox = needsAdaptation && covariant != boxed + + // Adapt the inner shape type: get the adapted shape type, and the capture set leaked during adaptation + val (styp1, leaked) = styp match { case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) => - val (parent1, leaked) = adaptFun(parent, args.init, args.last, expected, covariant, insertBox, + adaptFun(actual, args.init, args.last, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1)) - (parent1, leaked ++ cs) case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) => // TODO Find a way to combine handling of generic and dependent function types (here and elsewhere) - val (parent1, leaked) = adaptFun(parent, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, + adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, (aargs1, ares1) => rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1) .toFunctionType(isJava = false, alwaysDependent = true)) - (parent1, leaked ++ cs) case actual: MethodType => - val (parent1, leaked) = adaptFun(parent, actual.paramInfos, actual.resType, expected, covariant, insertBox, + adaptFun(actual, actual.paramInfos, actual.resType, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedLambdaType(paramInfos = aargs1, resType = ares1)) - (parent1, leaked ++ cs) case actual @ RefinedType(p, nme, rinfo: PolyType) if defn.isFunctionOrPolyType(actual) => - val (parent1, leaked) = adaptTypeFun(parent, rinfo.resType, expected, covariant, insertBox, + adaptTypeFun(actual, rinfo.resType, expected, covariant, insertBox, ares1 => val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1) val actual1 = actual.derivedRefinedType(p, nme, rinfo1) actual1 ) - (parent1, leaked ++ cs) case _ => - (parent, cs) + (styp, CaptureSet()) } + // Capture set of the term after adaptation + val cs1 = cs ++ leaked + + // Compute the adapted type + def adaptedType(resultBoxed: Boolean) = + styp1.capturing(if alwaysConst then CaptureSet(cs1.elems) else cs1).forceBoxStatus(resultBoxed) + if needsAdaptation then val criticalSet = // the set which is not allowed to have `*` if covariant then cs1 // can't box with `*` @@ -797,9 +788,9 @@ class CheckCaptures extends Recheck, SymTransformer: } if !insertBox then // unboxing markFree(criticalSet, pos) - recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, !actualIsBoxed)) + adaptedType(!boxed) else - recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, actualIsBoxed)) + adaptedType(boxed) } var actualw = actual.widenDealias