Skip to content

Commit ced732f

Browse files
committed
Add initial partial evaluation of isInstanceOf calls
1 parent 134ad7a commit ced732f

File tree

3 files changed

+92
-2
lines changed

3 files changed

+92
-2
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class Compiler {
6161
new CrossCastAnd, // Normalize selections involving intersection types.
6262
new Splitter), // Expand selections involving union types into conditionals
6363
List(new VCInlineMethods, // Inlines calls to value class methods
64+
new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions
6465
new SeqLiterals, // Express vararg arguments as arrays
6566
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
6667
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.Types._
6+
import dotty.tools.dotc.core.Decorators._
7+
import dotty.tools.dotc.core.Flags
8+
import dotty.tools.dotc.core.Symbols._
9+
import dotty.tools.dotc.util.Positions._
10+
import dotty.tools.dotc.core.TypeErasure._
11+
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
12+
13+
class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer =>
14+
15+
import dotty.tools.dotc.ast.tpd._
16+
17+
def phaseName = "reachabilityChecker"
18+
19+
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
20+
val defn = ctx.definitions
21+
22+
def handleStaticallyKnown(tree: TypeApply, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree =
23+
if (!(scrutinee <:< selector) && inMatch) {
24+
ctx.error(s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", pos)
25+
tree
26+
} else if (!(scrutinee <:< selector) && !inMatch) {
27+
ctx.warning(s"this will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}` (will be optimized away)", pos)
28+
rewrite(tree, to = false)
29+
} else if (scrutinee <:< selector && !inMatch) {
30+
ctx.warning(s"this will always yield true since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)", pos)
31+
rewrite(tree, to = true)
32+
} else /* if (scrutinee <:< selector && inMatch) */ rewrite(tree, to = true)
33+
34+
def rewrite(tree: TypeApply, to: Boolean): Tree = tree
35+
36+
def evalTypeApply(tree: TypeApply): Tree =
37+
if (tree.symbol != defn.Any_isInstanceOf) tree
38+
else tree.fun match {
39+
case s: Select => {
40+
val scrutinee = erasure(s.qualifier.tpe.widen)
41+
val selector = erasure(tree.args.head.tpe.widen)
42+
43+
val scTrait = scrutinee.typeSymbol is Flags.Trait
44+
val scClass =
45+
scrutinee.typeSymbol.isClass &&
46+
!(scrutinee.typeSymbol is Flags.Trait) &&
47+
!(scrutinee.typeSymbol is Flags.Module)
48+
49+
val scClassNonFinal = scClass && !scrutinee.typeSymbol.is(Flags.Final)
50+
val scFinalClass = scClass && (scrutinee.typeSymbol is Flags.Final)
51+
52+
val selTrait = selector.typeSymbol is Flags.Trait
53+
val selClass =
54+
selector.typeSymbol.isClass &&
55+
!(selector.typeSymbol is Flags.Trait) &&
56+
!(selector.typeSymbol is Flags.Module)
57+
58+
val selClassNonFinal = scClass && !(selector.typeSymbol is Flags.Final)
59+
val selFinalClass = scClass && (selector.typeSymbol is Flags.Final)
60+
61+
// Cases ---------------------------------
62+
val knownStatically = scFinalClass
63+
64+
val falseIfUnrelated =
65+
(scClassNonFinal && selClassNonFinal) ||
66+
(scClassNonFinal && selFinalClass) ||
67+
(scTrait && selFinalClass)
68+
69+
// Doesn't need to be calculated (since true if others are false)
70+
//val happens =
71+
// (scClassNonFinal && selClassNonFinal) ||
72+
// (scTrait && selClassNonFinal) ||
73+
// (scTrait && selTrait)
74+
75+
val inMatch = s.qualifier.symbol is Flags.Case
76+
77+
if (knownStatically)
78+
handleStaticallyKnown(tree, scrutinee, selector, inMatch, tree.pos)
79+
else if (falseIfUnrelated && !(selector <:< scrutinee))
80+
rewrite(tree, to = false)
81+
else /*if (happens)*/ tree
82+
}
83+
84+
case _ => tree
85+
}
86+
87+
evalTypeApply(tree)
88+
}
89+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
5656

5757
override def transformTry(tree: tpd.Try)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
5858
val selector =
59-
ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic, defn.ThrowableType, coord = tree.pos)
59+
ctx.newSymbol(ctx.owner, ctx.freshName("ex").toTermName, Flags.Synthetic | Flags.Case, defn.ThrowableType, coord = tree.pos)
6060
val sel = Ident(selector.termRef).withPos(tree.pos)
6161
val rethrow = tpd.CaseDef(EmptyTree, EmptyTree, Throw(ref(selector)))
6262
val newCases = tpd.CaseDef(
@@ -80,7 +80,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
8080
// assert(owner ne null); assert(owner ne NoSymbol)
8181
def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x", owner: Symbol = ctx.owner) = {
8282
ctr += 1
83-
ctx.newSymbol(owner, ctx.freshName(prefix + ctr).toTermName, Flags.Synthetic, tp, coord = pos)
83+
ctx.newSymbol(owner, ctx.freshName(prefix + ctr).toTermName, Flags.Synthetic | Flags.Case, tp, coord = pos)
8484
}
8585

8686
def newSynthCaseLabel(name: String, tpe:Type, owner: Symbol = ctx.owner) =

0 commit comments

Comments
 (0)