diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-05-24 13:38:28 +0200 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2016-05-26 13:20:10 +0200 |
commit | efee4faad746736c41d60ab0b488b615e7b54ed4 (patch) | |
tree | 107c709b192cb27f2a4d0adc78cd6fd579ea9b67 /src | |
parent | 7930293ee1f2d169956d472631487a637b744eb2 (diff) | |
download | dotty-efee4faad746736c41d60ab0b488b615e7b54ed4.tar.gz dotty-efee4faad746736c41d60ab0b488b615e7b54ed4.tar.bz2 dotty-efee4faad746736c41d60ab0b488b615e7b54ed4.zip |
Rewrite `TypeApply` to null-check on rewrite to true, add docstrings
Diffstat (limited to 'src')
-rw-r--r-- | src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala | 105 |
1 files changed, 82 insertions, 23 deletions
diff --git a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala index d4483bb18..a8d5b7685 100644 --- a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala +++ b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala @@ -1,38 +1,95 @@ 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} - +import core._ +import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ +import TypeUtils._, TypeErasure._ + + +/** Implements partial `isInstanceOf` evaluation according to the matrix on: + * https://github.com/lampepfl/dotty/issues/1255 + * + * This is a generalized solution to raising an error on unreachable match + * cases and warnings on other statically known results of `isInstanceOf`. + * + * Steps taken: + * + * 1. evalTypeApply will establish the matrix and choose the appropriate + * handling for the case: + * 2. a) handleStaticallyKnown + * b) falseIfUnrelated with `scrutinee <:< selector` + * c) handleFalseUnrelated + * d) leave as is (aka `happens`) + * 3. Rewrite according to step taken in `2` + */ class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer => import dotty.tools.dotc.ast.tpd._ - def phaseName = "reachabilityChecker" - + def phaseName = "isInstanceOfEvaluator" 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 = + /** Handles the four cases of statically known `isInstanceOf`s and gives + * the correct warnings, or an error if statically known to be false in + * match + */ + def handleStaticallyKnown(tree: Select, 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 + ctx.error( + s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", + Position(pos.start - 5, pos.end - 5) + ) + rewrite(tree, to = false) } 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) + 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) + 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 + /** Rewrites cases with unrelated types */ + def handleFalseUnrelated(tree: Select, scrutinee: Type, selector: Type, inMatch: Boolean) = + if (inMatch) { + ctx.error( + s"will never match since `${selector.show}` is not a subclass of `${scrutinee.show}`", + Position(tree.pos.start - 5, tree.pos.end - 5) + ) + rewrite(tree, to = false) + } else { + ctx.warning( + s"will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}`", + tree.pos + ) + rewrite(tree, to = false) + } + /** Rewrites the select to a boolean if `to` is false or if the qualifier + * is a primitive. + * + * If `to` is set to true and the qualifier is not a primitive, the + * instanceOf is replaced by a null check, since: + * + * `srutinee.isInstanceOf[Selector]` if `scrutinee eq null` + */ + def rewrite(tree: Select, to: Boolean): Tree = + if (!to || tree.qualifier.tpe.widen.isPrimitiveValueType) + Literal(Constant(to)) + else + Apply(tree.qualifier.select(defn.Object_ne), List(Literal(Constant(null)))) + + /** Attempts to rewrite TypeApply to either `scrutinee ne null` or a + * constant + */ def evalTypeApply(tree: TypeApply): Tree = if (tree.symbol != defn.Any_isInstanceOf) tree else tree.fun match { @@ -66,19 +123,21 @@ class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer => (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 happens = + (scClassNonFinal && selClassNonFinal) || + (scTrait && selClassNonFinal) || + (scTrait && selTrait) val inMatch = s.qualifier.symbol is Flags.Case if (knownStatically) - handleStaticallyKnown(tree, scrutinee, selector, inMatch, tree.pos) + handleStaticallyKnown(s, scrutinee, selector, inMatch, tree.pos) + else if (falseIfUnrelated && scrutinee <:< selector) + rewrite(s, to = true) else if (falseIfUnrelated && !(selector <:< scrutinee)) - rewrite(tree, to = false) - else /*if (happens)*/ tree + handleFalseUnrelated(s, scrutinee, selector, inMatch) + else if (happens) tree + else tree } case _ => tree |