@@ -713,16 +713,27 @@ object Contexts {
713
713
def addBound (sym : Symbol , bound : Type , isUpper : Boolean )(implicit ctx : Context ): Boolean
714
714
def bounds (sym : Symbol )(implicit ctx : Context ): TypeBounds
715
715
def contains (sym : Symbol )(implicit ctx : Context ): Boolean
716
+ def debugBoundsDescription (implicit ctx : Context ): String
716
717
def derived : GADTMap
717
718
}
718
719
719
720
final class SmartGADTMap (
720
721
private [this ] var myConstraint : Constraint = new OrderingConstraint (SimpleIdentityMap .Empty , SimpleIdentityMap .Empty , SimpleIdentityMap .Empty ),
721
722
private [this ] var mapping : SimpleIdentityMap [Symbol , TypeVar ] = SimpleIdentityMap .Empty ,
722
- private [this ] var reverseMapping : SimpleIdentityMap [TypeVar , Symbol ] = SimpleIdentityMap .Empty
723
+ private [this ] var reverseMapping : SimpleIdentityMap [TypeParamRef , Symbol ] = SimpleIdentityMap .Empty
723
724
) extends GADTMap with ConstraintHandling {
724
725
import dotty .tools .dotc .config .Printers .gadts
725
726
727
+ override def debugBoundsDescription (implicit ctx : Context ): String = {
728
+ val sb = new mutable.StringBuilder
729
+ sb ++= constraint.show
730
+ sb += '\n '
731
+ mapping.foreachBinding { case (sym, _) =>
732
+ sb ++= i " $sym: ${bounds(sym)}\n "
733
+ }
734
+ sb.result
735
+ }
736
+
726
737
// TODO: dirty kludge - should this class be an inner class of TyperState instead?
727
738
private [this ] var myCtx : Context = null
728
739
implicit override def ctx = myCtx
@@ -750,13 +761,12 @@ object Contexts {
750
761
val poly = PolyType (DepParamName .fresh(sym.name.toTypeName) :: Nil )(
751
762
pt => TypeBounds .empty :: Nil ,
752
763
pt => defn.AnyType )
753
- // null out creatorState as a precaution
754
764
new TypeVar (poly.paramRefs.head, creatorState = null )
755
765
}
756
766
gadts.println(i " GADTMap: created tvar $sym -> $res" )
757
767
constraint = constraint.add(res.origin.binder, res :: Nil )
758
768
mapping = mapping.updated(sym, res)
759
- reverseMapping = reverseMapping.updated(res, sym)
769
+ reverseMapping = reverseMapping.updated(res.origin , sym)
760
770
res
761
771
}
762
772
res
@@ -765,26 +775,93 @@ object Contexts {
765
775
override def addEmptyBounds (sym : Symbol )(implicit ctx : Context ): Unit = tvar(sym)
766
776
767
777
override def addBound (sym : Symbol , bound : Type , isUpper : Boolean )(implicit ctx : Context ): Boolean = inCtx(ctx) {
778
+ @ annotation.tailrec def stripInst (tp : Type ): Type = tp match {
779
+ case tv : TypeVar =>
780
+ val inst = tv.inst orElse instType(tv)
781
+ if (inst.exists) stripInst(inst) else tv
782
+ case _ => tp
783
+ }
784
+
785
+ def cautiousSubtype (tp1 : Type , tp2 : Type , isSubtype : Boolean , allowNarrowing : Boolean = false ): Boolean = {
786
+ val externalizedTp1 = (new TypeVarRemovingMap )(tp1)
787
+ val externalizedTp2 = (new TypeVarRemovingMap )(tp2)
788
+
789
+ def descr = {
790
+ def op = s " frozen_ ${if (isSubtype) " <:<" else " >:>" }"
791
+ def flex = s " GADTFlexible= $allowNarrowing"
792
+ i " $tp1 $op $tp2\n\t $externalizedTp1 $op $externalizedTp2 ( $flex) "
793
+ }
794
+ // gadts.println(descr)
795
+
796
+ val outerCtx = ctx
797
+ val res = {
798
+ implicit val ctx : Context =
799
+ if (allowNarrowing) outerCtx else outerCtx.fresh.retractMode(Mode .GADTflexible )
800
+
801
+ // TypeComparer.explain[Boolean](gadts.println) { implicit ctx =>
802
+ if (isSubtype) externalizedTp1 frozen_<:< externalizedTp2
803
+ else externalizedTp2 frozen_<:< externalizedTp1
804
+ // }
805
+ }
806
+ gadts.println(i " $descr = $res" )
807
+ res
808
+ }
809
+
810
+ val symTvar : TypeVar = stripInst(tvar(sym)) match {
811
+ case tv : TypeVar => tv
812
+ case inst =>
813
+ gadts.println(i " instantiated: $sym -> $inst" )
814
+ return cautiousSubtype(inst, bound, isSubtype = isUpper, allowNarrowing = true )
815
+ }
816
+
817
+ def doAddBound (bound : Type ): Boolean = {
818
+ val res = stripInst(bound) match {
819
+ case boundTvar : TypeVar =>
820
+ if (boundTvar eq symTvar) true
821
+ else if (isUpper) addLess(symTvar.origin, boundTvar.origin)
822
+ else addLess(boundTvar.origin, symTvar.origin)
823
+ case bound =>
824
+ if (cautiousSubtype(symTvar, bound, isSubtype = ! isUpper)) { instantiate(symTvar, bound); true }
825
+ else if (isUpper) addUpperBound(symTvar.origin, bound)
826
+ else addLowerBound(symTvar.origin, bound)
827
+ }
828
+
829
+ vacuum()
830
+ res
831
+ }
832
+
768
833
def isEmptyBounds (tp : Type ) = tp match {
769
834
case TypeBounds (lo, hi) => (lo eq defn.NothingType ) && (hi eq defn.AnyType )
770
835
case _ => false
771
836
}
772
837
773
- val symTvar = tvar(sym)
774
-
775
- def doAddOrdering (bound : TypeParamRef ) =
776
- if (isUpper) addLess(symTvar.origin, bound) else addLess(bound, symTvar.origin)
838
+ def instantiate (tv : TypeVar , tp : Type ): Unit = {
839
+ // instantiating one TypeVar to another makes us actually lose information
840
+ // that is, we need to know that both TypeVars are equal to another
841
+ // *and* be able to record further bounds on either one
842
+ if (tp.isInstanceOf [TypeVar ]) return
843
+ val externalizedTp = (new TypeVarRemovingMap )(tp)
844
+ gadts.println(i " instantiating $tv to $externalizedTp ( $tp ) " )
845
+ tv.inst = externalizedTp
846
+ constraint = constraint.replace(tv.origin, externalizedTp)
847
+ }
777
848
778
- def doAddBound (bound : Type ) =
779
- if (isUpper) addUpperBound(symTvar.origin, bound) else addLowerBound(symTvar.origin, bound)
849
+ def vacuum (): Unit = {
850
+ constraint.foreachTypeVar { tv =>
851
+ if (! tv.inst.exists) {
852
+ val inst = instType(tv)
853
+ if (inst.exists) instantiate(tv, inst)
854
+ }
855
+ }
856
+ }
780
857
781
858
val tvarBound = (new TypeVarInsertingMap )(bound)
782
859
val res = tvarBound match {
783
860
case boundTvar : TypeVar =>
784
- if (boundTvar eq symTvar) true else doAddOrdering(boundTvar.origin )
861
+ doAddBound (boundTvar)
785
862
// hack to normalize T and T[_]
786
863
case AppliedType (boundTvar : TypeVar , args) if args forall isEmptyBounds =>
787
- doAddOrdering (boundTvar.origin )
864
+ doAddBound (boundTvar)
788
865
case tp => doAddBound(tp)
789
866
}
790
867
@@ -800,12 +877,10 @@ object Contexts {
800
877
mapping(sym) match {
801
878
case null => null
802
879
case tv =>
803
- val tb = constraint.fullBounds(tv.origin)
804
- val res = {
805
- val tm = new TypeVarRemovingMap
806
- tb.derivedTypeBounds(tm(tb.lo), tm(tb.hi))
807
- }
808
- gadts.println(i " gadt bounds $sym: $res\t ( $tv: $tb ) " )
880
+ val tb =
881
+ if (tv.inst.exists) TypeAlias (tv.inst) else constraint.fullBounds(tv.origin)
882
+ val res = (new TypeVarRemovingMap )(tb).asInstanceOf [TypeBounds ]
883
+ // gadts.println(i"gadt bounds $sym: $res\t( $tv: $tb )")
809
884
res
810
885
}
811
886
}
@@ -831,17 +906,15 @@ object Contexts {
831
906
private final class TypeVarRemovingMap extends TypeMap {
832
907
override def apply (tp : Type ): Type = tp match {
833
908
case tpr : TypeParamRef =>
834
- constraint.typeVarOfParam(tpr) match {
835
- case tv : TypeVar =>
836
- reverseMapping(tv).typeRef
837
- case unexpected =>
838
- // if we didn't get a TypeVar, it's likely to cause problems
839
- gadts.println(i " GADTMap: unexpected typeVarOfParam( $tpr) = ` $unexpected` ${unexpected.getClass}" )
840
- tpr
909
+ reverseMapping(tpr) match {
910
+ case null => tpr
911
+ case sym => sym.typeRef
841
912
}
842
913
case tv : TypeVar =>
843
- if (reverseMapping.contains(tv)) reverseMapping(tv).typeRef
844
- else tv
914
+ reverseMapping(tv.origin) match {
915
+ case null => tv
916
+ case sym => sym.typeRef
917
+ }
845
918
case _ =>
846
919
mapOver(tp)
847
920
}
@@ -853,6 +926,7 @@ object Contexts {
853
926
override def addBound (sym : Symbol , bound : Type , isUpper : Boolean )(implicit ctx : Context ): Boolean = unsupported(" EmptyGADTMap.addBound" )
854
927
override def bounds (sym : Symbol )(implicit ctx : Context ): TypeBounds = null
855
928
override def contains (sym : Symbol )(implicit ctx : Context ) = false
929
+ override def debugBoundsDescription (implicit ctx : Context ): String = " EmptyGADTMap"
856
930
override def derived = new SmartGADTMap
857
931
}
858
932
}
0 commit comments