@@ -16,6 +16,7 @@ import transform.TypeUtils._
16
16
import transform .SymUtils ._
17
17
import scala .util .control .NonFatal
18
18
import typer .ProtoTypes .constrained
19
+ import typer .Applications .productSelectorTypes
19
20
import reporting .trace
20
21
21
22
final class AbsentContext
@@ -2136,19 +2137,52 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
2136
2137
/** Returns last check's debug mode, if explicitly enabled. */
2137
2138
def lastTrace (): String = " "
2138
2139
2139
- /** Are `tp1` and `tp2` disjoint types?
2140
+ private def typeparamCorrespondsToField (tycon : Type , tparam : TypeParamInfo ): Boolean =
2141
+ productSelectorTypes(tycon, null ).exists {
2142
+ case tp : TypeRef =>
2143
+ tp.designator.eq(tparam) // Bingo!
2144
+ case _ =>
2145
+ false
2146
+ }
2147
+
2148
+ /** Is `tp` an empty type?
2140
2149
*
2141
- * `true` implies that we found a proof; uncertainty default to `false`.
2150
+ * `true` implies that we found a proof; uncertainty defaults to `false`.
2151
+ */
2152
+ def provablyEmpty (tp : Type ): Boolean =
2153
+ tp.dealias match {
2154
+ case tp if tp.isBottomType => true
2155
+ case AndType (tp1, tp2) => provablyDisjoint(tp1, tp2)
2156
+ case OrType (tp1, tp2) => provablyEmpty(tp1) && provablyEmpty(tp2)
2157
+ case at @ AppliedType (tycon, args) =>
2158
+ args.lazyZip(tycon.typeParams).exists { (arg, tparam) =>
2159
+ tparam.paramVariance >= 0
2160
+ && provablyEmpty(arg)
2161
+ && typeparamCorrespondsToField(tycon, tparam)
2162
+ }
2163
+ case tp : TypeProxy =>
2164
+ provablyEmpty(tp.underlying)
2165
+ case _ => false
2166
+ }
2167
+
2168
+
2169
+ /** Are `tp1` and `tp2` provablyDisjoint types?
2170
+ *
2171
+ * `true` implies that we found a proof; uncertainty defaults to `false`.
2142
2172
*
2143
2173
* Proofs rely on the following properties of Scala types:
2144
2174
*
2145
2175
* 1. Single inheritance of classes
2146
2176
* 2. Final classes cannot be extended
2147
- * 3. ConstantTypes with distinc values are non intersecting
2177
+ * 3. ConstantTypes with distinct values are non intersecting
2148
2178
* 4. There is no value of type Nothing
2179
+ *
2180
+ * Note on soundness: the correctness of match types relies on on the
2181
+ * property that in all possible contexts, the same match type expression
2182
+ * is either stuck or reduces to the same case.
2149
2183
*/
2150
- def disjoint (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Boolean = {
2151
- // println(s"disjoint (${tp1.show}, ${tp2.show})")
2184
+ def provablyDisjoint (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Boolean = {
2185
+ // println(s"provablyDisjoint (${tp1.show}, ${tp2.show})")
2152
2186
/** Can we enumerate all instantiations of this type? */
2153
2187
def isClosedSum (tp : Symbol ): Boolean =
2154
2188
tp.is(Sealed ) && tp.isOneOf(AbstractOrTrait ) && ! tp.hasAnonymousChild
@@ -2181,33 +2215,19 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
2181
2215
// of classes.
2182
2216
true
2183
2217
else if (isClosedSum(cls1))
2184
- decompose(cls1, tp1).forall(x => disjoint (x, tp2))
2218
+ decompose(cls1, tp1).forall(x => provablyDisjoint (x, tp2))
2185
2219
else if (isClosedSum(cls2))
2186
- decompose(cls2, tp2).forall(x => disjoint (x, tp1))
2220
+ decompose(cls2, tp2).forall(x => provablyDisjoint (x, tp1))
2187
2221
else
2188
2222
false
2189
2223
case (AppliedType (tycon1, args1), AppliedType (tycon2, args2)) if tycon1 == tycon2 =>
2224
+ // It is possible to conclude that two types applies are disjoint by
2225
+ // looking at covariant type parameters if the said type parameters
2226
+ // are disjoin and correspond to fields.
2227
+ // (Type parameter disjointness is not enough by itself as it could
2228
+ // lead to incorrect conclusions for phantom type parameters).
2190
2229
def covariantDisjoint (tp1 : Type , tp2 : Type , tparam : TypeParamInfo ): Boolean =
2191
- disjoint(tp1, tp2) && {
2192
- // We still need to proof that `Nothing` is not a valid
2193
- // instantiation of this type parameter. We have two ways
2194
- // to get to that conclusion:
2195
- // 1. `Nothing` does not conform to the type parameter's lb
2196
- // 2. `tycon1` has a field typed with this type parameter.
2197
- //
2198
- // Because of separate compilation, the use of 2. is
2199
- // limited to case classes.
2200
- import dotty .tools .dotc .typer .Applications .productSelectorTypes
2201
- val lowerBoundedByNothing = tparam.paramInfo.bounds.lo eq NothingType
2202
- val typeUsedAsField =
2203
- productSelectorTypes(tycon1, null ).exists {
2204
- case tp : TypeRef =>
2205
- (tp.designator: Any ) == tparam // Bingo!
2206
- case _ =>
2207
- false
2208
- }
2209
- ! lowerBoundedByNothing || typeUsedAsField
2210
- }
2230
+ provablyDisjoint(tp1, tp2) && typeparamCorrespondsToField(tycon1, tparam)
2211
2231
2212
2232
args1.lazyZip(args2).lazyZip(tycon1.typeParams).exists {
2213
2233
(arg1, arg2, tparam) =>
@@ -2236,29 +2256,29 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
2236
2256
}
2237
2257
}
2238
2258
case (tp1 : HKLambda , tp2 : HKLambda ) =>
2239
- disjoint (tp1.resType, tp2.resType)
2259
+ provablyDisjoint (tp1.resType, tp2.resType)
2240
2260
case (_ : HKLambda , _) =>
2241
- // The intersection of these two types would be ill kinded, they are therefore disjoint .
2261
+ // The intersection of these two types would be ill kinded, they are therefore provablyDisjoint .
2242
2262
true
2243
2263
case (_, _ : HKLambda ) =>
2244
2264
true
2245
2265
case (tp1 : OrType , _) =>
2246
- disjoint (tp1.tp1, tp2) && disjoint (tp1.tp2, tp2)
2266
+ provablyDisjoint (tp1.tp1, tp2) && provablyDisjoint (tp1.tp2, tp2)
2247
2267
case (_, tp2 : OrType ) =>
2248
- disjoint (tp1, tp2.tp1) && disjoint (tp1, tp2.tp2)
2268
+ provablyDisjoint (tp1, tp2.tp1) && provablyDisjoint (tp1, tp2.tp2)
2249
2269
case (tp1 : AndType , tp2 : AndType ) =>
2250
- (disjoint (tp1.tp1, tp2.tp1) || disjoint (tp1.tp2, tp2.tp2)) &&
2251
- (disjoint (tp1.tp1, tp2.tp2) || disjoint (tp1.tp2, tp2.tp1))
2270
+ (provablyDisjoint (tp1.tp1, tp2.tp1) || provablyDisjoint (tp1.tp2, tp2.tp2)) &&
2271
+ (provablyDisjoint (tp1.tp1, tp2.tp2) || provablyDisjoint (tp1.tp2, tp2.tp1))
2252
2272
case (tp1 : AndType , _) =>
2253
- disjoint (tp1.tp2, tp2) || disjoint (tp1.tp1, tp2)
2273
+ provablyDisjoint (tp1.tp2, tp2) || provablyDisjoint (tp1.tp1, tp2)
2254
2274
case (_, tp2 : AndType ) =>
2255
- disjoint (tp1, tp2.tp2) || disjoint (tp1, tp2.tp1)
2275
+ provablyDisjoint (tp1, tp2.tp2) || provablyDisjoint (tp1, tp2.tp1)
2256
2276
case (tp1 : TypeProxy , tp2 : TypeProxy ) =>
2257
- disjoint (tp1.underlying, tp2) || disjoint (tp1, tp2.underlying)
2277
+ provablyDisjoint (tp1.underlying, tp2) || provablyDisjoint (tp1, tp2.underlying)
2258
2278
case (tp1 : TypeProxy , _) =>
2259
- disjoint (tp1.underlying, tp2)
2279
+ provablyDisjoint (tp1.underlying, tp2)
2260
2280
case (_, tp2 : TypeProxy ) =>
2261
- disjoint (tp1, tp2.underlying)
2281
+ provablyDisjoint (tp1, tp2.underlying)
2262
2282
case _ =>
2263
2283
false
2264
2284
}
@@ -2419,6 +2439,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
2419
2439
}.apply(tp)
2420
2440
2421
2441
val defn .MatchCase (pat, body) = cas1
2442
+
2422
2443
if (isSubType(scrut, pat))
2423
2444
// `scrut` is a subtype of `pat`: *It's a Match!*
2424
2445
Some {
@@ -2432,8 +2453,8 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
2432
2453
}
2433
2454
else if (isSubType(widenAbstractTypes(scrut), widenAbstractTypes(pat)))
2434
2455
Some (NoType )
2435
- else if (disjoint (scrut, pat))
2436
- // We found a proof that `scrut` and `pat` are incompatible.
2456
+ else if (provablyDisjoint (scrut, pat))
2457
+ // We found a proof that `scrut` and `pat` are incompatible.
2437
2458
// The search continues.
2438
2459
None
2439
2460
else
@@ -2445,7 +2466,25 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
2445
2466
case Nil => NoType
2446
2467
}
2447
2468
2448
- inFrozenConstraint(recur(cases))
2469
+ inFrozenConstraint {
2470
+ // Empty types break the basic assumption that if a scrutinee and a
2471
+ // pattern are disjoint it's OK to reduce passed that pattern. Indeed,
2472
+ // empty types viewed as a set of value is always a subset of any other
2473
+ // types. As a result, we first check that the scrutinee isn't empty
2474
+ // before proceeding with reduction. See `tests/neg/6570.scala` and
2475
+ // `6570-1.scala` for examples that exploit emptiness to break match
2476
+ // type soundness.
2477
+
2478
+ // If we revered the uncertainty case of this empty check, that is,
2479
+ // `!provablyNonEmpty` instead of `provablyEmpty`, that would be
2480
+ // obviously sound, but quite restrictive. With the current formulation,
2481
+ // we need to be careful that `provablyEmpty` covers all the conditions
2482
+ // used to conclude disjointness in `provablyDisjoint`.
2483
+ if (provablyEmpty(scrut))
2484
+ NoType
2485
+ else
2486
+ recur(cases)
2487
+ }
2449
2488
}
2450
2489
}
2451
2490
0 commit comments