Skip to content

Commit

Permalink
Erase fields for Null/Nothing val and var
Browse files Browse the repository at this point in the history
Getters for Null fields return trivially `null`.
Getter for Nothing will `throw null`. This is only be reachable
if the field is accesed in the costructor before the field is
initialized. In this case a `NullPointerException` is thrown as before.
  • Loading branch information
nicolasstucki committed May 8, 2017
1 parent 16756e4 commit 38c486c
Show file tree
Hide file tree
Showing 15 changed files with 193 additions and 8 deletions.
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,22 @@ import Decorators._
var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform)
if (isWildcardArg(rhs)) rhs = EmptyTree
val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs)))
val isUnitField = tree.tpt.tpe.widenDealias =:= defn.BoxedUnitType
val getterRhs =
if (isUnitField) ref(defn.BoxedUnit_UNIT)
val rhsClass = tree.tpt.tpe.widenDealias.classSymbol
val getterRhs = {
if (rhsClass eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT)
else if (rhsClass eq defn.NullClass) Literal(Constant(null))
else if (rhsClass eq defn.NothingClass) Throw(Literal(Constant(null)))
else transformFollowingDeep(ref(field))(ctx.withOwner(sym), info)
}
val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
Thicket(fieldDef, getterDef)
} else if (sym.isSetter) {
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion
field.setFlag(Mutable) // necessary for vals mixed in from Scala2 traits
if (tree.vparamss.head.head.tpt.tpe =:= defn.BoxedUnitType) tree
else {
val rhsCls = tree.vparamss.head.head.tpt.tpe.classSymbol
if ((rhsCls eq defn.BoxedUnitClass) || (rhsCls eq defn.NullClass) || (rhsCls eq defn.NothingClass)) {
tree
} else {
val initializer = Assign(ref(field), adaptToField(ref(tree.vparamss.head.head.symbol)))
cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(ctx.withOwner(sym), info))
}
Expand Down
4 changes: 4 additions & 0 deletions tests/run/nothing-lazy-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0
1
foo
???
28 changes: 28 additions & 0 deletions tests/run/nothing-lazy-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

object Test {
def main(args: Array[String]): Unit = {
try {
println("0")
val f = new Foo
println("1")
println(f.foo)
println("2")
println(f.foo)
} catch {
case e: NotImplementedError => println("???")
}

// TODO: Erase
// Currently not erasing fields for lazy vals
assert(classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
}


}

class Foo {
lazy val foo: Nothing = {
println("foo")
???
}
}
3 changes: 3 additions & 0 deletions tests/run/nothing-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0
foo
???
24 changes: 24 additions & 0 deletions tests/run/nothing-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

object Test {
def main(args: Array[String]): Unit = {
try {
println("0")
val f = new Foo
println("1")
println(f.foo)
} catch {
case e: NotImplementedError => println("???")
}

assert(!classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")
}


}

class Foo {
val foo: Nothing = {
println("foo")
???
}
}
3 changes: 3 additions & 0 deletions tests/run/nothing-var.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0
foo
???
35 changes: 35 additions & 0 deletions tests/run/nothing-var.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

object Test {
def main(args: Array[String]): Unit = {
try {
println("0")
val f = new Foo
println("1")
f.foo
f.foo
f.foo = {
println("foo3")
???
}
println(f.foo)
} catch {
case e: NotImplementedError => println("???")
}

assert(!classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")
}


}

class Foo {
var foo: Nothing = {
println("foo")
???
}

foo = {
println("foo2")
???
}
}
5 changes: 5 additions & 0 deletions tests/run/null-lazy-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1
foo
null
2
null
24 changes: 24 additions & 0 deletions tests/run/null-lazy-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

object Test {

def main(args: Array[String]): Unit = {
val f = new Foo
println(1)
println(f.foo)
println(2)
println(f.foo)

// TODO: Erase
// Currently not erasing fields for lazy vals
assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
}


}

class Foo {
lazy val foo: Null = {
println("foo")
null
}
}
3 changes: 3 additions & 0 deletions tests/run/null-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
foo
null
null
16 changes: 16 additions & 0 deletions tests/run/null-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

object Test {
def main(args: Array[String]): Unit = {
val f = new Foo
println(f.foo)
println(f.foo)
assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")
}
}

class Foo {
val foo: Null = {
println("foo")
null
}
}
5 changes: 5 additions & 0 deletions tests/run/null-var.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
foo
foo2
null
foo3
null
26 changes: 26 additions & 0 deletions tests/run/null-var.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

object Test {
def main(args: Array[String]): Unit = {
val f = new Foo
println(f.foo)
f.foo
f.foo = {
println("foo3")
null
}
println(f.foo)
assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")
}
}

class Foo {
var foo: Null = {
println("foo")
null
}

foo = {
println("foo2")
null
}
}
6 changes: 4 additions & 2 deletions tests/run/phantom-lazy-val-2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ object Test {
f.foo
println(2)
f.foo
// Currently not erasing fields for lazy phantom vals
// assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")

// TODO: Erase
// Currently not erasing fields for lazy vals
assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
}


Expand Down
4 changes: 3 additions & 1 deletion tests/run/unit-lazy-val.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ object Test {
f.foo
println(2)
f.foo

// TODO: Erase
// Currently not erasing fields for lazy vals
// assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased")
assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
}


Expand Down

0 comments on commit 38c486c

Please sign in to comment.