aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.md7
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala2
-rw-r--r--src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala168
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala12
-rw-r--r--test/test/AsmConverters.scala256
-rw-r--r--test/test/AsmNode.scala61
-rw-r--r--test/test/DottyBytecodeTest.scala208
-rw-r--r--test/test/DottyBytecodeTests.scala188
-rw-r--r--tests/disabled/pos-scala2/i1059.scala (renamed from tests/pos-scala2/i1059.scala)0
-rw-r--r--tests/disabled/pos/t3480.scala (renamed from tests/pos/t3480.scala)0
-rw-r--r--tests/neg/i1255.scala6
-rw-r--r--tests/pos/t1168.scala2
-rw-r--r--tests/run/isInstanceOf-eval.check2
-rw-r--r--tests/run/isInstanceOf-eval.scala15
-rw-r--r--tests/run/t6637.scala3
17 files changed, 924 insertions, 9 deletions
diff --git a/AUTHORS.md b/AUTHORS.md
index 4e8bb3d30..4d54d2ee3 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -45,3 +45,10 @@ The majority of the dotty codebase is new code, with the exception of the compon
`dotty.tools.io`
> The I/O support library was adapted from current Scala compiler. Original authors were Paul Phillips and others.
+
+`dotty.test.DottyBytecodeTest`
+
+> Is an adaptation of the bytecode testing from
+> [scala/scala](https://github.com/scala/scala). It has been reworked to fit
+> the needs of dotty. Original authors include: Adrian Moors, Lukas Rytz,
+> Grzegorz Kossakowski, Paul Phillips
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/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index bcc986755..5cb373cfd 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -486,6 +486,8 @@ class Definitions {
def TASTYLongSignatureAnnot(implicit ctx: Context) = TASTYLongSignatureAnnotType.symbol.asClass
lazy val TailrecAnnotType = ctx.requiredClassRef("scala.annotation.tailrec")
def TailrecAnnot(implicit ctx: Context) = TailrecAnnotType.symbol.asClass
+ lazy val SwitchAnnotType = ctx.requiredClassRef("scala.annotation.switch")
+ def SwitchAnnot(implicit ctx: Context) = SwitchAnnotType.symbol.asClass
lazy val ThrowsAnnotType = ctx.requiredClassRef("scala.throws")
def ThrowsAnnot(implicit ctx: Context) = ThrowsAnnotType.symbol.asClass
lazy val TransientAnnotType = ctx.requiredClassRef("scala.transient")
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 71ea6d2b3..cec6fd3b1 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -107,7 +107,7 @@ object Types {
* It makes no sense for it to be an alias type because isRef would always
* return false in that case.
*/
- def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match {
+ def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
case this1: TypeRef =>
this1.info match { // see comment in Namer#typeDefSig
case TypeAlias(tp) => tp.isRef(sym)
diff --git a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala
new file mode 100644
index 000000000..6765604c8
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala
@@ -0,0 +1,168 @@
+package dotty.tools.dotc
+package transform
+
+import dotty.tools.dotc.util.Positions._
+import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
+import core._
+import Contexts.Context, Types._, Constants._, Decorators._, Symbols._
+import TypeUtils._, TypeErasure._, Flags._
+
+
+/** Implements partial evaluation of `sc.isInstanceOf[Sel]` according to:
+ *
+ * +-------------+----------------------------+----------------------------+------------------+
+ * | Sel\sc | trait | class | final class |
+ * +-------------+----------------------------+----------------------------+------------------+
+ * | trait | ? | ? | statically known |
+ * | class | ? | false if classes unrelated | statically known |
+ * | final class | false if classes unrelated | false if classes unrelated | statically known |
+ * +-------------+----------------------------+----------------------------+------------------+
+ *
+ * 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) Sel/sc is a value class or scrutinee is `Any`
+ * b) handleStaticallyKnown
+ * c) falseIfUnrelated with `scrutinee <:< selector`
+ * d) handleFalseUnrelated
+ * e) 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 = "isInstanceOfEvaluator"
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val defn = ctx.definitions
+
+ /** 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(select: Select, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree = {
+ val scrutineeSubSelector = scrutinee <:< selector
+ if (!scrutineeSubSelector && inMatch) {
+ 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(select, to = false)
+ } else if (!scrutineeSubSelector && !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(select, to = false)
+ } else if (scrutineeSubSelector && !inMatch) {
+ ctx.warning(
+ s"this will always yield true if the scrutinee is non-null, since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)",
+ pos
+ )
+ rewrite(select, to = true)
+ } else /* if (scrutineeSubSelector && inMatch) */ rewrite(select, to = true)
+ }
+
+ /** Rewrites cases with unrelated types */
+ def handleFalseUnrelated(select: 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(select.pos.start - 5, select.pos.end - 5)
+ )
+ rewrite(select, to = false)
+ } else {
+ ctx.warning(
+ s"will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}`",
+ select.pos
+ )
+ rewrite(select, to = false)
+ }
+
+ /** Rewrites the select to a boolean if `to` is false or if the qualifier
+ * is a value class.
+ *
+ * If `to` is set to true and the qualifier is not a primitive, the
+ * instanceOf is replaced by a null check, since:
+ *
+ * `scrutinee.isInstanceOf[Selector]` if `scrutinee eq null`
+ */
+ def rewrite(tree: Select, to: Boolean): Tree =
+ if (!to || !tree.qualifier.tpe.widen.derivesFrom(defn.AnyRefAlias)) {
+ val literal = Literal(Constant(to))
+ if (!isPureExpr(tree.qualifier)) Block(List(tree.qualifier), literal)
+ else literal
+ } 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 {
+ case s: Select => {
+ val scrutinee = erasure(s.qualifier.tpe.widen)
+ val selector = erasure(tree.args.head.tpe.widen)
+
+ val scTrait = scrutinee.typeSymbol is Trait
+ val scClass =
+ scrutinee.typeSymbol.isClass &&
+ !(scrutinee.typeSymbol is Trait) &&
+ !(scrutinee.typeSymbol is Module)
+
+ val scClassNonFinal = scClass && !(scrutinee.typeSymbol is Final)
+ val scFinalClass = scClass && (scrutinee.typeSymbol is Final)
+
+ val selTrait = selector.typeSymbol is Trait
+ val selClass =
+ selector.typeSymbol.isClass &&
+ !(selector.typeSymbol is Trait) &&
+ !(selector.typeSymbol is Module)
+
+ val selClassNonFinal = scClass && !(selector.typeSymbol is Final)
+ val selFinalClass = scClass && (selector.typeSymbol is Final)
+
+ // Cases ---------------------------------
+ val valueClassesOrAny =
+ ValueClasses.isDerivedValueClass(scrutinee.typeSymbol) ||
+ ValueClasses.isDerivedValueClass(selector.typeSymbol) ||
+ scrutinee == defn.ObjectType
+
+ val knownStatically = scFinalClass
+
+ val falseIfUnrelated =
+ (scClassNonFinal && selClassNonFinal) ||
+ (scClassNonFinal && selFinalClass) ||
+ (scTrait && selFinalClass)
+
+ val happens =
+ (scClassNonFinal && selClassNonFinal) ||
+ (scTrait && selClassNonFinal) ||
+ (scTrait && selTrait)
+
+ val inMatch = s.qualifier.symbol is Case
+
+ if (valueClassesOrAny) tree
+ else if (knownStatically)
+ handleStaticallyKnown(s, scrutinee, selector, inMatch, tree.pos)
+ else if (falseIfUnrelated && scrutinee <:< selector)
+ // scrutinee is a subtype of the selector, safe to rewrite
+ rewrite(s, to = true)
+ else if (falseIfUnrelated && !(selector <:< scrutinee))
+ // selector and scrutinee are unrelated
+ handleFalseUnrelated(s, scrutinee, selector, inMatch)
+ else if (happens) tree
+ else 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..24d0e9ae0 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) =
@@ -306,12 +306,11 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Symbol => Tree], unchecked: Boolean): Option[Tree] = {
// TODO Deal with guards?
- def isSwitchableType(tpe: Type): Boolean = {
+ def isSwitchableType(tpe: Type): Boolean =
(tpe isRef defn.IntClass) ||
(tpe isRef defn.ByteClass) ||
(tpe isRef defn.ShortClass) ||
(tpe isRef defn.CharClass)
- }
object IntEqualityTestTreeMaker {
def unapply(treeMaker: EqualityTestTreeMaker): Option[Int] = treeMaker match {
@@ -423,7 +422,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
Match(intScrut, newCases :+ defaultCase)
}
- if (isSwitchableType(scrut.tpe.widenDealias) && cases.forall(isSwitchCase)) {
+ val dealiased = scrut.tpe.widenDealias
+ if (isSwitchableType(dealiased) && cases.forall(isSwitchCase)) {
val valuesToCases = cases.map(extractSwitchCase)
val values = valuesToCases.map(_._1)
if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) {
@@ -433,6 +433,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
Some(makeSwitch(valuesToCases))
}
} else {
+ if (dealiased hasAnnotation defn.SwitchAnnot)
+ ctx.warning("failed to emit switch for `@switch` annotated match")
None
}
}
diff --git a/test/test/AsmConverters.scala b/test/test/AsmConverters.scala
new file mode 100644
index 000000000..5734e8660
--- /dev/null
+++ b/test/test/AsmConverters.scala
@@ -0,0 +1,256 @@
+package test
+
+import scala.tools.asm
+import asm._
+import asm.tree._
+import scala.collection.JavaConverters._
+
+/** Makes using ASM from tests more convenient.
+ *
+ * Wraps ASM instructions in case classes so that equals and toString work
+ * for the purpose of bytecode diffing and pretty printing.
+ */
+object ASMConverters {
+ import asm.{tree => t}
+
+ /**
+ * Transform the instructions of an ASM Method into a list of [[Instruction]]s.
+ */
+ def instructionsFromMethod(meth: t.MethodNode): List[Instruction] = new AsmToScala(meth).instructions
+
+ def convertMethod(meth: t.MethodNode): Method = new AsmToScala(meth).method
+
+ implicit class RichInstructionLists(val self: List[Instruction]) extends AnyVal {
+ def === (other: List[Instruction]) = equivalentBytecode(self, other)
+
+ def dropLinesFrames = self.filterNot(i => i.isInstanceOf[LineNumber] || i.isInstanceOf[FrameEntry])
+
+ private def referencedLabels(instruction: Instruction): Set[Instruction] = instruction match {
+ case Jump(op, label) => Set(label)
+ case LookupSwitch(op, dflt, keys, labels) => (dflt :: labels).toSet
+ case TableSwitch(op, min, max, dflt, labels) => (dflt :: labels).toSet
+ case LineNumber(line, start) => Set(start)
+ case _ => Set.empty
+ }
+
+ def dropStaleLabels = {
+ val definedLabels: Set[Instruction] = self.filter(_.isInstanceOf[Label]).toSet
+ val usedLabels: Set[Instruction] = self.flatMap(referencedLabels)(collection.breakOut)
+ self.filterNot(definedLabels diff usedLabels)
+ }
+
+ def dropNonOp = dropLinesFrames.dropStaleLabels
+
+ def summary: List[Any] = dropNonOp map {
+ case i: Invoke => i.name
+ case i => i.opcode
+ }
+
+ def summaryText: String = {
+ def comment(i: Instruction) = i match {
+ case j: Jump => s" /*${j.label.offset}*/"
+ case l: Label => s" /*${l.offset}*/"
+ case _ => ""
+ }
+ dropNonOp.map({
+ case i: Invoke => s""""${i.name}""""
+ case ins => opcodeToString(ins.opcode, ins.opcode) + comment(ins)
+ }).mkString("List(", ", ", ")")
+ }
+ }
+
+ def opcodeToString(op: Int, default: Any = "?"): String = {
+ import scala.tools.asm.util.Printer.OPCODES
+ if (OPCODES.isDefinedAt(op)) OPCODES(op) else default.toString
+ }
+
+ sealed abstract class Instruction extends Product {
+ def opcode: Int
+
+ // toString such that the first field, "opcode: Int", is printed textually.
+ final override def toString() = {
+ val printOpcode = opcode != -1
+ productPrefix + (
+ if (printOpcode) Iterator(opcodeToString(opcode)) ++ productIterator.drop(1)
+ else productIterator
+ ).mkString("(", ", ", ")")
+ }
+ }
+
+ case class Method(instructions: List[Instruction], handlers: List[ExceptionHandler], localVars: List[LocalVariable])
+
+ case class Field (opcode: Int, owner: String, name: String, desc: String) extends Instruction
+ case class Incr (opcode: Int, `var`: Int, incr: Int) extends Instruction
+ case class Op (opcode: Int) extends Instruction
+ case class IntOp (opcode: Int, operand: Int) extends Instruction
+ case class Jump (opcode: Int, label: Label) extends Instruction
+ case class Ldc (opcode: Int, cst: Any) extends Instruction
+ case class LookupSwitch (opcode: Int, dflt: Label, keys: List[Int], labels: List[Label]) extends Instruction
+ case class TableSwitch (opcode: Int, min: Int, max: Int, dflt: Label, labels: List[Label]) extends Instruction
+ case class Invoke (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) extends Instruction
+ case class InvokeDynamic(opcode: Int, name: String, desc: String, bsm: MethodHandle, bsmArgs: List[AnyRef]) extends Instruction
+ case class NewArray (opcode: Int, desc: String, dims: Int) extends Instruction
+ case class TypeOp (opcode: Int, desc: String) extends Instruction
+ case class VarOp (opcode: Int, `var`: Int) extends Instruction
+ case class Label (offset: Int) extends Instruction { def opcode: Int = -1 }
+ case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 }
+ case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 }
+
+ case class MethodHandle(tag: Int, owner: String, name: String, desc: String)
+
+ case class ExceptionHandler(start: Label, end: Label, handler: Label, desc: Option[String])
+ case class LocalVariable(name: String, desc: String, signature: Option[String], start: Label, end: Label, index: Int)
+
+ class AsmToScala(asmMethod: t.MethodNode) {
+
+ def instructions: List[Instruction] = asmMethod.instructions.iterator.asScala.toList map apply
+
+ def method: Method = Method(instructions, convertHandlers(asmMethod), convertLocalVars(asmMethod))
+
+ private def labelIndex(l: t.LabelNode): Int = asmMethod.instructions.indexOf(l)
+
+ private def op(i: t.AbstractInsnNode): Int = i.getOpcode
+
+ private def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList
+
+ // Heterogeneous List[Any] is used in FrameNode: type information about locals / stack values
+ // are stored in a List[Any] (Integer, String or LabelNode), see Javadoc of MethodNode#visitFrame.
+ // Opcodes (eg Opcodes.INTEGER) and Reference types (eg "java/lang/Object") are returned unchanged,
+ // LabelNodes are mapped to their LabelEntry.
+ private def mapOverFrameTypes(is: List[Any]): List[Any] = is map {
+ case i: t.LabelNode => applyLabel(i)
+ case x => x
+ }
+
+ // avoids some casts
+ private def applyLabel(l: t.LabelNode) = this(l: t.AbstractInsnNode).asInstanceOf[Label]
+
+ private def apply(x: t.AbstractInsnNode): Instruction = x match {
+ case i: t.FieldInsnNode => Field (op(i), i.owner, i.name, i.desc)
+ case i: t.IincInsnNode => Incr (op(i), i.`var`, i.incr)
+ case i: t.InsnNode => Op (op(i))
+ case i: t.IntInsnNode => IntOp (op(i), i.operand)
+ case i: t.JumpInsnNode => Jump (op(i), applyLabel(i.label))
+ case i: t.LdcInsnNode => Ldc (op(i), i.cst: Any)
+ case i: t.LookupSwitchInsnNode => LookupSwitch (op(i), applyLabel(i.dflt), lst(i.keys) map (x => x: Int), lst(i.labels) map applyLabel)
+ case i: t.TableSwitchInsnNode => TableSwitch (op(i), i.min, i.max, applyLabel(i.dflt), lst(i.labels) map applyLabel)
+ case i: t.MethodInsnNode => Invoke (op(i), i.owner, i.name, i.desc, i.itf)
+ case i: t.InvokeDynamicInsnNode => InvokeDynamic(op(i), i.name, i.desc, convertMethodHandle(i.bsm), convertBsmArgs(i.bsmArgs))
+ case i: t.MultiANewArrayInsnNode => NewArray (op(i), i.desc, i.dims)
+ case i: t.TypeInsnNode => TypeOp (op(i), i.desc)
+ case i: t.VarInsnNode => VarOp (op(i), i.`var`)
+ case i: t.LabelNode => Label (labelIndex(i))
+ case i: t.FrameNode => FrameEntry (i.`type`, mapOverFrameTypes(lst(i.local)), mapOverFrameTypes(lst(i.stack)))
+ case i: t.LineNumberNode => LineNumber (i.line, applyLabel(i.start))
+ }
+
+ private def convertBsmArgs(a: Array[Object]): List[Object] = a.map({
+ case h: asm.Handle => convertMethodHandle(h)
+ case _ => a // can be: Class, method Type, primitive constant
+ })(collection.breakOut)
+
+ private def convertMethodHandle(h: asm.Handle): MethodHandle = MethodHandle(h.getTag, h.getOwner, h.getName, h.getDesc)
+
+ private def convertHandlers(method: t.MethodNode): List[ExceptionHandler] = {
+ method.tryCatchBlocks.asScala.map(h => ExceptionHandler(applyLabel(h.start), applyLabel(h.end), applyLabel(h.handler), Option(h.`type`)))(collection.breakOut)
+ }
+
+ private def convertLocalVars(method: t.MethodNode): List[LocalVariable] = {
+ method.localVariables.asScala.map(v => LocalVariable(v.name, v.desc, Option(v.signature), applyLabel(v.start), applyLabel(v.end), v.index))(collection.breakOut)
+ }
+ }
+
+ import collection.mutable.{Map => MMap}
+
+ /**
+ * Bytecode is equal modulo local variable numbering and label numbering.
+ */
+ def equivalentBytecode(as: List[Instruction], bs: List[Instruction], varMap: MMap[Int, Int] = MMap(), labelMap: MMap[Int, Int] = MMap()): Boolean = {
+ def same(v1: Int, v2: Int, m: MMap[Int, Int]) = {
+ if (m contains v1) m(v1) == v2
+ else if (m.valuesIterator contains v2) false // v2 is already associated with some different value v1
+ else { m(v1) = v2; true }
+ }
+ def sameVar(v1: Int, v2: Int) = same(v1, v2, varMap)
+ def sameLabel(l1: Label, l2: Label) = same(l1.offset, l2.offset, labelMap)
+ def sameLabels(ls1: List[Label], ls2: List[Label]) = (ls1 corresponds ls2)(sameLabel)
+
+ def sameFrameTypes(ts1: List[Any], ts2: List[Any]) = (ts1 corresponds ts2) {
+ case (t1: Label, t2: Label) => sameLabel(t1, t2)
+ case (x, y) => x == y
+ }
+
+ if (as.isEmpty) bs.isEmpty
+ else if (bs.isEmpty) false
+ else ((as.head, bs.head) match {
+ case (VarOp(op1, v1), VarOp(op2, v2)) => op1 == op2 && sameVar(v1, v2)
+ case (Incr(op1, v1, inc1), Incr(op2, v2, inc2)) => op1 == op2 && sameVar(v1, v2) && inc1 == inc2
+
+ case (l1 @ Label(_), l2 @ Label(_)) => sameLabel(l1, l2)
+ case (Jump(op1, l1), Jump(op2, l2)) => op1 == op2 && sameLabel(l1, l2)
+ case (LookupSwitch(op1, l1, keys1, ls1), LookupSwitch(op2, l2, keys2, ls2)) => op1 == op2 && sameLabel(l1, l2) && keys1 == keys2 && sameLabels(ls1, ls2)
+ case (TableSwitch(op1, min1, max1, l1, ls1), TableSwitch(op2, min2, max2, l2, ls2)) => op1 == op2 && min1 == min2 && max1 == max2 && sameLabel(l1, l2) && sameLabels(ls1, ls2)
+ case (LineNumber(line1, l1), LineNumber(line2, l2)) => line1 == line2 && sameLabel(l1, l2)
+ case (FrameEntry(tp1, loc1, stk1), FrameEntry(tp2, loc2, stk2)) => tp1 == tp2 && sameFrameTypes(loc1, loc2) && sameFrameTypes(stk1, stk2)
+
+ // this needs to go after the above. For example, Label(1) may not equal Label(1), if before
+ // the left 1 was associated with another right index.
+ case (a, b) if a == b => true
+
+ case _ => false
+ }) && equivalentBytecode(as.tail, bs.tail, varMap, labelMap)
+ }
+
+ def applyToMethod(method: t.MethodNode, instructions: List[Instruction]): Unit = {
+ val asmLabel = createLabelNodes(instructions)
+ instructions.foreach(visitMethod(method, _, asmLabel))
+ }
+
+ /**
+ * Convert back a [[Method]] to ASM land. The code is emitted into the parameter `asmMethod`.
+ */
+ def applyToMethod(asmMethod: t.MethodNode, method: Method): Unit = {
+ val asmLabel = createLabelNodes(method.instructions)
+ method.instructions.foreach(visitMethod(asmMethod, _, asmLabel))
+ method.handlers.foreach(h => asmMethod.visitTryCatchBlock(asmLabel(h.start), asmLabel(h.end), asmLabel(h.handler), h.desc.orNull))
+ method.localVars.foreach(v => asmMethod.visitLocalVariable(v.name, v.desc, v.signature.orNull, asmLabel(v.start), asmLabel(v.end), v.index))
+ }
+
+ private def createLabelNodes(instructions: List[Instruction]): Map[Label, asm.Label] = {
+ val labels = instructions collect {
+ case l: Label => l
+ }
+ assert(labels.distinct == labels, s"Duplicate labels in: $labels")
+ labels.map(l => (l, new asm.Label())).toMap
+ }
+
+ private def frameTypesToAsm(l: List[Any], asmLabel: Map[Label, asm.Label]): List[Object] = l map {
+ case l: Label => asmLabel(l)
+ case x => x.asInstanceOf[Object]
+ }
+
+ def unconvertMethodHandle(h: MethodHandle): asm.Handle = new asm.Handle(h.tag, h.owner, h.name, h.desc)
+ def unconvertBsmArgs(a: List[Object]): Array[Object] = a.map({
+ case h: MethodHandle => unconvertMethodHandle(h)
+ case o => o
+ })(collection.breakOut)
+
+ private def visitMethod(method: t.MethodNode, instruction: Instruction, asmLabel: Map[Label, asm.Label]): Unit = instruction match {
+ case Field(op, owner, name, desc) => method.visitFieldInsn(op, owner, name, desc)
+ case Incr(op, vr, incr) => method.visitIincInsn(vr, incr)
+ case Op(op) => method.visitInsn(op)
+ case IntOp(op, operand) => method.visitIntInsn(op, operand)
+ case Jump(op, label) => method.visitJumpInsn(op, asmLabel(label))
+ case Ldc(op, cst) => method.visitLdcInsn(cst)
+ case LookupSwitch(op, dflt, keys, labels) => method.visitLookupSwitchInsn(asmLabel(dflt), keys.toArray, (labels map asmLabel).toArray)
+ case TableSwitch(op, min, max, dflt, labels) => method.visitTableSwitchInsn(min, max, asmLabel(dflt), (labels map asmLabel).toArray: _*)
+ case Invoke(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
+ case InvokeDynamic(op, name, desc, bsm, bsmArgs) => method.visitInvokeDynamicInsn(name, desc, unconvertMethodHandle(bsm), unconvertBsmArgs(bsmArgs))
+ case NewArray(op, desc, dims) => method.visitMultiANewArrayInsn(desc, dims)
+ case TypeOp(op, desc) => method.visitTypeInsn(op, desc)
+ case VarOp(op, vr) => method.visitVarInsn(op, vr)
+ case l: Label => method.visitLabel(asmLabel(l))
+ case FrameEntry(tp, local, stack) => method.visitFrame(tp, local.length, frameTypesToAsm(local, asmLabel).toArray, stack.length, frameTypesToAsm(stack, asmLabel).toArray)
+ case LineNumber(line, start) => method.visitLineNumber(line, asmLabel(start))
+ }
+}
diff --git a/test/test/AsmNode.scala b/test/test/AsmNode.scala
new file mode 100644
index 000000000..80211e2af
--- /dev/null
+++ b/test/test/AsmNode.scala
@@ -0,0 +1,61 @@
+package test
+
+import java.lang.reflect.Modifier
+import scala.tools.asm
+import asm._
+import asm.tree._
+import scala.collection.JavaConverters._
+
+sealed trait AsmNode[+T] {
+ def node: T
+ def access: Int
+ def desc: String
+ def name: String
+ def signature: String
+ def attrs: List[Attribute]
+ def visibleAnnotations: List[AnnotationNode]
+ def invisibleAnnotations: List[AnnotationNode]
+ def characteristics = f"$name%15s $desc%-30s$accessString$sigString"
+ def erasedCharacteristics = f"$name%15s $desc%-30s$accessString"
+
+ private def accessString = if (access == 0) "" else " " + Modifier.toString(access)
+ private def sigString = if (signature == null) "" else " " + signature
+ override def toString = characteristics
+}
+
+object AsmNode {
+ type AsmMethod = AsmNode[MethodNode]
+ type AsmField = AsmNode[FieldNode]
+ type AsmMember = AsmNode[_]
+
+ implicit class ClassNodeOps(val node: ClassNode) {
+ def fieldsAndMethods: List[AsmMember] = {
+ val xs: List[AsmMember] = (
+ node.methods.asScala.toList.map(x => (x: AsmMethod))
+ ++ node.fields.asScala.toList.map(x => (x: AsmField))
+ )
+ xs sortBy (_.characteristics)
+ }
+ }
+ implicit class AsmMethodNode(val node: MethodNode) extends AsmNode[MethodNode] {
+ def access: Int = node.access
+ def desc: String = node.desc
+ def name: String = node.name
+ def signature: String = node.signature
+ def attrs: List[Attribute] = node.attrs.asScala.toList
+ def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
+ def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
+ }
+ implicit class AsmFieldNode(val node: FieldNode) extends AsmNode[FieldNode] {
+ def access: Int = node.access
+ def desc: String = node.desc
+ def name: String = node.name
+ def signature: String = node.signature
+ def attrs: List[Attribute] = node.attrs.asScala.toList
+ def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
+ def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
+ }
+
+ def apply(node: MethodNode): AsmMethodNode = new AsmMethodNode(node)
+ def apply(node: FieldNode): AsmFieldNode = new AsmFieldNode(node)
+}
diff --git a/test/test/DottyBytecodeTest.scala b/test/test/DottyBytecodeTest.scala
new file mode 100644
index 000000000..f2218d4b6
--- /dev/null
+++ b/test/test/DottyBytecodeTest.scala
@@ -0,0 +1,208 @@
+package test
+
+import dotty.tools.dotc.core.Contexts.{Context, ContextBase}
+import dotty.tools.dotc.core.Phases.Phase
+import dotty.tools.backend.jvm.GenBCode
+import dotty.tools.dotc.Compiler
+
+import scala.reflect.io.{VirtualDirectory => Directory}
+import scala.tools.asm
+import asm._
+import asm.tree._
+import scala.collection.JavaConverters._
+
+import scala.tools.nsc.util.JavaClassPath
+import scala.collection.JavaConverters._
+import scala.tools.asm.{ClassWriter, ClassReader}
+import scala.tools.asm.tree._
+import java.io.{File => JFile, InputStream}
+
+private[test] class TestGenBCode(val outDir: String) extends GenBCode {
+ override def phaseName: String = "testGenBCode"
+ val virtualDir = new Directory(outDir, None)
+ override def outputDir(implicit ctx: Context) = virtualDir
+}
+
+trait DottyBytecodeTest extends DottyTest {
+ import AsmNode._
+ import ASMConverters._
+
+ protected object Opcode {
+ val newarray = 188
+ val anewarray = 189
+ val multianewarray = 197
+
+ val boolean = 4
+ val char = 5
+ val float = 6
+ val double = 7
+ val byte = 8
+ val short = 9
+ val int = 10
+ val long = 11
+
+ val boxedUnit = "scala/runtime/BoxedUnit"
+ val javaString = "java/lang/String"
+ }
+
+ private def bCodeCheckingComp(phase: TestGenBCode)(check: Directory => Unit) =
+ new Compiler {
+ override def phases = {
+ val updatedPhases = {
+ def replacePhase: Phase => Phase =
+ { p => if (p.phaseName == "genBCode") phase else p }
+
+ for (phaseList <- super.phases) yield phaseList.map(replacePhase)
+ }
+
+ val checkerPhase = List(List(new Phase {
+ def phaseName = "assertionChecker"
+ override def run(implicit ctx: Context): Unit =
+ check(phase.virtualDir)
+ }))
+
+ updatedPhases ::: checkerPhase
+ }
+ }
+
+ private def outPath(obj: Any) =
+ "/genBCodeTest" + math.abs(obj.hashCode) + System.currentTimeMillis
+
+ /** Checks source code from raw string */
+ def checkBCode(source: String)(assertion: Directory => Unit) = {
+ val comp = bCodeCheckingComp(new TestGenBCode(outPath(source)))(assertion)
+ comp.rootContext(ctx)
+ comp.newRun.compile(source)
+ }
+
+ /** Checks actual _files_ referenced in `sources` list */
+ def checkBCode(sources: List[String])(assertion: Directory => Unit) = {
+ val comp = bCodeCheckingComp(new TestGenBCode(outPath(sources)))(assertion)
+ comp.rootContext(ctx)
+ comp.newRun.compile(sources)
+ }
+
+ protected def loadClassNode(input: InputStream, skipDebugInfo: Boolean = true): ClassNode = {
+ val cr = new ClassReader(input)
+ val cn = new ClassNode()
+ cr.accept(cn, if (skipDebugInfo) ClassReader.SKIP_DEBUG else 0)
+ cn
+ }
+
+ protected def getMethod(classNode: ClassNode, name: String): MethodNode =
+ classNode.methods.asScala.find(_.name == name) getOrElse
+ sys.error(s"Didn't find method '$name' in class '${classNode.name}'")
+
+ def diffInstructions(isa: List[Instruction], isb: List[Instruction]): String = {
+ val len = Math.max(isa.length, isb.length)
+ val sb = new StringBuilder
+ if (len > 0 ) {
+ val width = isa.map(_.toString.length).max
+ val lineWidth = len.toString.length
+ (1 to len) foreach { line =>
+ val isaPadded = isa.map(_.toString) orElse Stream.continually("")
+ val isbPadded = isb.map(_.toString) orElse Stream.continually("")
+ val a = isaPadded(line-1)
+ val b = isbPadded(line-1)
+
+ sb append (s"""$line${" " * (lineWidth-line.toString.length)} ${if (a==b) "==" else "<>"} $a${" " * (width-a.length)} | $b""")
+ }
+ }
+ sb.toString
+ }
+
+ /**************************** Comparison Methods ****************************/
+ def verifySwitch(method: MethodNode, shouldFail: Boolean = false, debug: Boolean = false): Boolean = {
+ val instructions = instructionsFromMethod(method)
+
+ val succ = instructions
+ .collect {
+ case x: TableSwitch => x
+ case x: LookupSwitch => x
+ }
+ .length > 0
+
+ if (debug || !succ && !shouldFail || succ && shouldFail)
+ instructions.foreach(Console.err.println)
+
+ succ && !shouldFail || shouldFail && !succ
+ }
+
+ def sameBytecode(methA: MethodNode, methB: MethodNode) = {
+ val isa = instructionsFromMethod(methA)
+ val isb = instructionsFromMethod(methB)
+ assert(isa == isb, s"Bytecode wasn't same:\n${diffInstructions(isa, isb)}")
+ }
+
+ def similarBytecode(
+ methA: MethodNode,
+ methB: MethodNode,
+ similar: (List[Instruction], List[Instruction]) => Boolean
+ ) = {
+ val isa = instructionsFromMethod(methA)
+ val isb = instructionsFromMethod(methB)
+ assert(
+ similar(isa, isb),
+ s"""|Bytecode wasn't similar according to the provided predicate:
+ |${diffInstructions(isa, isb)}""".stripMargin)
+ }
+
+ def sameMethodAndFieldSignatures(clazzA: ClassNode, clazzB: ClassNode) =
+ sameCharacteristics(clazzA, clazzB)(_.characteristics)
+
+ /**
+ * Same as sameMethodAndFieldSignatures, but ignoring generic signatures.
+ * This allows for methods which receive the same descriptor but differing
+ * generic signatures. In particular, this happens with value classes, which
+ * get a generic signature where a method written in terms of the underlying
+ * values does not.
+ */
+ def sameMethodAndFieldDescriptors(clazzA: ClassNode, clazzB: ClassNode): Unit = {
+ val (succ, msg) = sameCharacteristics(clazzA, clazzB)(_.erasedCharacteristics)
+ assert(succ, msg)
+ }
+
+ private def sameCharacteristics(clazzA: ClassNode, clazzB: ClassNode)(f: AsmNode[_] => String): (Boolean, String) = {
+ val ms1 = clazzA.fieldsAndMethods.toIndexedSeq
+ val ms2 = clazzB.fieldsAndMethods.toIndexedSeq
+ val name1 = clazzA.name
+ val name2 = clazzB.name
+
+ if (ms1.length != ms2.length) {
+ (false, s"Different member counts in $name1 and $name2")
+ } else {
+ val msg = new StringBuilder
+ val success = (ms1, ms2).zipped forall { (m1, m2) =>
+ val c1 = f(m1)
+ val c2 = f(m2).replaceAllLiterally(name2, name1)
+ if (c1 == c2)
+ msg append (s"[ok] $m1")
+ else
+ msg append (s"[fail]\n in $name1: $c1\n in $name2: $c2")
+
+ c1 == c2
+ }
+
+ (success, msg.toString)
+ }
+ }
+
+ def correctNumberOfNullChecks(expectedChecks: Int, insnList: InsnList) = {
+ /** Is given instruction a null check?
+ *
+ * This will detect direct null comparison as in
+ * if (x == null) ...
+ * and not indirect as in
+ * val foo = null
+ * if (x == foo) ...
+ */
+ def isNullCheck(node: asm.tree.AbstractInsnNode): Boolean = {
+ val opcode = node.getOpcode
+ (opcode == asm.Opcodes.IFNULL) || (opcode == asm.Opcodes.IFNONNULL)
+ }
+ val actualChecks = insnList.iterator.asScala.count(isNullCheck)
+ assert(expectedChecks == actualChecks,
+ s"Wrong number of null checks ($actualChecks), expected: $expectedChecks"
+ )
+ }
+}
diff --git a/test/test/DottyBytecodeTests.scala b/test/test/DottyBytecodeTests.scala
new file mode 100644
index 000000000..5b5989474
--- /dev/null
+++ b/test/test/DottyBytecodeTests.scala
@@ -0,0 +1,188 @@
+package test
+
+import org.junit.Assert._
+import org.junit.Test
+
+class TestBCode extends DottyBytecodeTest {
+ import ASMConverters._
+ @Test def nullChecks = {
+ val source = """
+ |class Foo {
+ | def foo(x: AnyRef): Int = {
+ | val bool = x == null
+ | if (x != null) 1
+ | else 0
+ | }
+ |}
+ """.stripMargin
+
+ checkBCode(source) { dir =>
+ val clsIn = dir.lookupName("Foo.class", directory = false).input
+ val clsNode = loadClassNode(clsIn)
+ val methodNode = getMethod(clsNode, "foo")
+ correctNumberOfNullChecks(2, methodNode.instructions)
+ }
+ }
+
+ /** This test verifies that simple matches are transformed if possible
+ * despite no annotation
+ */
+ @Test def basicTransformNonAnnotated = {
+ val source = """
+ |object Foo {
+ | def foo(i: Int) = i match {
+ | case 2 => println(2)
+ | case 1 => println(1)
+ | }
+ |}""".stripMargin
+
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Foo$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val methodNode = getMethod(moduleNode, "foo")
+ assert(verifySwitch(methodNode))
+ }
+ }
+
+ /** This test verifies that simple matches with `@switch` annotations are
+ * indeed transformed to a switch
+ */
+ @Test def basicTransfromAnnotated = {
+ val source = """
+ |object Foo {
+ | import scala.annotation.switch
+ | def foo(i: Int) = (i: @switch) match {
+ | case 2 => println(2)
+ | case 1 => println(1)
+ | }
+ |}""".stripMargin
+
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Foo$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val methodNode = getMethod(moduleNode, "foo")
+ assert(verifySwitch(methodNode))
+ }
+ }
+
+ @Test def failTransform = {
+ val source = """
+ |object Foo {
+ | import scala.annotation.switch
+ | def foo(i: Any) = (i: @switch) match {
+ | case x: String => println("string!")
+ | case x :: xs => println("list!")
+ | }
+ |}""".stripMargin
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Foo$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val methodNode = getMethod(moduleNode, "foo")
+
+ assert(verifySwitch(methodNode, shouldFail = true))
+ }
+ }
+
+ /** Make sure that creating multidim arrays reduces to "multinewarray"
+ * instruction
+ */
+ @Test def multidimArraysFromOfDim = {
+ val source = """
+ |object Arr {
+ | def arr = Array.ofDim[Int](2, 1)
+ |}""".stripMargin
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Arr$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val method = getMethod(moduleNode, "arr")
+
+ val hadCorrectInstr =
+ instructionsFromMethod(method)
+ .collect {
+ case x @ NewArray(op, _, dims)
+ if op == Opcode.multianewarray && dims == 2 => x
+ }
+ .length > 0
+
+ assert(hadCorrectInstr,
+ "Did not contain \"multianewarray\" instruction in:\n" +
+ instructionsFromMethod(method).mkString("\n"))
+ }
+ }
+
+ @Test def arraysFromOfDim = {
+ val source = """
+ |object Arr {
+ | def arr1 = Array.ofDim[Int](2)
+ | def arr2 = Array.ofDim[Unit](2)
+ | def arr3 = Array.ofDim[String](2)
+ | def arr4 = Array.ofDim[Map[String, String]](2)
+ |}""".stripMargin
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Arr$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val arr1 = getMethod(moduleNode, "arr1")
+ val arr2 = getMethod(moduleNode, "arr2")
+ val arr3 = getMethod(moduleNode, "arr3")
+
+ val arr1CorrectInstr =
+ instructionsFromMethod(arr1)
+ .collect {
+ case x @ IntOp(op, oprnd)
+ if op == Opcode.newarray && oprnd == Opcode.int => x
+ }
+ .length > 0
+
+ assert(arr1CorrectInstr,
+ "Did not contain \"multianewarray\" instruction in:\n" +
+ instructionsFromMethod(arr1).mkString("\n"))
+
+ val arr2CorrectInstr =
+ instructionsFromMethod(arr2)
+ .collect {
+ case x @ TypeOp(op, oprnd)
+ if op == Opcode.anewarray && oprnd == Opcode.boxedUnit => x
+ }
+ .length > 0
+
+ assert(arr2CorrectInstr,
+ "arr2 bytecode did not contain correct `anewarray` instruction:\n" +
+ instructionsFromMethod(arr2)mkString("\n"))
+
+ val arr3CorrectInstr =
+ instructionsFromMethod(arr3)
+ .collect {
+ case x @ TypeOp(op, oprnd)
+ if op == Opcode.anewarray && oprnd == Opcode.javaString => x
+ }
+ .length > 0
+
+ assert(arr3CorrectInstr,
+ "arr3 bytecode did not contain correct `anewarray` instruction:\n" +
+ instructionsFromMethod(arr3).mkString("\n"))
+ }
+ }
+
+ @Test def arraysFromDimAndFromNewEqual = {
+ val source = """
+ |object Arr {
+ | def arr1 = Array.ofDim[Int](2)
+ | def arr2 = new Array[Int](2)
+ |}""".stripMargin
+
+ checkBCode(source) { dir =>
+ val moduleIn = dir.lookupName("Arr$.class", directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ val arr1 = getMethod(moduleNode, "arr1")
+ val arr2 = getMethod(moduleNode, "arr2")
+
+ // First two instructions of `arr1` fetch the static reference to `Array`
+ val instructions1 = instructionsFromMethod(arr1).drop(2)
+ val instructions2 = instructionsFromMethod(arr2)
+
+ assert(instructions1 == instructions2,
+ "Creating arrays using `Array.ofDim[Int](2)` did not equal bytecode for `new Array[Int](2)`\n" +
+ diffInstructions(instructions1, instructions2))
+ }
+ }
+}
diff --git a/tests/pos-scala2/i1059.scala b/tests/disabled/pos-scala2/i1059.scala
index cd23e1916..cd23e1916 100644
--- a/tests/pos-scala2/i1059.scala
+++ b/tests/disabled/pos-scala2/i1059.scala
diff --git a/tests/pos/t3480.scala b/tests/disabled/pos/t3480.scala
index ba2e1a4b8..ba2e1a4b8 100644
--- a/tests/pos/t3480.scala
+++ b/tests/disabled/pos/t3480.scala
diff --git a/tests/neg/i1255.scala b/tests/neg/i1255.scala
new file mode 100644
index 000000000..3bb7e8f25
--- /dev/null
+++ b/tests/neg/i1255.scala
@@ -0,0 +1,6 @@
+object Test {
+ def foo(x: Option[Int]) = x match {
+ case Some(_: Double) => true // error
+ case None => true
+ }
+}
diff --git a/tests/pos/t1168.scala b/tests/pos/t1168.scala
index f43436812..08f1b5cd9 100644
--- a/tests/pos/t1168.scala
+++ b/tests/pos/t1168.scala
@@ -1,6 +1,6 @@
object Test extends App {
- trait SpecialException {}
+ trait SpecialException extends Throwable {}
try {
throw new Exception
diff --git a/tests/run/isInstanceOf-eval.check b/tests/run/isInstanceOf-eval.check
new file mode 100644
index 000000000..1191247b6
--- /dev/null
+++ b/tests/run/isInstanceOf-eval.check
@@ -0,0 +1,2 @@
+1
+2
diff --git a/tests/run/isInstanceOf-eval.scala b/tests/run/isInstanceOf-eval.scala
new file mode 100644
index 000000000..aa907b697
--- /dev/null
+++ b/tests/run/isInstanceOf-eval.scala
@@ -0,0 +1,15 @@
+object Test extends dotty.runtime.LegacyApp {
+ lazy val any = {
+ println(1)
+ 1: Any
+ }
+
+ any.isInstanceOf[Int]
+
+ lazy val int = {
+ println(2)
+ 2
+ }
+
+ int.isInstanceOf[Int]
+}
diff --git a/tests/run/t6637.scala b/tests/run/t6637.scala
index 7f9c3cd61..052e7f5d6 100644
--- a/tests/run/t6637.scala
+++ b/tests/run/t6637.scala
@@ -1,7 +1,6 @@
-
object Test extends dotty.runtime.LegacyApp {
try {
- class A ; class B ; List().head.isInstanceOf[A with B]
+ List().head
} catch {
case _ :java.util.NoSuchElementException => println("ok")
}