Skip to content

Commit 21d0e27

Browse files
author
Aleksander Boruch-Gruszecki
committed
Handle TypeVar instantiation
Since GADTMap is re-initialised each time it is used, it's not necessary to care about avoiding instantiations for shared TypeVars.
1 parent bfc5ff3 commit 21d0e27

File tree

6 files changed

+146
-33
lines changed

6 files changed

+146
-33
lines changed

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 100 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -713,16 +713,27 @@ object Contexts {
713713
def addBound(sym: Symbol, bound: Type, isUpper: Boolean)(implicit ctx: Context): Boolean
714714
def bounds(sym: Symbol)(implicit ctx: Context): TypeBounds
715715
def contains(sym: Symbol)(implicit ctx: Context): Boolean
716+
def debugBoundsDescription(implicit ctx: Context): String
716717
def derived: GADTMap
717718
}
718719

719720
final class SmartGADTMap(
720721
private[this] var myConstraint: Constraint = new OrderingConstraint(SimpleIdentityMap.Empty, SimpleIdentityMap.Empty, SimpleIdentityMap.Empty),
721722
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
723724
) extends GADTMap with ConstraintHandling {
724725
import dotty.tools.dotc.config.Printers.gadts
725726

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+
726737
// TODO: dirty kludge - should this class be an inner class of TyperState instead?
727738
private[this] var myCtx: Context = null
728739
implicit override def ctx = myCtx
@@ -750,13 +761,12 @@ object Contexts {
750761
val poly = PolyType(DepParamName.fresh(sym.name.toTypeName) :: Nil)(
751762
pt => TypeBounds.empty :: Nil,
752763
pt => defn.AnyType)
753-
// null out creatorState as a precaution
754764
new TypeVar(poly.paramRefs.head, creatorState = null)
755765
}
756766
gadts.println(i"GADTMap: created tvar $sym -> $res")
757767
constraint = constraint.add(res.origin.binder, res :: Nil)
758768
mapping = mapping.updated(sym, res)
759-
reverseMapping = reverseMapping.updated(res, sym)
769+
reverseMapping = reverseMapping.updated(res.origin, sym)
760770
res
761771
}
762772
res
@@ -765,26 +775,93 @@ object Contexts {
765775
override def addEmptyBounds(sym: Symbol)(implicit ctx: Context): Unit = tvar(sym)
766776

767777
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+
768833
def isEmptyBounds(tp: Type) = tp match {
769834
case TypeBounds(lo, hi) => (lo eq defn.NothingType) && (hi eq defn.AnyType)
770835
case _ => false
771836
}
772837

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+
}
777848

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+
}
780857

781858
val tvarBound = (new TypeVarInsertingMap)(bound)
782859
val res = tvarBound match {
783860
case boundTvar: TypeVar =>
784-
if (boundTvar eq symTvar) true else doAddOrdering(boundTvar.origin)
861+
doAddBound(boundTvar)
785862
// hack to normalize T and T[_]
786863
case AppliedType(boundTvar: TypeVar, args) if args forall isEmptyBounds =>
787-
doAddOrdering(boundTvar.origin)
864+
doAddBound(boundTvar)
788865
case tp => doAddBound(tp)
789866
}
790867

@@ -800,12 +877,10 @@ object Contexts {
800877
mapping(sym) match {
801878
case null => null
802879
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 )")
809884
res
810885
}
811886
}
@@ -831,17 +906,15 @@ object Contexts {
831906
private final class TypeVarRemovingMap extends TypeMap {
832907
override def apply(tp: Type): Type = tp match {
833908
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
841912
}
842913
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+
}
845918
case _ =>
846919
mapOver(tp)
847920
}
@@ -853,6 +926,7 @@ object Contexts {
853926
override def addBound(sym: Symbol, bound: Type, isUpper: Boolean)(implicit ctx: Context): Boolean = unsupported("EmptyGADTMap.addBound")
854927
override def bounds(sym: Symbol)(implicit ctx: Context): TypeBounds = null
855928
override def contains(sym: Symbol)(implicit ctx: Context) = false
929+
override def debugBoundsDescription(implicit ctx: Context): String = "EmptyGADTMap"
856930
override def derived = new SmartGADTMap
857931
}
858932
}

compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
325325
private def order(current: This, param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This =
326326
if (param1 == param2 || current.isLess(param1, param2)) this
327327
else {
328-
assert(contains(param1))
329-
assert(contains(param2))
328+
assert(contains(param1), i"$param1")
329+
assert(contains(param2), i"$param2")
330330
val newUpper = param2 :: exclusiveUpper(param2, param1)
331331
val newLower = param1 :: exclusiveLower(param1, param2)
332332
val current1 = (current /: newLower)(upperLens.map(this, _, _, newUpper ::: _))

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3529,7 +3529,7 @@ object Types {
35293529
private[core] def inst: Type = myInst
35303530
private[core] def inst_=(tp: Type): Unit = {
35313531
myInst = tp
3532-
if (tp.exists) {
3532+
if (tp.exists && (owningState ne null)) {
35333533
owningState.get.ownedVars -= this
35343534
owningState = null // no longer needed; null out to avoid a memory leak
35353535
}
@@ -3538,13 +3538,14 @@ object Types {
35383538
/** The state owning the variable. This is at first `creatorState`, but it can
35393539
* be changed to an enclosing state on a commit.
35403540
*/
3541-
private[core] var owningState: WeakReference[TyperState] = new WeakReference(creatorState)
3541+
private[core] var owningState: WeakReference[TyperState] =
3542+
if (creatorState == null) null else new WeakReference(creatorState)
35423543

35433544
/** The instance type of this variable, or NoType if the variable is currently
35443545
* uninstantiated
35453546
*/
35463547
def instanceOpt(implicit ctx: Context): Type =
3547-
if (inst.exists) inst else ctx.typerState.instType(this)
3548+
if (inst.exists) inst else ctx.typeComparer.instType(this)
35483549

35493550
/** Is the variable already instantiated? */
35503551
def isInstantiated(implicit ctx: Context): Boolean = instanceOpt.exists

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,11 @@ object Inferencing {
206206
}
207207

208208
val widePt = if (ctx.scala2Mode || refinementIsInvariant(tp)) pt else widenVariantParams(pt)
209-
tp <:< widePt
209+
import dotty.tools.dotc.config.Printers.gadts
210+
gadts.println(i"constraining pattern type $tp <:< $widePt")
211+
val res = tp <:< widePt
212+
gadts.println(i"constrained pattern type $tp <:< $widePt = $res\n${ctx.gadt.debugBoundsDescription}")
213+
res
210214
}
211215

212216
/** The list of uninstantiated type variables bound by some prefix of type `T` which
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
object one {
1+
object i4075 {
22
case class One[T](fst: T)
33
def bad[T](e: One[T]) = e match {
44
case foo: One[a] =>
55
val t: T = e.fst
66
// val nok: Nothing = t // should not compile
77
val ok: a = t // does compile
8+
One(ok)
89
}
10+
11+
val one: One[Int] = bad(One(0))
912
}

tests/gadt/i4471.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
object i4471 {
2+
sealed trait Shuffle[A1, A2] {
3+
def andThen[A3](that: Shuffle[A2, A3]): Shuffle[A1, A3] = AndThen(this, that)
4+
}
5+
6+
case class Id[A]() extends Shuffle[A, A]
7+
case class Swap[A, B]() extends Shuffle[(A, B), (B, A)]
8+
case class AssocLR[A, B, C]() extends Shuffle[((A, B), C), (A, (B, C))]
9+
case class AssocRL[A, B, C]() extends Shuffle[(A, (B, C)), ((A, B), C)]
10+
case class Par[A1, B1, A2, B2](_1: Shuffle[A1, B1], _2: Shuffle[A2, B2]) extends Shuffle[(A1, A2), (B1, B2)]
11+
case class AndThen[A1, A2, A3](_1: Shuffle[A1, A2], _2: Shuffle[A2, A3]) extends Shuffle[A1, A3]
12+
13+
def rewrite3[A1, A2, A3, A4](
14+
op1: Shuffle[A1, A2],
15+
op2: Shuffle[A2, A3],
16+
op3: Shuffle[A3, A4]
17+
): Option[Shuffle[A1, A4]] = (op1, op2, op3) match {
18+
case (
19+
_: Swap[x, y],
20+
_: AssocRL[u, v, w],
21+
op3_ : Par[p1, q1, p2, q2]
22+
) => op3_ match {
23+
case Par(_: Swap[r, s], _: Id[p2_]) =>
24+
Some(
25+
AssocLR[v, w, u]() andThen Par(Id[v](), Swap[w, u]()) andThen AssocRL[v, u, w]()
26+
)
27+
case _ => None
28+
}
29+
case _ => None
30+
}
31+
}

0 commit comments

Comments
 (0)