aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala89
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala4
3 files changed, 92 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index b63e0236d..049906ef0 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -61,6 +61,7 @@ class Compiler {
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter), // Expand selections involving union types into conditionals
List(new VCInlineMethods, // Inlines calls to value class methods
+ new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions
new SeqLiterals, // Express vararg arguments as arrays
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
diff --git a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala
new file mode 100644
index 000000000..d4483bb18
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala
@@ -0,0 +1,89 @@
+package dotty.tools.dotc
+package transform
+
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Types._
+import dotty.tools.dotc.core.Decorators._
+import dotty.tools.dotc.core.Flags
+import dotty.tools.dotc.core.Symbols._
+import dotty.tools.dotc.util.Positions._
+import dotty.tools.dotc.core.TypeErasure._
+import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
+
+class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer =>
+
+ import dotty.tools.dotc.ast.tpd._
+
+ def phaseName = "reachabilityChecker"
+
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val defn = ctx.definitions
+
+ def handleStaticallyKnown(tree: TypeApply, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree =
+ if (!(scrutinee <:< selector) && inMatch) {
+ ctx.error(s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", pos)
+ tree
+ } else if (!(scrutinee <:< selector) && !inMatch) {
+ ctx.warning(s"this will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}` (will be optimized away)", pos)
+ rewrite(tree, to = false)
+ } else if (scrutinee <:< selector && !inMatch) {
+ ctx.warning(s"this will always yield true since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)", pos)
+ rewrite(tree, to = true)
+ } else /* if (scrutinee <:< selector && inMatch) */ rewrite(tree, to = true)
+
+ def rewrite(tree: TypeApply, to: Boolean): Tree = tree
+
+ def evalTypeApply(tree: TypeApply): Tree =
+ if (tree.symbol != defn.Any_isInstanceOf) tree
+ else tree.fun match {
+ case s: Select => {
+ val scrutinee = erasure(s.qualifier.tpe.widen)
+ val selector = erasure(tree.args.head.tpe.widen)
+
+ val scTrait = scrutinee.typeSymbol is Flags.Trait
+ val scClass =
+ scrutinee.typeSymbol.isClass &&
+ !(scrutinee.typeSymbol is Flags.Trait) &&
+ !(scrutinee.typeSymbol is Flags.Module)
+
+ val scClassNonFinal = scClass && !scrutinee.typeSymbol.is(Flags.Final)
+ val scFinalClass = scClass && (scrutinee.typeSymbol is Flags.Final)
+
+ val selTrait = selector.typeSymbol is Flags.Trait
+ val selClass =
+ selector.typeSymbol.isClass &&
+ !(selector.typeSymbol is Flags.Trait) &&
+ !(selector.typeSymbol is Flags.Module)
+
+ val selClassNonFinal = scClass && !(selector.typeSymbol is Flags.Final)
+ val selFinalClass = scClass && (selector.typeSymbol is Flags.Final)
+
+ // Cases ---------------------------------
+ val knownStatically = scFinalClass
+
+ val falseIfUnrelated =
+ (scClassNonFinal && selClassNonFinal) ||
+ (scClassNonFinal && selFinalClass) ||
+ (scTrait && selFinalClass)
+
+ // Doesn't need to be calculated (since true if others are false)
+ //val happens =
+ // (scClassNonFinal && selClassNonFinal) ||
+ // (scTrait && selClassNonFinal) ||
+ // (scTrait && selTrait)
+
+ val inMatch = s.qualifier.symbol is Flags.Case
+
+ if (knownStatically)
+ handleStaticallyKnown(tree, scrutinee, selector, inMatch, tree.pos)
+ else if (falseIfUnrelated && !(selector <:< scrutinee))
+ rewrite(tree, to = false)
+ else /*if (happens)*/ tree
+ }
+
+ case _ => tree
+ }
+
+ evalTypeApply(tree)
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 740fd5460..64b85a51f 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -56,7 +56,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
override def transformTry(tree: tpd.Try)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
val selector =
- ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic, defn.ThrowableType, coord = tree.pos)
+ ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic | Flags.Case, defn.ThrowableType, coord = tree.pos)
val sel = Ident(selector.termRef).withPos(tree.pos)
val rethrow = tpd.CaseDef(EmptyTree, EmptyTree, Throw(ref(selector)))
val newCases = tpd.CaseDef(
@@ -80,7 +80,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// assert(owner ne null); assert(owner ne NoSymbol)
def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x", owner: Symbol = ctx.owner) = {
ctr += 1
- ctx.newSymbol(owner, ctx.freshName(prefix + ctr).toTermName, Flags.Synthetic, tp, coord = pos)
+ ctx.newSymbol(owner, ctx.freshName(prefix + ctr).toTermName, Flags.Synthetic | Flags.Case, tp, coord = pos)
}
def newSynthCaseLabel(name: String, tpe:Type, owner: Symbol = ctx.owner) =