Skip to content

Commit 02e3883

Browse files
authored
Merge pull request #2267 from dotty-staging/fix-constant-lazy-vals
Fix #2266: Do not replace constant type lazy vals with constant.
2 parents 4623e12 + 983ce8d commit 02e3883

14 files changed

+123
-7
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotc
33
package ast
44

55
import core._
6-
import Flags._, Trees._, Types._, Contexts._
6+
import Flags._, Trees._, Types._, Contexts._, Constants._
77
import Names._, StdNames._, NameOps._, Decorators._, Symbols._
88
import util.HashSet
99
import typer.ConstFold
@@ -426,8 +426,18 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
426426
*/
427427
def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
428428
val tree1 = ConstFold(tree)
429+
def canInlineConstant(value: Constant): Boolean = {
430+
val sym = tree1.symbol
431+
isIdempotentExpr(tree1) && // see note in documentation
432+
// lazy value must be initialized (would not be needed with isPureExpr)
433+
!sym.is(Lazy) &&
434+
// could hide initialization order issues (ex. val with constant type read before initialized)
435+
(!ctx.owner.isLocalDummy || (!sym.is(Method) && !sym.is(Lazy) && value.isZero) ||
436+
ctx.scala2Mode // ignore in Scala 2 because of inlined `final val` values
437+
)
438+
}
429439
tree1.tpe.widenTermRefExpr match {
430-
case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value)
440+
case ConstantType(value) if canInlineConstant(value) => Literal(value)
431441
case _ => tree1
432442
}
433443
}

compiler/src/dotty/tools/dotc/core/Constants.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ object Constants {
5353
def isNonUnitAnyVal = BooleanTag <= tag && tag <= DoubleTag
5454
def isAnyVal = UnitTag <= tag && tag <= DoubleTag
5555

56+
/** Is the zero or un-initialized value of the type */
57+
def isZero(implicit ctx: Context): Boolean = tag match {
58+
case BooleanTag => !value.asInstanceOf[Boolean]
59+
case ByteTag => value.asInstanceOf[Byte] == 0
60+
case ShortTag => value.asInstanceOf[Short] == 0
61+
case CharTag => value.asInstanceOf[Char] == 0
62+
case IntTag => value.asInstanceOf[Int] == 0
63+
case LongTag => value.asInstanceOf[Long] == 0L
64+
case FloatTag => value.asInstanceOf[Float] == 0.0
65+
case DoubleTag => value.asInstanceOf[Double] == 0.0
66+
case NullTag => true
67+
case _ => false
68+
}
69+
5670
def tpe(implicit ctx: Context): Type = tag match {
5771
case UnitTag => defn.UnitType
5872
case BooleanTag => defn.BooleanType

compiler/src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import dotty.tools.dotc.transform.TreeTransforms._
99
import ast.Trees._
1010
import Flags._
1111
import Types._
12-
import Constants.Constant
12+
import Constants._
1313
import Contexts.Context
1414
import Symbols._
1515
import SymDenotations._
@@ -34,6 +34,8 @@ import StdNames._
3434
* - drops branches of ifs using the rules
3535
* if (true) A else B --> A
3636
* if (false) A else B --> B
37+
* if (C: true) A else B --> C; A
38+
* if (C: false) A else B --> C; B
3739
*/
3840
class FirstTransform extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer =>
3941
import ast.tpd._
@@ -190,11 +192,16 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota
190192
override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) =
191193
constToLiteral(tree)
192194

193-
override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
194-
tree.cond match {
195-
case Literal(Constant(c: Boolean)) => if (c) tree.thenp else tree.elsep
196-
case _ => tree
195+
override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) = {
196+
tree.cond.tpe.widenTermRefExpr match {
197+
case ConstantType(Constant(condVal: Boolean)) =>
198+
val selected = if (condVal) tree.thenp else tree.elsep
199+
if (isPureExpr(tree.cond)) selected
200+
else Block(tree.cond :: Nil, selected)
201+
case _ =>
202+
tree
197203
}
204+
}
198205

199206
// invariants: all modules have companion objects
200207
// all types are TypeTrees

compiler/test/dotc/scala-collections.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,5 @@ scala/util/control/Exception.scala
8181
# 51 | implicit def throwableSubtypeToCatcher[Ex <: Throwable: ClassTag, T](pf: PartialFunction[Ex, T]) =
8282
# | ^
8383
# | cyclic reference involving method mkCatcher
84+
85+
scala/concurrent/duration/Duration.scala

tests/run/if-with-constant-cond.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cond1
2+
then1
3+
cond2
4+
else2

tests/run/if-with-constant-cond.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
object Test {
3+
4+
def main(args: Array[String]): Unit = {
5+
if ({ println("cond1"); true }: true) println("then1") else println("else1")
6+
if ({ println("cond2"); false }: false) println("then2") else println("else2")
7+
}
8+
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert
2+
s
3+
r init
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
3+
abstract class A {
4+
def s: Boolean = { println("s"); r }
5+
def r: Boolean
6+
}
7+
8+
object Test extends A {
9+
assert({ println("assert"); r == s }) // r constant type not replaced by true, r not initialized yet
10+
override val r: true = {
11+
println("r init")
12+
true
13+
}
14+
def main(args: Array[String]): Unit = {}
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert
2+
s
3+
r init
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
abstract class A {
3+
def s: Boolean = { println("s"); r }
4+
def r: Boolean
5+
}
6+
7+
object Test extends A {
8+
assert({ println("assert"); r == s }) // r constant type replaced by false
9+
override val r: false = {
10+
println("r init")
11+
false
12+
}
13+
def main(args: Array[String]): Unit = {}
14+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
assert
2+
r2
3+
s
4+
r init
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
3+
abstract class A {
4+
def s: Boolean = { println("s"); r }
5+
def r: Boolean
6+
}
7+
8+
object Test extends A {
9+
assert({ println("assert"); r2 != s }) // s not initialized yet
10+
def r2: true = {
11+
println("r2")
12+
true
13+
}
14+
override val r: true = {
15+
println("r init")
16+
true
17+
}
18+
def main(args: Array[String]): Unit = {}
19+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
X
2+
Y
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
object Test {
3+
lazy val x: true = { println("X"); true }
4+
5+
def main(args: Array[String]): Unit = {
6+
lazy val y: true = { println("Y"); true }
7+
x
8+
y
9+
}
10+
}

0 commit comments

Comments
 (0)