Skip to content

Commit 69771cc

Browse files
authored
Merge pull request #5611 from dotty-staging/gadt-unification
Gadt unification
2 parents 2e17a6c + d1fa5d7 commit 69771cc

32 files changed

+999
-111
lines changed

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ object Printers {
2121
val dottydoc: Printer = noPrinter
2222
val exhaustivity: Printer = noPrinter
2323
val gadts: Printer = noPrinter
24+
val gadtsConstr: Printer = noPrinter
2425
val hk: Printer = noPrinter
2526
val implicits: Printer = noPrinter
2627
val implicitsDetailed: Printer = noPrinter

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

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ import config.Printers.{constr, typr}
1818
* By comparison: Constraint handlers are parts of type comparers and can use their functionality.
1919
* Constraint handlers update the current constraint as a side effect.
2020
*/
21-
trait ConstraintHandling {
21+
trait ConstraintHandling[AbstractContext] {
2222

23-
implicit val ctx: Context
23+
def constr_println(msg: => String): Unit = constr.println(msg)
24+
def typr_println(msg: => String): Unit = typr.println(msg)
2425

25-
protected def isSubType(tp1: Type, tp2: Type): Boolean
26-
protected def isSameType(tp1: Type, tp2: Type): Boolean
26+
implicit def ctx(implicit ac: AbstractContext): Context
2727

28-
val state: TyperState
29-
import state.constraint
28+
protected def isSubType(tp1: Type, tp2: Type)(implicit actx: AbstractContext): Boolean
29+
protected def isSameType(tp1: Type, tp2: Type)(implicit actx: AbstractContext): Boolean
30+
31+
protected def constraint: Constraint
32+
protected def constraint_=(c: Constraint): Unit
3033

3134
private[this] var addConstraintInvocations = 0
3235

@@ -50,7 +53,20 @@ trait ConstraintHandling {
5053
*/
5154
protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty
5255

53-
protected def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean): Boolean =
56+
/** Gives for each instantiated type var that does not yet have its `inst` field
57+
* set, the instance value stored in the constraint. Storing instances in constraints
58+
* is done only in a temporary way for contexts that may be retracted
59+
* without also retracting the type var as a whole.
60+
*/
61+
def instType(tvar: TypeVar): Type = constraint.entry(tvar.origin) match {
62+
case _: TypeBounds => NoType
63+
case tp: TypeParamRef =>
64+
var tvar1 = constraint.typeVarOfParam(tp)
65+
if (tvar1.exists) tvar1 else tp
66+
case tp => tp
67+
}
68+
69+
protected def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(implicit actx: AbstractContext): Boolean =
5470
!constraint.contains(param) || {
5571
def occursIn(bound: Type): Boolean = {
5672
val b = bound.dealias
@@ -100,34 +116,34 @@ trait ConstraintHandling {
100116

101117
private def location(implicit ctx: Context) = "" // i"in ${ctx.typerState.stateChainStr}" // use for debugging
102118

103-
protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = {
119+
protected def addUpperBound(param: TypeParamRef, bound: Type)(implicit actx: AbstractContext): Boolean = {
104120
def description = i"constraint $param <: $bound to\n$constraint"
105121
if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) {
106122
def msg = s"!!! instantiated to Nothing: $param, constraint = ${constraint.show}"
107123
if (Config.failOnInstantiationToNothing) assert(false, msg)
108124
else ctx.log(msg)
109125
}
110-
constr.println(i"adding $description$location")
126+
constr_println(i"adding $description$location")
111127
val lower = constraint.lower(param)
112128
val res =
113129
addOneBound(param, bound, isUpper = true) &&
114130
lower.forall(addOneBound(_, bound, isUpper = true))
115-
constr.println(i"added $description = $res$location")
131+
constr_println(i"added $description = $res$location")
116132
res
117133
}
118134

119-
protected def addLowerBound(param: TypeParamRef, bound: Type): Boolean = {
135+
protected def addLowerBound(param: TypeParamRef, bound: Type)(implicit actx: AbstractContext): Boolean = {
120136
def description = i"constraint $param >: $bound to\n$constraint"
121-
constr.println(i"adding $description")
137+
constr_println(i"adding $description")
122138
val upper = constraint.upper(param)
123139
val res =
124140
addOneBound(param, bound, isUpper = false) &&
125141
upper.forall(addOneBound(_, bound, isUpper = false))
126-
constr.println(i"added $description = $res$location")
142+
constr_println(i"added $description = $res$location")
127143
res
128144
}
129145

130-
protected def addLess(p1: TypeParamRef, p2: TypeParamRef): Boolean = {
146+
protected def addLess(p1: TypeParamRef, p2: TypeParamRef)(implicit actx: AbstractContext): Boolean = {
131147
def description = i"ordering $p1 <: $p2 to\n$constraint"
132148
val res =
133149
if (constraint.isLess(p2, p1)) unify(p2, p1)
@@ -136,20 +152,20 @@ trait ConstraintHandling {
136152
val up2 = p2 :: constraint.exclusiveUpper(p2, p1)
137153
val lo1 = constraint.nonParamBounds(p1).lo
138154
val hi2 = constraint.nonParamBounds(p2).hi
139-
constr.println(i"adding $description down1 = $down1, up2 = $up2$location")
155+
constr_println(i"adding $description down1 = $down1, up2 = $up2$location")
140156
constraint = constraint.addLess(p1, p2)
141157
down1.forall(addOneBound(_, hi2, isUpper = true)) &&
142158
up2.forall(addOneBound(_, lo1, isUpper = false))
143159
}
144-
constr.println(i"added $description = $res$location")
160+
constr_println(i"added $description = $res$location")
145161
res
146162
}
147163

148164
/** Make p2 = p1, transfer all bounds of p2 to p1
149165
* @pre less(p1)(p2)
150166
*/
151-
private def unify(p1: TypeParamRef, p2: TypeParamRef): Boolean = {
152-
constr.println(s"unifying $p1 $p2")
167+
private def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit actx: AbstractContext): Boolean = {
168+
constr_println(s"unifying $p1 $p2")
153169
assert(constraint.isLess(p1, p2))
154170
val down = constraint.exclusiveLower(p2, p1)
155171
val up = constraint.exclusiveUpper(p1, p2)
@@ -163,7 +179,7 @@ trait ConstraintHandling {
163179
}
164180

165181

166-
protected def isSubType(tp1: Type, tp2: Type, whenFrozen: Boolean): Boolean = {
182+
protected def isSubType(tp1: Type, tp2: Type, whenFrozen: Boolean)(implicit actx: AbstractContext): Boolean = {
167183
if (whenFrozen)
168184
isSubTypeWhenFrozen(tp1, tp2)
169185
else
@@ -182,13 +198,13 @@ trait ConstraintHandling {
182198
}
183199
}
184200

185-
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = inFrozenConstraint(isSubType(tp1, tp2))
186-
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = inFrozenConstraint(isSameType(tp1, tp2))
201+
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type)(implicit actx: AbstractContext): Boolean = inFrozenConstraint(isSubType(tp1, tp2))
202+
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type)(implicit actx: AbstractContext): Boolean = inFrozenConstraint(isSameType(tp1, tp2))
187203

188204
/** Test whether the lower bounds of all parameters in this
189205
* constraint are a solution to the constraint.
190206
*/
191-
protected final def isSatisfiable: Boolean =
207+
protected final def isSatisfiable(implicit actx: AbstractContext): Boolean =
192208
constraint.forallParams { param =>
193209
val TypeBounds(lo, hi) = constraint.entry(param)
194210
isSubType(lo, hi) || {
@@ -207,7 +223,7 @@ trait ConstraintHandling {
207223
* @return the instantiating type
208224
* @pre `param` is in the constraint's domain.
209225
*/
210-
final def approximation(param: TypeParamRef, fromBelow: Boolean): Type = {
226+
final def approximation(param: TypeParamRef, fromBelow: Boolean)(implicit actx: AbstractContext): Type = {
211227
val avoidParam = new TypeMap {
212228
override def stopAtStatic = true
213229
def avoidInArg(arg: Type): Type =
@@ -247,7 +263,7 @@ trait ConstraintHandling {
247263
case _: TypeBounds =>
248264
val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param)
249265
val inst = avoidParam(bound)
250-
typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
266+
typr_println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
251267
inst
252268
case inst =>
253269
assert(inst.exists, i"param = $param\nconstraint = $constraint")
@@ -261,7 +277,7 @@ trait ConstraintHandling {
261277
* 2. If `tp` is a union type, yet upper bound is not a union type,
262278
* approximate the union type from above by an intersection of all common base types.
263279
*/
264-
def widenInferred(tp: Type, bound: Type): Type = {
280+
def widenInferred(tp: Type, bound: Type)(implicit actx: AbstractContext): Type = {
265281
def isMultiSingleton(tp: Type): Boolean = tp.stripAnnots match {
266282
case tp: SingletonType => true
267283
case AndType(tp1, tp2) => isMultiSingleton(tp1) | isMultiSingleton(tp2)
@@ -294,7 +310,7 @@ trait ConstraintHandling {
294310
* a lower bound instantiation can be a singleton type only if the upper bound
295311
* is also a singleton type.
296312
*/
297-
def instanceType(param: TypeParamRef, fromBelow: Boolean): Type = {
313+
def instanceType(param: TypeParamRef, fromBelow: Boolean)(implicit actx: AbstractContext): Type = {
298314
val inst = approximation(param, fromBelow).simplified
299315
if (fromBelow) widenInferred(inst, constraint.fullUpperBound(param)) else inst
300316
}
@@ -309,7 +325,7 @@ trait ConstraintHandling {
309325
* Both `c1` and `c2` are required to derive from constraint `pre`, possibly
310326
* narrowing it with further bounds.
311327
*/
312-
protected final def subsumes(c1: Constraint, c2: Constraint, pre: Constraint): Boolean =
328+
protected final def subsumes(c1: Constraint, c2: Constraint, pre: Constraint)(implicit actx: AbstractContext): Boolean =
313329
if (c2 eq pre) true
314330
else if (c1 eq pre) false
315331
else {
@@ -323,7 +339,7 @@ trait ConstraintHandling {
323339
}
324340

325341
/** The current bounds of type parameter `param` */
326-
def bounds(param: TypeParamRef): TypeBounds = {
342+
def bounds(param: TypeParamRef)(implicit actx: AbstractContext): TypeBounds = {
327343
val e = constraint.entry(param)
328344
if (e.exists) e.bounds
329345
else {
@@ -337,7 +353,7 @@ trait ConstraintHandling {
337353
* and propagate all bounds.
338354
* @param tvars See Constraint#add
339355
*/
340-
def addToConstraint(tl: TypeLambda, tvars: List[TypeVar]): Boolean =
356+
def addToConstraint(tl: TypeLambda, tvars: List[TypeVar])(implicit actx: AbstractContext): Boolean =
341357
checkPropagated(i"initialized $tl") {
342358
constraint = constraint.add(tl, tvars)
343359
tl.paramRefs.forall { param =>
@@ -346,7 +362,7 @@ trait ConstraintHandling {
346362
val lower = constraint.lower(param)
347363
val upper = constraint.upper(param)
348364
if (lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass) ||
349-
upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $tl")
365+
upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr_println(i"INIT*** $tl")
350366
lower.forall(addOneBound(_, bounds.hi, isUpper = true)) &&
351367
upper.forall(addOneBound(_, bounds.lo, isUpper = false))
352368
case _ =>
@@ -365,7 +381,7 @@ trait ConstraintHandling {
365381
* This holds if `TypeVarsMissContext` is set unless `param` is a part
366382
* of a MatchType that is currently normalized.
367383
*/
368-
final def assumedTrue(param: TypeParamRef): Boolean =
384+
final def assumedTrue(param: TypeParamRef)(implicit actx: AbstractContext): Boolean =
369385
ctx.mode.is(Mode.TypevarsMissContext) && (caseLambda `ne` param.binder)
370386

371387
/** Add constraint `param <: bound` if `fromBelow` is false, `param >: bound` otherwise.
@@ -375,7 +391,7 @@ trait ConstraintHandling {
375391
* not be AndTypes and lower bounds may not be OrTypes. This is assured by the
376392
* way isSubType is organized.
377393
*/
378-
protected def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean): Boolean = {
394+
protected def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean)(implicit actx: AbstractContext): Boolean = {
379395
def description = i"constr $param ${if (fromBelow) ">:" else "<:"} $bound:\n$constraint"
380396
//checkPropagated(s"adding $description")(true) // DEBUG in case following fails
381397
checkPropagated(s"added $description") {
@@ -491,7 +507,7 @@ trait ConstraintHandling {
491507
}
492508

493509
/** Instantiate `param` to `tp` if the constraint stays satisfiable */
494-
protected def tryInstantiate(param: TypeParamRef, tp: Type): Boolean = {
510+
protected def tryInstantiate(param: TypeParamRef, tp: Type)(implicit actx: AbstractContext): Boolean = {
495511
val saved = constraint
496512
constraint =
497513
if (addConstraint(param, tp, fromBelow = true) &&
@@ -501,7 +517,7 @@ trait ConstraintHandling {
501517
}
502518

503519
/** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */
504-
def checkPropagated(msg: => String)(result: Boolean): Boolean = {
520+
def checkPropagated(msg: => String)(result: Boolean)(implicit actx: AbstractContext): Boolean = {
505521
if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) {
506522
inFrozenConstraint {
507523
for (p <- constraint.domainParams) {

0 commit comments

Comments
 (0)