Skip to content

Commit

Permalink
Handle sealed prefixes in exh checking
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jan 10, 2023
1 parent 805dda8 commit 391b433
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 5 deletions.
17 changes: 13 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ast.tpd._
import reporting.trace
import config.Printers.typr
import config.Feature
import transform.SymUtils.*
import typer.ProtoTypes._
import typer.ForceDegree
import typer.Inferencing._
Expand Down Expand Up @@ -846,14 +847,22 @@ object TypeOps:
var prefixTVar: Type | Null = null
def apply(tp: Type): Type = tp match {
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
if (tref.symbol.is(Module))
TermRef(this(tref.prefix), tref.symbol.sourceModule)
val symbol = tref.symbol
if (symbol.is(Module))
TermRef(this(tref.prefix), symbol.sourceModule)
else if (prefixTVar != null)
this(tref)
else {
prefixTVar = WildcardType // prevent recursive call from assigning it
val tref2 = this(tref.applyIfParameterized(tref.typeParams.map(_ => TypeBounds.empty)))
prefixTVar = newTypeVar(TypeBounds.upper(tref2))
prefixTVar = if symbol.is(Sealed) && symbol.isOneOf(AbstractOrTrait) && symbol.children.sizeIs > 0 && !symbol.hasAnonymousChild then
symbol.children.foldLeft(WildcardType: Type) { (acc, childSym) =>
val child = childSym.namedType
val tp = this(child.applyIfParameterized(child.typeParams.map(_ => TypeBounds.empty)))
acc | tp
}
else
val tref2 = this(tref.applyIfParameterized(tref.typeParams.map(_ => TypeBounds.empty)))
newTypeVar(TypeBounds.upper(tref2))
prefixTVar.uncheckedNN
}
case tp => mapOver(tp)
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,17 @@ class SpaceEngine(using Context) extends SpaceLogic {
Typ(ConstantType(Constant(())), true) :: Nil
case tp if tp.classSymbol.isAllOf(JavaEnumTrait) =>
tp.classSymbol.children.map(sym => Typ(sym.termRef, true))

case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDecompose(tycon) =>
// It might not obvious that it's OK to apply the type arguments of a parent type to child types.
// But this is guarded by `tp.classSymbol.children.isEmpty`,
// meaning we'll decompose to the same class, just not the same type.
// For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`.
rec(tycon, Nil).map(typ => Typ(tp.derivedAppliedType(typ.tp, targs)))

case tp: NamedType if canDecompose(tp.prefix) =>
rec(tp.prefix, Nil).map(typ => Typ(tp.derivedSelect(typ.tp)))

case tp =>
def getChildren(sym: Symbol): List[Symbol] =
sym.children.flatMap { child =>
Expand Down Expand Up @@ -669,9 +680,11 @@ class SpaceEngine(using Context) extends SpaceLogic {
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
def canDecompose(tp: Type): Boolean =
val res = tp.dealias match
case AppliedType(tycon, _) if canDecompose(tycon) => true
case tp: NamedType if canDecompose(tp.prefix) => true
case _: SingletonType => false
case _: OrType => true
case and: AndType => canDecompose(and.tp1) || canDecompose(and.tp2)
case AndType(tp1, tp2) => canDecompose(tp1) || canDecompose(tp2)
case _ =>
val cls = tp.classSymbol
cls.is(Sealed)
Expand Down
25 changes: 25 additions & 0 deletions tests/pos/i15029.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// scalac: -Werror
sealed trait Schema[A]

sealed trait RecordInstances:
case class Field[B]() extends Schema[B]
case object Thing extends Schema[Int]

object X extends RecordInstances
object Y extends RecordInstances

// Match not exhaustive error! (with fatal warnings :P)
class Test:
def handle[T](schema: Schema[T]) =
schema match // was: match may not be exhaustive
case X.Field() =>
case X.Thing =>
case Y.Field() =>
case Y.Thing =>

def t(a: Boolean, b: Boolean) =
(a, b) match
case (false, false) => 1
case (false, true ) => 2
case (true, false) => 3
case (true, true ) => 4

0 comments on commit 391b433

Please sign in to comment.