@@ -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