@@ -1219,6 +1219,17 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1219
1219
fix(tp)
1220
1220
}
1221
1221
1222
+ /** Returns true iff the result of evaluating either `op1` or `op2` is true and approximates resulting constraints.
1223
+ *
1224
+ * If we're _not_ in GADTFlexible mode, we try to keep the smaller of the two constraints.
1225
+ * If we're _in_ GADTFlexible mode, we keep the smaller constraint if any, or no constraint at all.
1226
+ *
1227
+ * @see [[sufficientEither ]] for the normal case
1228
+ * @see [[necessaryEither ]] for the GADTFlexible case
1229
+ */
1230
+ private def either (op1 : => Boolean , op2 : => Boolean ): Boolean =
1231
+ if (ctx.mode.is(Mode .GADTflexible )) necessaryEither(op1, op2) else sufficientEither(op1, op2)
1232
+
1222
1233
/** Returns true iff the result of evaluating either `op1` or `op2` is true,
1223
1234
* trying at the same time to keep the constraint as wide as possible.
1224
1235
* E.g, if
@@ -1247,13 +1258,79 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1247
1258
*
1248
1259
* Here, each precondition leads to a different constraint, and neither of
1249
1260
* the two post-constraints subsumes the other.
1261
+ *
1262
+ * Note that to be complete when it comes to typechecking, we would instead need to backtrack
1263
+ * and attempt to typecheck with the other constraint.
1264
+ *
1265
+ * Method name comes from the notion that we are keeping a constraint which is sufficient to satisfy
1266
+ * one of subtyping relationships.
1250
1267
*/
1251
- private def either (op1 : => Boolean , op2 : => Boolean ): Boolean = {
1268
+ private def sufficientEither (op1 : => Boolean , op2 : => Boolean ): Boolean = {
1269
+ val preConstraint = constraint
1270
+ op1 && {
1271
+ val leftConstraint = constraint
1272
+ constraint = preConstraint
1273
+ if (! (op2 && subsumes(leftConstraint, constraint, preConstraint))) {
1274
+ if (constr != noPrinter && ! subsumes(constraint, leftConstraint, preConstraint))
1275
+ constr.println(i " CUT - prefer $leftConstraint over $constraint" )
1276
+ constraint = leftConstraint
1277
+ }
1278
+ true
1279
+ } || op2
1280
+ }
1281
+
1282
+ /** Returns true iff the result of evaluating either `op1` or `op2` is true, keeping the smaller constraint if any.
1283
+ * E.g., if
1284
+ *
1285
+ * tp11 <:< tp12 = true with constraint c1 and GADT constraint g1
1286
+ * tp12 <:< tp22 = true with constraint c2 and GADT constraint g2
1287
+ *
1288
+ * We keep:
1289
+ * - (c1, g1) if c2 subsumes c1 and g2 subsumes g1
1290
+ * - (c2, g2) if c1 subsumes c2 and g1 subsumes g2
1291
+ * - neither constraint pair otherwise.
1292
+ *
1293
+ * Like [[sufficientEither ]], this method is used to approximate a solution in one of the following cases:
1294
+ *
1295
+ * T1 & T2 <:< T3
1296
+ * T1 <:< T2 | T3
1297
+ *
1298
+ * Unlike [[sufficientEither ]], this method is used in GADTFlexible mode, when we are attempting to infer GADT
1299
+ * constraints that necessarily follow from the subtyping relationship. For instance, if we have
1300
+ *
1301
+ * enum Expr[T] {
1302
+ * case IntExpr(i: Int) extends Expr[Int]
1303
+ * case StrExpr(s: String) extends Expr[String]
1304
+ * }
1305
+ *
1306
+ * and `A` is an abstract type and we know that
1307
+ *
1308
+ * Expr[A] <: IntExpr | StrExpr
1309
+ *
1310
+ * (the case with &-type is analogous) then this may follow either from
1311
+ *
1312
+ * Expr[A] <: IntExpr or Expr[A] <: StrExpr
1313
+ *
1314
+ * Since we don't know which branch is true, we need to give up and not keep either constraint. OTOH, if one
1315
+ * constraint pair is subsumed by the other, we know that it is necessary for both cases and therefore we can
1316
+ * keep it.
1317
+ *
1318
+ * Like [[sufficientEither ]], this method is not complete because sometimes, the necessary constraint
1319
+ * is neither of the pairs. For instance, if
1320
+ *
1321
+ * g1 = { A = Int, B = String }
1322
+ * g2 = { A = Int, B = Int }
1323
+ *
1324
+ * then the necessary constraint is { A = Int }, but correctly inferring that is, as far as we know, too expensive.
1325
+ *
1326
+ * Method name comes from the notion that we are keeping the constraint which is necessary to satisfy both
1327
+ * subtyping relationships.
1328
+ */
1329
+ private def necessaryEither (op1 : => Boolean , op2 : => Boolean ): Boolean = {
1252
1330
val preConstraint = constraint
1253
1331
1254
- if (ctx.mode.is(Mode .GADTflexible )) {
1255
1332
val preGadt = ctx.gadt.fresh
1256
- // if GADTflexible mode is on, we always have a ProperGadtConstraint
1333
+ // if GADTflexible mode is on, we expect to always have a ProperGadtConstraint
1257
1334
val pre = preGadt.asInstanceOf [ProperGadtConstraint ]
1258
1335
if (op1) {
1259
1336
val leftConstraint = constraint
@@ -1284,18 +1361,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1284
1361
true
1285
1362
}
1286
1363
} else op2
1287
- } else {
1288
- op1 && {
1289
- val leftConstraint = constraint
1290
- constraint = preConstraint
1291
- if (! (op2 && subsumes(leftConstraint, constraint, preConstraint))) {
1292
- if (constr != noPrinter && ! subsumes(constraint, leftConstraint, preConstraint))
1293
- constr.println(i " CUT - prefer $leftConstraint over $constraint" )
1294
- constraint = leftConstraint
1295
- }
1296
- true
1297
- } || op2
1298
- }
1299
1364
}
1300
1365
1301
1366
/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
0 commit comments