@@ -595,84 +595,86 @@ class CheckCaptures extends Recheck, SymTransformer:
595
595
* to `expected` type.
596
596
* @param reconstruct how to rebuild the adapted function type
597
597
*/
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 ,
599
599
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
601
602
val saved = curEnv
602
603
curEnv = Env (curEnv.owner, CaptureSet .Var (), isBoxed = false , if boxed then null else curEnv)
603
604
604
605
try
605
- val (eargs, eres) = expected.dealias match
606
+ val (eargs, eres) = trace( i " trying to dealias expected $expected " , show = true ) {expected .dealias} match
606
607
case defn.FunctionOf (eargs, eres, _, _) => (eargs, eres)
607
608
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)
610
611
611
612
val resTp =
612
613
if (ares1 eq ares) && (aargs1 eq aargs) then actual
613
614
else reconstruct(aargs1, ares1)
614
615
615
616
curEnv.captured.asVar.markSolved()
616
- (resTp, Some ( curEnv.captured) )
617
+ (resTp, curEnv.captured ++ cs0 )
617
618
finally
618
619
curEnv = saved
619
620
620
621
// def adaptInfo(actual: Type, expected: Type, covariant: Boolean): String =
621
622
// val (l, r) = if covariant then (actual, expected) else (expected, actual)
622
623
// i"adapting $l ~~> $r"
623
624
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 {
666
643
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 ,
668
645
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
669
646
case actual @ RefinedType (_, _, rinfo : MethodType ) if defn.isFunctionType(actual) =>
670
647
// 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 ,
672
649
(aargs1, ares1) =>
673
650
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
674
651
.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)
676
678
677
679
678
680
var actualw = actual.widenDealias
@@ -684,7 +686,7 @@ class CheckCaptures extends Recheck, SymTransformer:
684
686
// given `a: C T`, improve `C T` to `{a} T`
685
687
case _ =>
686
688
case _ =>
687
- val adapted = adapt(actualw, expected, covariant = true , boxed = false )
689
+ val adapted = adapt(actualw, expected, covariant = true )
688
690
if adapted ne actualw then
689
691
capt.println(i " adapt boxed $actual vs $expected ===> $adapted" )
690
692
adapted
0 commit comments