@@ -74,6 +74,13 @@ trait SpaceLogic {
74
74
/** Is `tp1` the same type as `tp2`? */
75
75
def isEqualType (tp1 : Type , tp2 : Type ): Boolean
76
76
77
+ /** Return a space containing the values of both types.
78
+ *
79
+ * The types should be atomic (non-decomposable) and unrelated (neither
80
+ * should be a subtype of the other).
81
+ */
82
+ def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ): Space
83
+
77
84
/** Is the type `tp` decomposable? i.e. all values of the type can be covered
78
85
* by its decomposed types.
79
86
*
@@ -171,7 +178,7 @@ trait SpaceLogic {
171
178
else if (isSubType(tp2, tp1)) b
172
179
else if (canDecompose(tp1)) tryDecompose1(tp1)
173
180
else if (canDecompose(tp2)) tryDecompose2(tp2)
174
- else Empty
181
+ else intersectUnrelatedAtomicTypes(tp1, tp2)
175
182
case (Typ (tp1, _), Kon (tp2, ss)) =>
176
183
if (isSubType(tp2, tp1)) b
177
184
else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class
@@ -239,10 +246,96 @@ trait SpaceLogic {
239
246
}
240
247
}
241
248
249
+ object SpaceEngine {
250
+ private sealed trait Implementability {
251
+ def show (implicit ctx : Context ) = this match {
252
+ case SubclassOf (classSyms) => s " SubclassOf( ${classSyms.map(_.show)}) "
253
+ case other => other.toString
254
+ }
255
+ }
256
+ private case object ClassOrTrait extends Implementability
257
+ private case class SubclassOf (classSyms : List [Symbol ]) extends Implementability
258
+ private case object Unimplementable extends Implementability
259
+ }
260
+
242
261
/** Scala implementation of space logic */
243
262
class SpaceEngine (implicit ctx : Context ) extends SpaceLogic {
263
+ import SpaceEngine ._
244
264
import tpd ._
245
265
266
+ /** Checks if it's possible to create a trait/class which is a subtype of `tp`.
267
+ *
268
+ * - doesn't handle member collisions (will not declare a type unimplementable because of one)
269
+ * - expects that neither Any nor Object reach it
270
+ * (this is currently true due to both isSubType and and/or type simplification)
271
+ *
272
+ * See [[intersectUnrelatedAtomicTypes ]].
273
+ */
274
+ private def implementability (tp : Type ): Implementability = tp.dealias match {
275
+ case AndType (tp1, tp2) =>
276
+ (implementability(tp1), implementability(tp2)) match {
277
+ case (Unimplementable , _) | (_, Unimplementable ) => Unimplementable
278
+ case (SubclassOf (classSyms1), SubclassOf (classSyms2)) =>
279
+ (for {
280
+ sym1 <- classSyms1
281
+ sym2 <- classSyms2
282
+ result <-
283
+ if (sym1 isSubClass sym2) List (sym1)
284
+ else if (sym2 isSubClass sym1) List (sym2)
285
+ else Nil
286
+ } yield result) match {
287
+ case Nil => Unimplementable
288
+ case lst => SubclassOf (lst)
289
+ }
290
+ case (ClassOrTrait , ClassOrTrait ) => ClassOrTrait
291
+ case (SubclassOf (clss), _) => SubclassOf (clss)
292
+ case (_, SubclassOf (clss)) => SubclassOf (clss)
293
+ }
294
+ case OrType (tp1, tp2) =>
295
+ (implementability(tp1), implementability(tp2)) match {
296
+ case (ClassOrTrait , _) | (_, ClassOrTrait ) => ClassOrTrait
297
+ case (SubclassOf (classSyms1), SubclassOf (classSyms2)) =>
298
+ SubclassOf (classSyms1 ::: classSyms2)
299
+ case (SubclassOf (classSyms), _) => SubclassOf (classSyms)
300
+ case (_, SubclassOf (classSyms)) => SubclassOf (classSyms)
301
+ case _ => Unimplementable
302
+ }
303
+ case _ : SingletonType =>
304
+ // singleton types have no instantiable subtypes
305
+ Unimplementable
306
+ case tp : RefinedType =>
307
+ // refinement itself is not considered - it would at most make
308
+ // a type unimplementable because of a member collision
309
+ implementability(tp.parent)
310
+ case other =>
311
+ val classSym = other.classSymbol
312
+ if (classSym.exists) {
313
+ if (classSym is Final ) Unimplementable
314
+ else if (classSym is Trait ) ClassOrTrait
315
+ else SubclassOf (List (classSym))
316
+ } else {
317
+ // if no class symbol exists, conservatively say that anything
318
+ // can implement `tp`
319
+ ClassOrTrait
320
+ }
321
+ }
322
+
323
+ override def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ) = {
324
+ val and = AndType (tp1, tp2)
325
+ // Precondition: !(tp1 <:< tp2) && !(tp2 <:< tp1)
326
+ // Then, no leaf of the and-type tree `and` is a subtype of `and`.
327
+ // Then, to create a value of type `and` you must instantiate a trait (class/module)
328
+ // which is a subtype of all the leaves of `and`.
329
+ val imp = implementability(and)
330
+
331
+ debug.println(s " atomic intersection: ${and.show} ~ ${imp.show}" )
332
+
333
+ imp match {
334
+ case Unimplementable => Empty
335
+ case _ => Typ (and, true )
336
+ }
337
+ }
338
+
246
339
/** Return the space that represents the pattern `pat`
247
340
*
248
341
* If roundUp is true, approximate extractors to its type,
@@ -325,6 +418,12 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
325
418
debug.println(s " candidates for ${tp.show} : [ ${children.map(_.show).mkString(" , " )}] " )
326
419
327
420
tp.dealias match {
421
+ case AndType (tp1, tp2) =>
422
+ intersect(Typ (tp1, false ), Typ (tp2, false )) match {
423
+ case Or (spaces) => spaces
424
+ case Empty => Nil
425
+ case space => List (space)
426
+ }
328
427
case OrType (tp1, tp2) => List (Typ (tp1, true ), Typ (tp2, true ))
329
428
case _ if tp =:= ctx.definitions.BooleanType =>
330
429
List (
@@ -377,9 +476,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
377
476
378
477
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
379
478
def canDecompose (tp : Type ): Boolean = {
479
+ val dealiasedTp = tp.dealias
380
480
val res = tp.classSymbol.is(allOf(Abstract , Sealed )) ||
381
481
tp.classSymbol.is(allOf(Trait , Sealed )) ||
382
- tp.dealias.isInstanceOf [OrType ] ||
482
+ dealiasedTp.isInstanceOf [OrType ] ||
483
+ (dealiasedTp.isInstanceOf [AndType ] && {
484
+ val and = dealiasedTp.asInstanceOf [AndType ]
485
+ canDecompose(and.tp1) || canDecompose(and.tp2)
486
+ }) ||
383
487
tp =:= ctx.definitions.BooleanType ||
384
488
tp.classSymbol.is(allOf(Enum , Sealed )) // Enum value doesn't have Sealed flag
385
489
@@ -477,6 +581,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
477
581
ctx.settings.YcheckAllPatmat .value ||
478
582
tp.typeSymbol.is(Sealed ) ||
479
583
tp.isInstanceOf [OrType ] ||
584
+ (tp.isInstanceOf [AndType ] && {
585
+ val and = tp.asInstanceOf [AndType ]
586
+ isCheckable(and.tp1) || isCheckable(and.tp2)
587
+ }) ||
480
588
tp.typeSymbol == ctx.definitions.BooleanType .typeSymbol ||
481
589
tp.typeSymbol.is(Enum ) ||
482
590
canDecompose(tp) ||
0 commit comments