|
1 | 1 | package dotty.tools.dotc
|
2 | 2 | package transform
|
3 | 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 | 4 | import dotty.tools.dotc.util.Positions._
|
10 |
| -import dotty.tools.dotc.core.TypeErasure._ |
11 | 5 | import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
|
12 |
| - |
| 6 | +import core._ |
| 7 | +import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ |
| 8 | +import TypeUtils._, TypeErasure._ |
| 9 | + |
| 10 | + |
| 11 | +/** Implements partial `isInstanceOf` evaluation according to the matrix on: |
| 12 | + * https://github.com/lampepfl/dotty/issues/1255 |
| 13 | + * |
| 14 | + * This is a generalized solution to raising an error on unreachable match |
| 15 | + * cases and warnings on other statically known results of `isInstanceOf`. |
| 16 | + * |
| 17 | + * Steps taken: |
| 18 | + * |
| 19 | + * 1. evalTypeApply will establish the matrix and choose the appropriate |
| 20 | + * handling for the case: |
| 21 | + * 2. a) handleStaticallyKnown |
| 22 | + * b) falseIfUnrelated with `scrutinee <:< selector` |
| 23 | + * c) handleFalseUnrelated |
| 24 | + * d) leave as is (aka `happens`) |
| 25 | + * 3. Rewrite according to step taken in `2` |
| 26 | + */ |
13 | 27 | class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer =>
|
14 | 28 |
|
15 | 29 | import dotty.tools.dotc.ast.tpd._
|
16 | 30 |
|
17 |
| - def phaseName = "reachabilityChecker" |
18 |
| - |
| 31 | + def phaseName = "isInstanceOfEvaluator" |
19 | 32 | override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
|
20 | 33 | val defn = ctx.definitions
|
21 | 34 |
|
22 |
| - def handleStaticallyKnown(tree: TypeApply, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree = |
| 35 | + /** Handles the four cases of statically known `isInstanceOf`s and gives |
| 36 | + * the correct warnings, or an error if statically known to be false in |
| 37 | + * match |
| 38 | + */ |
| 39 | + def handleStaticallyKnown(tree: Select, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree = |
23 | 40 | 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 |
| 41 | + ctx.error( |
| 42 | + s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", |
| 43 | + Position(pos.start - 5, pos.end - 5) |
| 44 | + ) |
| 45 | + rewrite(tree, to = false) |
26 | 46 | } 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) |
| 47 | + ctx.warning( |
| 48 | + s"this will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}` (will be optimized away)", |
| 49 | + pos |
| 50 | + ) |
28 | 51 | rewrite(tree, to = false)
|
29 | 52 | } 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) |
| 53 | + ctx.warning( |
| 54 | + s"this will always yield true since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)", |
| 55 | + pos |
| 56 | + ) |
31 | 57 | rewrite(tree, to = true)
|
32 | 58 | } else /* if (scrutinee <:< selector && inMatch) */ rewrite(tree, to = true)
|
33 | 59 |
|
34 |
| - def rewrite(tree: TypeApply, to: Boolean): Tree = tree |
| 60 | + /** Rewrites cases with unrelated types */ |
| 61 | + def handleFalseUnrelated(tree: Select, scrutinee: Type, selector: Type, inMatch: Boolean) = |
| 62 | + if (inMatch) { |
| 63 | + ctx.error( |
| 64 | + s"will never match since `${selector.show}` is not a subclass of `${scrutinee.show}`", |
| 65 | + Position(tree.pos.start - 5, tree.pos.end - 5) |
| 66 | + ) |
| 67 | + rewrite(tree, to = false) |
| 68 | + } else { |
| 69 | + ctx.warning( |
| 70 | + s"will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}`", |
| 71 | + tree.pos |
| 72 | + ) |
| 73 | + rewrite(tree, to = false) |
| 74 | + } |
35 | 75 |
|
| 76 | + /** Rewrites the select to a boolean if `to` is false or if the qualifier |
| 77 | + * is a primitive. |
| 78 | + * |
| 79 | + * If `to` is set to true and the qualifier is not a primitive, the |
| 80 | + * instanceOf is replaced by a null check, since: |
| 81 | + * |
| 82 | + * `srutinee.isInstanceOf[Selector]` if `scrutinee eq null` |
| 83 | + */ |
| 84 | + def rewrite(tree: Select, to: Boolean): Tree = |
| 85 | + if (!to || tree.qualifier.tpe.widen.isPrimitiveValueType) |
| 86 | + Literal(Constant(to)) |
| 87 | + else |
| 88 | + Apply(tree.qualifier.select(defn.Object_ne), List(Literal(Constant(null)))) |
| 89 | + |
| 90 | + /** Attempts to rewrite TypeApply to either `scrutinee ne null` or a |
| 91 | + * constant |
| 92 | + */ |
36 | 93 | def evalTypeApply(tree: TypeApply): Tree =
|
37 | 94 | if (tree.symbol != defn.Any_isInstanceOf) tree
|
38 | 95 | else tree.fun match {
|
@@ -66,19 +123,21 @@ class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer =>
|
66 | 123 | (scClassNonFinal && selFinalClass) ||
|
67 | 124 | (scTrait && selFinalClass)
|
68 | 125 |
|
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) |
| 126 | + val happens = |
| 127 | + (scClassNonFinal && selClassNonFinal) || |
| 128 | + (scTrait && selClassNonFinal) || |
| 129 | + (scTrait && selTrait) |
74 | 130 |
|
75 | 131 | val inMatch = s.qualifier.symbol is Flags.Case
|
76 | 132 |
|
77 | 133 | if (knownStatically)
|
78 |
| - handleStaticallyKnown(tree, scrutinee, selector, inMatch, tree.pos) |
| 134 | + handleStaticallyKnown(s, scrutinee, selector, inMatch, tree.pos) |
| 135 | + else if (falseIfUnrelated && scrutinee <:< selector) |
| 136 | + rewrite(s, to = true) |
79 | 137 | else if (falseIfUnrelated && !(selector <:< scrutinee))
|
80 |
| - rewrite(tree, to = false) |
81 |
| - else /*if (happens)*/ tree |
| 138 | + handleFalseUnrelated(s, scrutinee, selector, inMatch) |
| 139 | + else if (happens) tree |
| 140 | + else tree |
82 | 141 | }
|
83 | 142 |
|
84 | 143 | case _ => tree
|
|
0 commit comments