Skip to content

Commit

Permalink
SI-10190 Elide string to empty instead of null
Browse files Browse the repository at this point in the history
Avoid NPE when eliding string-valued functions.

For example, `log(s"$cheap$expensive")` needn't print null.

This is a natural and inexpensive way to elide strings.
  • Loading branch information
som-snytt committed Feb 15, 2017
1 parent 05d5338 commit 6fb3825
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 16 deletions.
8 changes: 6 additions & 2 deletions src/compiler/scala/tools/nsc/transform/UnCurry.scala
Expand Up @@ -342,12 +342,16 @@ abstract class UnCurry extends InfoTransform
* the whole tree with it.
*/
private def replaceElidableTree(tree: Tree): Tree = {
def elisionOf(t: Type): Tree = t.typeSymbol match {
case StringClass => Literal(Constant("")) setType t
case _ => gen.mkZero(t)
}
tree match {
case DefDef(_,_,_,_,_,rhs) =>
val rhs1 = if (rhs == EmptyTree) rhs else Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe
val rhs1 = if (rhs == EmptyTree) rhs else Block(Nil, elisionOf(rhs.tpe)) setType rhs.tpe
deriveDefDef(tree)(_ => rhs1) setSymbol tree.symbol setType tree.tpe
case _ =>
gen.mkZero(tree.tpe) setType tree.tpe
elisionOf(tree.tpe)
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/files/run/elidable-opt.check
Expand Up @@ -11,4 +11,4 @@ false
0
0.0
0.0
null

2 changes: 1 addition & 1 deletion test/files/run/elidable.check
Expand Up @@ -11,4 +11,4 @@ false
0
0.0
0.0
null

42 changes: 30 additions & 12 deletions test/files/run/elidable.scala
Expand Up @@ -3,31 +3,36 @@ import elidable._

// runs -Xelide-below WARNING or 900

object Fail {
def fail(msg: String): Unit = throw new IllegalStateException(s"Expected failure: $msg")
}
import Fail.fail

trait T {
@elidable(FINEST) def f1()
@elidable(SEVERE) def f2()
@elidable(FINEST) def f3() = assert(false, "Should have been elided.")
@elidable(FINEST) def f3() = fail("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.")
@elidable(FINEST) def f4() = fail("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(FINEST) def f1() = fail("Should have been elided.")
@elidable(INFO) def f2() = fail("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).")
@elidable(INFO) def f4 = fail("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(FINEST) def f1() = fail("Should have been elided.")
@elidable(INFO) def f2() = fail("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(INFO) def f4 = fail("Should have been elided (no parens).")

@elidable(FINEST) def f5() = {}
@elidable(FINEST) def f6() = true
Expand All @@ -38,12 +43,12 @@ object Test {
@elidable(FINEST) def fb() = 1l
@elidable(FINEST) def fc() = 1.0f
@elidable(FINEST) def fd() = 1.0
@elidable(FINEST) def fe() = "s"
@elidable(FINEST) def fe() = { fail("Should have been elided to empty string.") ; "hello, world" }

/* variable elisions? see test/files/neg/t10068.scala
@elidable(INFO) val goner1: Int = { assert(false, "Should have been elided.") ; 42 }
@elidable(INFO) lazy val goner2: Int = { assert(false, "Should have been elided.") ; 42 }
@elidable(INFO) var goner3: Int = { assert(false, "Should have been elided.") ; 42 }
@elidable(INFO) val goner1: Int = { fail("Should have been elided.") ; 42 }
@elidable(INFO) lazy val goner2: Int = { fail("Should have been elided.") ; 42 }
@elidable(INFO) var goner3: Int = { fail("Should have been elided.") ; 42 }
@elidable(INFO) var goner4: Nothing = _
*/

Expand Down Expand Up @@ -74,6 +79,19 @@ object Test {
println(fc())
println(fd())
println(fe())
if (!fe().isEmpty) fail(s"Not empty: [${fe()}]")
/*
()
false
0
0
0
0
0
0.0
0.0
// was: null
*/

// this one won't show up in the output because a call to f1 is elidable when accessed through T
(c:T).f1()
Expand Down

0 comments on commit 6fb3825

Please sign in to comment.