Skip to content

Commit 7830069

Browse files
committed
Refactor Mode.GADTFlexible and TypeComparer.frozenGadt
1 parent 3902ed2 commit 7830069

File tree

5 files changed

+27
-22
lines changed

5 files changed

+27
-22
lines changed

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ object Mode {
4949
/** We are in a pattern alternative */
5050
val InPatternAlternative: Mode = newMode(7, "InPatternAlternative")
5151

52-
/** Infer GADT constraints during type comparisons `A <:< B` */
53-
val GADTflexible: Mode = newMode(8, "GADTflexible")
52+
/** Make subtyping checks instead infer constraints necessarily following from given subtyping relation.
53+
*
54+
* This enables changing [[GadtConstraint]] and alters how [[TypeComparer]] approximates constraints.
55+
*/
56+
val GadtConstraintInference: Mode = newMode(8, "GadtConstraintInference")
5457

5558
/** Assume -language:strictEquality */
5659
val StrictEquality: Mode = newMode(9, "StrictEquality")

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

+18-16
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,11 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
141141
*/
142142
private [this] var leftRoot: Type = _
143143

144-
/** Are we forbidden from recording GADT constraints? */
144+
/** Are we forbidden from recording GADT constraints?
145+
*
146+
* This flag is set when we're already in [[Mode.GadtConstraintInference]],
147+
* to signify that we temporarily cannot record any GADT constraints.
148+
*/
145149
private[this] var frozenGadt = false
146150

147151
protected def isSubType(tp1: Type, tp2: Type, a: ApproxState): Boolean = {
@@ -846,7 +850,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
846850
isSubType(tycon1.prefix, tycon2.prefix) && {
847851
// check both tycons to deal with the case when they are equal b/c of GADT constraint
848852
val tyconIsInjective = tycon1sym.isClass || tycon2sym.isClass
849-
isSubArgs(args1, args2, tp1, tparams, inferGadtBounds = tyconIsInjective)
853+
def checkSubArgs() = isSubArgs(args1, args2, tp1, tparams)
854+
// we only record GADT constraints if tycon is guaranteed to be injective
855+
if (tyconIsInjective) checkSubArgs()
856+
else {
857+
val savedFrozenGadt = frozenGadt
858+
frozenGadt = true
859+
try checkSubArgs() finally frozenGadt = savedFrozenGadt
860+
}
850861
}
851862
if (res && touchedGADTs) GADTused = true
852863
res
@@ -1103,7 +1114,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
11031114
* @param tp1 The applied type containing `args1`
11041115
* @param tparams2 The type parameters of the type constructor applied to `args2`
11051116
*/
1106-
def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams2: List[ParamInfo], inferGadtBounds: Boolean = false): Boolean = {
1117+
def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams2: List[ParamInfo]): Boolean = {
11071118
/** The bounds of parameter `tparam`, where all references to type paramneters
11081119
* are replaced by corresponding arguments (or their approximations in the case of
11091120
* wildcard arguments).
@@ -1167,17 +1178,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
11671178
case arg1: TypeBounds =>
11681179
compareCaptured(arg1, arg2)
11691180
case _ =>
1170-
def isSub(tp: Type, pt: Type): Boolean = {
1171-
if (inferGadtBounds) isSubType(tp, pt)
1172-
else {
1173-
val savedFrozenGadt = frozenGadt
1174-
frozenGadt = true
1175-
try isSubType(tp, pt) finally frozenGadt = savedFrozenGadt
1176-
}
1177-
}
1178-
1179-
(v > 0 || isSub(arg2, arg1)) &&
1180-
(v < 0 || isSub(arg1, arg2))
1181+
(v > 0 || isSubType(arg2, arg1)) &&
1182+
(v < 0 || isSubType(arg1, arg2))
11811183
}
11821184
}
11831185

@@ -1243,7 +1245,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
12431245
* @see [[necessaryEither]] for the GADTFlexible case
12441246
*/
12451247
protected def either(op1: => Boolean, op2: => Boolean): Boolean =
1246-
if (ctx.mode.is(Mode.GADTflexible)) necessaryEither(op1, op2) else sufficientEither(op1, op2)
1248+
if (ctx.mode.is(Mode.GadtConstraintInference)) necessaryEither(op1, op2) else sufficientEither(op1, op2)
12471249

12481250
/** Returns true iff the result of evaluating either `op1` or `op2` is true,
12491251
* trying at the same time to keep the constraint as wide as possible.
@@ -1491,7 +1493,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
14911493
*/
14921494
private def narrowGADTBounds(tr: NamedType, bound: Type, approx: ApproxState, isUpper: Boolean): Boolean = {
14931495
val boundImprecise = approx.high || approx.low
1494-
ctx.mode.is(Mode.GADTflexible) && !frozenGadt && !frozenConstraint && !boundImprecise && {
1496+
ctx.mode.is(Mode.GadtConstraintInference) && !frozenGadt && !frozenConstraint && !boundImprecise && {
14951497
val tparam = tr.symbol
14961498
gadts.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.toString} ${bound.isRef(tparam)}")
14971499
if (bound.isRef(tparam)) false

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
10981098
// We ignore whether constraining the pattern succeeded.
10991099
// Constraining only fails if the pattern cannot possibly match,
11001100
// but useless pattern checks detect more such cases, so we simply rely on them instead.
1101-
ctx.addMode(Mode.GADTflexible).typeComparer.constrainPatternType(unapplyArgType, selType)
1101+
ctx.addMode(Mode.GadtConstraintInference).typeComparer.constrainPatternType(unapplyArgType, selType)
11021102
val patternBound = maximizeType(unapplyArgType, tree.span, fromScala2x)
11031103
if (patternBound.nonEmpty) unapplyFn = addBinders(unapplyFn, patternBound)
11041104
unapp.println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}")

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
916916
}
917917

918918
if (!isImplicit) caseBindingMap += ((NoSymbol, scrutineeBinding))
919-
val gadtCtx = ctx.fresh.setFreshGADTBounds.addMode(Mode.GADTflexible)
919+
val gadtCtx = ctx.fresh.setFreshGADTBounds.addMode(Mode.GadtConstraintInference)
920920
if (reducePattern(caseBindingMap, scrutineeSym.termRef, cdef.pat)(gadtCtx)) {
921921
val (caseBindings, from, to) = substBindings(caseBindingMap.toList, mutable.ListBuffer(), Nil, Nil)
922922
val guardOK = cdef.guard.isEmpty || {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ class Typer extends Namer
607607
def handlePattern: Tree = {
608608
val tpt1 = typedTpt
609609
if (!ctx.isAfterTyper && pt != defn.ImplicitScrutineeTypeRef)
610-
ctx.addMode(Mode.GADTflexible).typeComparer.constrainPatternType(tpt1.tpe, pt)
610+
ctx.addMode(Mode.GadtConstraintInference).typeComparer.constrainPatternType(tpt1.tpe, pt)
611611
// special case for an abstract type that comes with a class tag
612612
tryWithClassTag(ascription(tpt1, isWildcard = true), pt)
613613
}
@@ -3153,7 +3153,7 @@ class Typer extends Namer
31533153
case _: RefTree | _: Literal
31543154
if !isVarPattern(tree) &&
31553155
!(pt <:< tree.tpe) &&
3156-
!ctx.addMode(Mode.GADTflexible).typeComparer.constrainPatternType(tree.tpe, pt) =>
3156+
!ctx.addMode(Mode.GadtConstraintInference).typeComparer.constrainPatternType(tree.tpe, pt) =>
31573157
val cmp =
31583158
untpd.Apply(
31593159
untpd.Select(untpd.TypedSplice(tree), nme.EQ),

0 commit comments

Comments
 (0)