Skip to content

Commit d02b2eb

Browse files
committed
initial implementation of exhaustivity and redundancy check
1 parent 07fd8a3 commit d02b2eb

File tree

10 files changed

+705
-17
lines changed

10 files changed

+705
-17
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,16 +616,20 @@ object desugar {
616616
*
617617
* { cases }
618618
* ==>
619-
* x$1 => x$1 match { cases }
619+
* x$1 => (x$1 @unchecked) match { cases }
620620
*
621621
* If `nparams` != 1, expand instead to
622622
*
623-
* (x$1, ..., x$n) => (x$0, ..., x${n-1}) match { cases }
623+
* (x$1, ..., x$n) => (x$0, ..., x${n-1} @unchecked) match { cases }
624624
*/
625-
def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = {
625+
def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1, unchecked: Boolean = true)(implicit ctx: Context) = {
626626
val params = (1 to nparams).toList.map(makeSyntheticParameter(_))
627627
val selector = makeTuple(params.map(p => Ident(p.name)))
628-
Function(params, Match(selector, cases))
628+
629+
if (unchecked)
630+
Function(params, Match(Annotated(New(ref(defn.UncheckedAnnotType)), selector), cases))
631+
else
632+
Function(params, Match(selector, cases))
629633
}
630634

631635
/** Map n-ary function `(p1, ..., pn) => body` where n != 1 to unary function as follows:
@@ -753,7 +757,7 @@ object desugar {
753757
case VarPattern(named, tpt) =>
754758
Function(derivedValDef(named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body)
755759
case _ =>
756-
makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil)
760+
makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false)
757761
}
758762

759763
/** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
@@ -799,7 +803,7 @@ object desugar {
799803
val cases = List(
800804
CaseDef(pat, EmptyTree, Literal(Constant(true))),
801805
CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))))
802-
Apply(Select(rhs, nme.withFilter), Match(EmptyTree, cases))
806+
Apply(Select(rhs, nme.withFilter), makeCaseLambda(cases))
803807
}
804808

805809
/** Is pattern `pat` irrefutable when matched against `rhs`?

src/dotty/tools/dotc/core/Types.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,13 @@ object Types {
810810
case _ => this
811811
}
812812

813+
/** Eliminate anonymous classes */
814+
final def deAnonymize(implicit ctx: Context): Type = this match {
815+
case tp:TypeRef if tp.symbol.isAnonymousClass =>
816+
tp.symbol.asClass.typeRef.asSeenFrom(tp.prefix, tp.symbol.owner)
817+
case tp => tp
818+
}
819+
813820
/** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
814821
* is no longer alias type, LazyRef, or instantiated type variable.
815822
*/

src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class ClassfileParser(
9090
val jflags = in.nextChar
9191
val isAnnotation = hasAnnotation(jflags)
9292
val sflags = classTranslation.flags(jflags)
93+
val isEnum = (jflags & JAVA_ACC_ENUM) != 0
9394
val nameIdx = in.nextChar
9495
currentClassName = pool.getClassName(nameIdx)
9596

@@ -145,6 +146,15 @@ class ClassfileParser(
145146
setClassInfo(classRoot, classInfo)
146147
setClassInfo(moduleRoot, staticInfo)
147148
}
149+
150+
// eager load java enum definitions for exhaustivity check of pattern match
151+
if (isEnum) {
152+
instanceScope.toList.map(_.ensureCompleted())
153+
staticScope.toList.map(_.ensureCompleted())
154+
classRoot.setFlag(Flags.Enum)
155+
moduleRoot.setFlag(Flags.Enum)
156+
}
157+
148158
result
149159
}
150160

src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>
7474
Bind(defaultSym, Underscore(selector.tpe.widen)),
7575
EmptyTree,
7676
Literal(Constant(false)))
77-
cpy.Match(applyRhs)(paramRef, cases.map(translateCase) :+ defaultCase)
77+
val annotated = Annotated(New(ref(defn.UncheckedAnnotType)), paramRef)
78+
cpy.Match(applyRhs)(annotated, cases.map(translateCase) :+ defaultCase)
7879
case _ =>
7980
tru
8081
}

src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Applications._
2424
import TypeApplications._
2525
import SymUtils._, core.NameOps._
2626
import core.Mode
27+
import patmat._
2728

2829
import dotty.tools.dotc.util.Positions.Position
2930
import dotty.tools.dotc.core.Decorators._
@@ -52,6 +53,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
5253
override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = {
5354
val translated = new Translator()(ctx).translator.translateMatch(tree)
5455

56+
// check exhaustivity and unreachability
57+
val engine = new SpaceEngine
58+
if (engine.checkable(tree)) {
59+
engine.checkExhaustivity(tree)
60+
engine.checkRedundancy(tree)
61+
}
62+
5563
translated.ensureConforms(tree.tpe)
5664
}
5765

@@ -1246,13 +1254,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12461254
case _ => false
12471255
}
12481256

1249-
def elimAnonymousClass(t: Type) = t match {
1250-
case t:TypeRef if t.symbol.isAnonymousClass =>
1251-
t.symbol.asClass.typeRef.asSeenFrom(t.prefix, t.symbol.owner)
1252-
case _ =>
1253-
t
1254-
}
1255-
12561257
/** Implement a pattern match by turning its cases (including the implicit failure case)
12571258
* into the corresponding (monadic) extractors, and combining them with the `orElse` combinator.
12581259
*
@@ -1266,7 +1267,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12661267
def translateMatch(match_ : Match): Tree = {
12671268
val Match(sel, cases) = match_
12681269

1269-
val selectorTp = elimAnonymousClass(sel.tpe.widen/*withoutAnnotations*/)
1270+
val selectorTp = sel.tpe.widen.deAnonymize/*withoutAnnotations*/
12701271

12711272
val selectorSym = freshSym(sel.pos, selectorTp, "selector")
12721273

@@ -1275,6 +1276,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
12751276
case _ => (cases, None)
12761277
}
12771278

1279+
12781280
// checkMatchVariablePatterns(nonSyntheticCases) // only used for warnings
12791281

12801282
// we don't transform after uncurry

src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import Symbols._, TypeUtils._
3838
*
3939
* (9) Adds SourceFile annotations to all top-level classes and objects
4040
*
41+
* (10) Adds Child annotations to all sealed classes
42+
*
4143
* The reason for making this a macro transform is that some functions (in particular
4244
* super and protected accessors and instantiation checks) are naturally top-down and
4345
* don't lend themselves to the bottom-up approach of a mini phase. The other two functions
@@ -231,6 +233,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
231233
ctx.compilationUnit.source.exists &&
232234
sym != defn.SourceFileAnnot)
233235
sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path))
236+
237+
if (!sym.isAnonymousClass) // ignore anonymous class
238+
for (parent <- sym.asClass.classInfo.classParents) {
239+
val pclazz = parent.classSymbol
240+
if (pclazz.is(Sealed)) pclazz.addAnnotation(Annotation.makeChild(sym))
241+
}
242+
234243
tree
235244
}
236245
else {

0 commit comments

Comments
 (0)