Skip to content

Commit 75c4f38

Browse files
committed
Rewrite implementability with soundness in mind
1 parent 6fc2cd9 commit 75c4f38

File tree

2 files changed

+39
-15
lines changed

2 files changed

+39
-15
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -247,22 +247,29 @@ trait SpaceLogic {
247247
}
248248

249249
object SpaceEngine {
250-
private sealed trait Implementability
251-
private object ClassOrTrait extends Implementability
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
252257
private case class SubclassOf(classSyms: List[Symbol]) extends Implementability
253-
private object Unimplementable extends Implementability
258+
private case object Unimplementable extends Implementability
254259
}
255260

256261
/** Scala implementation of space logic */
257262
class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
258263
import SpaceEngine._
259264
import tpd._
260265

261-
/** Checks if `tp` can be implemented by a new class/trait.
266+
/** Checks if it's possible to create a trait/class which is a subtype of `tp`.
262267
*
263-
* - doesn't handle member collisions (assumes they don't happen)
268+
* - doesn't handle member collisions (will not declare a type unimplementable because of one)
264269
* - expects that neither Any nor Object reach it
265270
* (this is currently true due to both isSubType and and/or type simplification)
271+
*
272+
* See [[intersectUnrelatedAtomicTypes]].
266273
*/
267274
private def implementability(tp: Type): Implementability = tp.dealias match {
268275
case AndType(tp1, tp2) =>
@@ -293,20 +300,37 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
293300
case (_, SubclassOf(classSyms)) => SubclassOf(classSyms)
294301
case _ => Unimplementable
295302
}
296-
case tp: RefinedType => implementability(tp.parent)
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)
297310
case other =>
298311
val classSym = other.classSymbol
299-
val isOpen =
300-
!classSym.is(Sealed | Final) && !other.termSymbol.is(Module)
301-
302-
if (isOpen) {
303-
if (classSym is Trait) ClassOrTrait else SubclassOf(List(classSym))
304-
} else Unimplementable
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+
}
305321
}
306322

307323
override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type) = {
308324
val and = AndType(tp1, tp2)
309-
implementability(and) match {
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 {
310334
case Unimplementable => Empty
311335
case _ => Typ(and, true)
312336
}

tests/patmat/andtype-opentype-interaction.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
23: Pattern Match Exhaustivity: _: AbstractClass & OpenTrait, _: Clazz & OpenTrait, _: Trait & OpenTrait
2-
27: Pattern Match Exhaustivity: _: AbstractClass & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: Trait & OpenTrait & OpenTrait2
1+
23: Pattern Match Exhaustivity: _: SealedClass & OpenTrait, _: AbstractClass & OpenTrait, _: Clazz & OpenTrait, _: Trait & OpenTrait
2+
27: Pattern Match Exhaustivity: _: SealedClass & OpenTrait & OpenTrait2, _: AbstractClass & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: Trait & OpenTrait & OpenTrait2
33
31: Pattern Match Exhaustivity: _: Trait & OpenClass
44
35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass
55
43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass

0 commit comments

Comments
 (0)