diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/TreeGen.scala | 27 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 190 | ||||
-rw-r--r-- | test/files/neg/elide-to-nothing.check | 4 | ||||
-rw-r--r-- | test/files/neg/elide-to-nothing.flags | 1 | ||||
-rw-r--r-- | test/files/neg/elide-to-nothing.scala | 31 | ||||
-rw-r--r-- | test/files/run/elidable-opt.check | 14 | ||||
-rw-r--r-- | test/files/run/elidable-opt.flags | 1 | ||||
-rw-r--r-- | test/files/run/elidable-opt.scala | 85 | ||||
-rw-r--r-- | test/files/run/elidable.check | 15 | ||||
-rw-r--r-- | test/files/run/elidable.scala | 73 |
10 files changed, 323 insertions, 118 deletions
diff --git a/src/compiler/scala/reflect/internal/TreeGen.scala b/src/compiler/scala/reflect/internal/TreeGen.scala index e537c6b83f..cc882ad5ed 100644 --- a/src/compiler/scala/reflect/internal/TreeGen.scala +++ b/src/compiler/scala/reflect/internal/TreeGen.scala @@ -250,20 +250,19 @@ abstract class TreeGen { * var x: T = _ * which is appropriate to the given Type. */ - def mkZero(tp: Type): Tree = { - val tree = tp.typeSymbol match { - case UnitClass => Literal(Constant()) - case BooleanClass => Literal(Constant(false)) - case FloatClass => Literal(Constant(0.0f)) - case DoubleClass => Literal(Constant(0.0d)) - case ByteClass => Literal(Constant(0.toByte)) - case ShortClass => Literal(Constant(0.toShort)) - case IntClass => Literal(Constant(0)) - case LongClass => Literal(Constant(0L)) - case CharClass => Literal(Constant(0.toChar)) - case _ => Literal(Constant(null)) - } - tree setType tp + def mkZero(tp: Type): Tree = Literal(mkConstantZero(tp)) setType tp + + def mkConstantZero(tp: Type): Constant = tp.typeSymbol match { + case UnitClass => Constant(()) + case BooleanClass => Constant(false) + case FloatClass => Constant(0.0f) + case DoubleClass => Constant(0.0d) + case ByteClass => Constant(0.toByte) + case ShortClass => Constant(0.toShort) + case IntClass => Constant(0) + case LongClass => Constant(0L) + case CharClass => Constant(0.toChar) + case _ => Constant(null) } def mkZeroContravariantAfterTyper(tp: Type): Tree = { diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index b458d305a5..69bf006036 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -452,16 +452,17 @@ abstract class UnCurry extends InfoTransform } } - /** For removing calls to specially designated methods. + /** Called if a tree's symbol is elidable. If it's a DefDef, + * replace only the body/rhs with 0/false/()/null; otherwise replace + * the whole tree with it. */ - def elideIntoUnit(tree: Tree): Tree = Literal(Constant()) setPos tree.pos setType UnitClass.tpe - def isElidable(tree: Tree) = { - val sym = treeInfo.methPart(tree).symbol - // XXX settings.noassertions.value temporarily retained to avoid - // breakage until a reasonable interface is settled upon. - sym != null && sym.elisionLevel.exists(x => x < settings.elidebelow.value || settings.noassertions.value) && { - log("Eliding call from " + tree.symbol.owner + " to " + sym + " based on its elision threshold of " + sym.elisionLevel.get) - true + private def replaceElidableTree(tree: Tree): Tree = { + tree match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + val newRhs = Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe + treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, newRhs) setSymbol tree.symbol setType tree.tpe + case _ => + gen.mkZero(tree.tpe) setType tree.tpe } } @@ -502,109 +503,96 @@ abstract class UnCurry extends InfoTransform finally this.inConstructorFlag = saved } - if (isElidable(tree)) elideIntoUnit(tree) - else tree match { - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => - if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd) - withNeedLift(false) { - if (tree.symbol.isClassConstructor) { - atOwner(tree.symbol) { - val rhs1 = (rhs: @unchecked) match { - case Block(stats, expr) => - def transformInConstructor(stat: Tree) = - withInConstructorFlag(INCONSTRUCTOR) { transform(stat) } - val presupers = treeInfo.preSuperFields(stats) map transformInConstructor - val rest = stats drop presupers.length - val supercalls = rest take 1 map transformInConstructor - val others = rest drop 1 map transform - treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr)) + val sym = tree.symbol + val result = ( + // TODO - settings.noassertions.value temporarily retained to avoid + // breakage until a reasonable interface is settled upon. + if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions.value))) + super.transform(replaceElidableTree(tree)) + else tree match { + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd) + withNeedLift(false) { + if (dd.symbol.isClassConstructor) { + atOwner(sym) { + val rhs1 = (rhs: @unchecked) match { + case Block(stats, expr) => + def transformInConstructor(stat: Tree) = + withInConstructorFlag(INCONSTRUCTOR) { transform(stat) } + val presupers = treeInfo.preSuperFields(stats) map transformInConstructor + val rest = stats drop presupers.length + val supercalls = rest take 1 map transformInConstructor + val others = rest drop 1 map transform + treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr)) + } + treeCopy.DefDef( + tree, mods, name, transformTypeDefs(tparams), + transformValDefss(vparamss), transform(tpt), rhs1) } - treeCopy.DefDef( - tree, mods, name, transformTypeDefs(tparams), - transformValDefss(vparamss), transform(tpt), rhs1) + } else { + super.transform(tree) } - } else { - super.transform(tree) - } - } - case ValDef(_, _, _, rhs) => - val sym = tree.symbol - if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) - // a local variable that is mutable and free somewhere later should be lifted - // as lambda lifting (coming later) will wrap 'rhs' in an Ref object. - if (!sym.owner.isSourceMethod) - withNeedLift(true) { super.transform(tree) } - else - super.transform(tree) -/* - case Apply(Select(Block(List(), Function(vparams, body)), nme.apply), args) => - // perform beta-reduction; this helps keep view applications small - println("beta-reduce1: "+tree) - withNeedLift(true) { - mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body)) - } - - case Apply(Select(Function(vparams, body), nme.apply), args) => -// if (List.forall2(vparams, args)((vparam, arg) => treeInfo.isAffineIn(body) || -// treeInfo.isExprSafeToInline(arg))) => - // perform beta-reduction; this helps keep view applications small - println("beta-reduce2: "+tree) - withNeedLift(true) { - mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body)) - } -*/ - case UnApply(fn, args) => - val fn1 = withInPattern(false)(transform(fn)) - val args1 = transformTrees(fn.symbol.name match { - case nme.unapply => args - case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe)) - case _ => sys.error("internal error: UnApply node has wrong symbol") - }) - treeCopy.UnApply(tree, fn1, args1) - - case Apply(fn, args) => - if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) - transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) - else - withNeedLift(true) { - val formals = fn.tpe.paramTypes - treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) } + case ValDef(_, _, _, rhs) => + if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) + // a local variable that is mutable and free somewhere later should be lifted + // as lambda lifting (coming later) will wrap 'rhs' in an Ref object. + if (!sym.owner.isSourceMethod) + withNeedLift(true) { super.transform(tree) } + else + super.transform(tree) + case UnApply(fn, args) => + val fn1 = withInPattern(false)(transform(fn)) + val args1 = transformTrees(fn.symbol.name match { + case nme.unapply => args + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe)) + case _ => sys.error("internal error: UnApply node has wrong symbol") + }) + treeCopy.UnApply(tree, fn1, args1) + + case Apply(fn, args) => + if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) + transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) + else + withNeedLift(true) { + val formals = fn.tpe.paramTypes + treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) + } - case Assign(Select(_, _), _) => - withNeedLift(true) { super.transform(tree) } + case Assign(Select(_, _), _) => + withNeedLift(true) { super.transform(tree) } - case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => - withNeedLift(true) { super.transform(tree) } + case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => + withNeedLift(true) { super.transform(tree) } - case Try(block, catches, finalizer) => - if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) - else super.transform(tree) + case Try(block, catches, finalizer) => + if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) + else super.transform(tree) - case CaseDef(pat, guard, body) => - val pat1 = withInPattern(true)(transform(pat)) - treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) + case CaseDef(pat, guard, body) => + val pat1 = withInPattern(true)(transform(pat)) + treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) - case fun @ Function(_, _) => - mainTransform(transformFunction(fun)) + case fun @ Function(_, _) => + mainTransform(transformFunction(fun)) - case Template(_, _, _) => - withInConstructorFlag(0) { super.transform(tree) } + case Template(_, _, _) => + withInConstructorFlag(0) { super.transform(tree) } - case _ => - val tree1 = super.transform(tree) - if (isByNameRef(tree1)) { - val tree2 = tree1 setType functionType(Nil, tree1.tpe) - return { - if (noApply contains tree2) tree2 - else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) + case _ => + val tree1 = super.transform(tree) + if (isByNameRef(tree1)) { + val tree2 = tree1 setType functionType(Nil, tree1.tpe) + return { + if (noApply contains tree2) tree2 + else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) + } } - } - tree1 - } - } setType { - assert(tree.tpe != null, tree + " tpe is null") - uncurryTreeType(tree.tpe) + tree1 + } + ) + assert(result.tpe != null, result + " tpe is null") + result setType uncurryTreeType(result.tpe) } def postTransform(tree: Tree): Tree = atPhase(phase.next) { diff --git a/test/files/neg/elide-to-nothing.check b/test/files/neg/elide-to-nothing.check new file mode 100644 index 0000000000..3ef05aac9a --- /dev/null +++ b/test/files/neg/elide-to-nothing.check @@ -0,0 +1,4 @@ +elide-to-nothing.scala:14: error: Cannot elide where Nothing is required. + val b: Nothing = unimplemented() + ^ +one error found diff --git a/test/files/neg/elide-to-nothing.flags b/test/files/neg/elide-to-nothing.flags new file mode 100644 index 0000000000..59a512e547 --- /dev/null +++ b/test/files/neg/elide-to-nothing.flags @@ -0,0 +1 @@ +-Xelide-below 500 diff --git a/test/files/neg/elide-to-nothing.scala b/test/files/neg/elide-to-nothing.scala new file mode 100644 index 0000000000..5008e8bc1d --- /dev/null +++ b/test/files/neg/elide-to-nothing.scala @@ -0,0 +1,31 @@ + +/** Test which should fail compilation */ +class ElysianFailed { + + import ElysianField._ + + // fine + val a: Int = myInt + + // fine + unimplemented() + + // not fine + val b: Nothing = unimplemented() + +} + +object ElysianField { + + import annotation.elidable + + @elidable(100) def unimplemented(): Nothing = throw new UnsupportedOperationException + + @elidable(100) def myInt: Int = 17 + +} + + + + + diff --git a/test/files/run/elidable-opt.check b/test/files/run/elidable-opt.check new file mode 100644 index 0000000000..88cf98e0d1 --- /dev/null +++ b/test/files/run/elidable-opt.check @@ -0,0 +1,14 @@ +Good for me, I was not elided. Test.f3 +Good for me, I was not elided. O.f3 +Good for me, I was not elided. C.f1 +Good for me, I was not elided. C.f2 +() +false +0 +0 +0 +0 +0 +0.0 +0.0 +null diff --git a/test/files/run/elidable-opt.flags b/test/files/run/elidable-opt.flags new file mode 100644 index 0000000000..62897ff218 --- /dev/null +++ b/test/files/run/elidable-opt.flags @@ -0,0 +1 @@ +-optimise -Xelide-below 900 diff --git a/test/files/run/elidable-opt.scala b/test/files/run/elidable-opt.scala new file mode 100644 index 0000000000..a2f29d2caf --- /dev/null +++ b/test/files/run/elidable-opt.scala @@ -0,0 +1,85 @@ +import annotation._ +import elidable._ + +trait T { + @elidable(FINEST) def f1() + @elidable(SEVERE) def f2() + @elidable(FINEST) def f3() = assert(false, "Should have been elided.") + def f4() +} + +class C extends T { + def f1() = println("Good for me, I was not elided. C.f1") + def f2() = println("Good for me, I was not elided. C.f2") + @elidable(FINEST) def f4() = assert(false, "Should have been elided.") +} + +object O { + @elidable(FINEST) def f1() = assert(false, "Should have been elided.") + @elidable(INFO) def f2() = assert(false, "Should have been elided.") + @elidable(SEVERE) def f3() = println("Good for me, I was not elided. O.f3") + @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") +} + +object Test { + @elidable(FINEST) def f1() = assert(false, "Should have been elided.") + @elidable(INFO) def f2() = assert(false, "Should have been elided.") + @elidable(SEVERE) def f3() = println("Good for me, I was not elided. Test.f3") + @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") + + @elidable(FINEST) def f5() = {} + @elidable(FINEST) def f6() = true + @elidable(FINEST) def f7() = 1:Byte + @elidable(FINEST) def f8() = 1:Short + @elidable(FINEST) def f9() = 1:Char + @elidable(FINEST) def fa() = 1 + @elidable(FINEST) def fb() = 1l + @elidable(FINEST) def fc() = 1.0f + @elidable(FINEST) def fd() = 1.0 + @elidable(FINEST) def fe() = "s" + + def main(args: Array[String]): Unit = { + f1() + f2() + f3() + f4 + O.f1() + O.f2() + O.f3() + O.f4 + + val c = new C + c.f1() + c.f2() + c.f3() + c.f4() + + // make sure a return value is still available when eliding a call + println(f5()) + println(f6()) + println(f7()) + println(f8()) + println(f9().toInt) + println(fa()) + println(fb()) + println(fc()) + println(fd()) + println(fe()) + + // this one won't show up in the output because a call to f1 is elidable when accessed through T + (c:T).f1() + + // Test whether the method definitions are still available. + List("Test", "Test$", "O", "O$", "C", "T") foreach { className => + List("f1", "f2", "f3", "f4") foreach { methodName => + Class.forName(className).getMethod(methodName) + } + } + List("Test", "Test$") foreach { className => + List("f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe") foreach { methodName => + Class.forName(className).getMethod(methodName) + } + } + Class.forName("T$class").getMethod("f3", classOf[T]) + } +} diff --git a/test/files/run/elidable.check b/test/files/run/elidable.check index 4ce04f0040..88cf98e0d1 100644 --- a/test/files/run/elidable.check +++ b/test/files/run/elidable.check @@ -1 +1,14 @@ -Good for me, I was not elided. +Good for me, I was not elided. Test.f3 +Good for me, I was not elided. O.f3 +Good for me, I was not elided. C.f1 +Good for me, I was not elided. C.f2 +() +false +0 +0 +0 +0 +0 +0.0 +0.0 +null diff --git a/test/files/run/elidable.scala b/test/files/run/elidable.scala index 264efbad59..a2f29d2caf 100644 --- a/test/files/run/elidable.scala +++ b/test/files/run/elidable.scala @@ -1,16 +1,85 @@ import annotation._ import elidable._ +trait T { + @elidable(FINEST) def f1() + @elidable(SEVERE) def f2() + @elidable(FINEST) def f3() = assert(false, "Should have been elided.") + def f4() +} + +class C extends T { + def f1() = println("Good for me, I was not elided. C.f1") + def f2() = println("Good for me, I was not elided. C.f2") + @elidable(FINEST) def f4() = assert(false, "Should have been elided.") +} + +object O { + @elidable(FINEST) def f1() = assert(false, "Should have been elided.") + @elidable(INFO) def f2() = assert(false, "Should have been elided.") + @elidable(SEVERE) def f3() = println("Good for me, I was not elided. O.f3") + @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") +} + object Test { @elidable(FINEST) def f1() = assert(false, "Should have been elided.") @elidable(INFO) def f2() = assert(false, "Should have been elided.") - @elidable(SEVERE) def f3() = println("Good for me, I was not elided.") + @elidable(SEVERE) def f3() = println("Good for me, I was not elided. Test.f3") @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") - + + @elidable(FINEST) def f5() = {} + @elidable(FINEST) def f6() = true + @elidable(FINEST) def f7() = 1:Byte + @elidable(FINEST) def f8() = 1:Short + @elidable(FINEST) def f9() = 1:Char + @elidable(FINEST) def fa() = 1 + @elidable(FINEST) def fb() = 1l + @elidable(FINEST) def fc() = 1.0f + @elidable(FINEST) def fd() = 1.0 + @elidable(FINEST) def fe() = "s" + def main(args: Array[String]): Unit = { f1() f2() f3() f4 + O.f1() + O.f2() + O.f3() + O.f4 + + val c = new C + c.f1() + c.f2() + c.f3() + c.f4() + + // make sure a return value is still available when eliding a call + println(f5()) + println(f6()) + println(f7()) + println(f8()) + println(f9().toInt) + println(fa()) + println(fb()) + println(fc()) + println(fd()) + println(fe()) + + // this one won't show up in the output because a call to f1 is elidable when accessed through T + (c:T).f1() + + // Test whether the method definitions are still available. + List("Test", "Test$", "O", "O$", "C", "T") foreach { className => + List("f1", "f2", "f3", "f4") foreach { methodName => + Class.forName(className).getMethod(methodName) + } + } + List("Test", "Test$") foreach { className => + List("f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe") foreach { methodName => + Class.forName(className).getMethod(methodName) + } + } + Class.forName("T$class").getMethod("f3", classOf[T]) } } |