Skip to content

Commit 6e3065f

Browse files
committed
Fix #1878: Use Inline to on final vals.
1 parent 4d7b247 commit 6e3065f

File tree

4 files changed

+51
-20
lines changed

4 files changed

+51
-20
lines changed

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

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,26 +99,11 @@ import Decorators._
9999

100100
if (sym.is(Accessor, butNot = NoFieldNeeded))
101101
if (sym.isGetter) {
102-
def skipBlocks(t: Tree): Tree = t match {
103-
case Block(_, t1) => skipBlocks(t1)
104-
case _ => t
105-
}
106-
skipBlocks(tree.rhs) match {
107-
case lit: Literal if sym.is(Final) && isIdempotentExpr(tree.rhs) =>
108-
// duplicating scalac behavior: for final vals that have rhs as constant, we do not create a field
109-
// and instead return the value. This seemingly minor optimization has huge effect on initialization
110-
// order and the values that can be observed during superconstructor call
111-
112-
// see remark about idempotency in PostTyper#normalizeTree
113-
cpy.DefDef(tree)(rhs = lit)
114-
case _ =>
115-
var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform)
116-
if (isWildcardArg(rhs)) rhs = EmptyTree
117-
118-
val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs)))
119-
val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))(ctx.withOwner(sym), info))
120-
Thicket(fieldDef, getterDef)
121-
}
102+
var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform)
103+
if (isWildcardArg(rhs)) rhs = EmptyTree
104+
val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs)))
105+
val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))(ctx.withOwner(sym), info))
106+
Thicket(fieldDef, getterDef)
122107
} else if (sym.isSetter) {
123108
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion
124109
field.setFlag(Mutable) // necessary for vals mixed in from Scala2 traits

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11731173
if (sym.is(Inline, butNot = DeferredOrParamAccessor))
11741174
checkInlineConformant(rhs1, em"right-hand side of inline $sym")
11751175
patchIfLazy(vdef1)
1176+
patchFinalVals(vdef1)
11761177
vdef1
11771178
}
11781179

@@ -1185,6 +1186,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11851186
patch(Position(toUntyped(vdef).pos.start), "@volatile ")
11861187
}
11871188

1189+
/** Adds inline to final vals with idempotent rhs
1190+
*
1191+
* duplicating scalac behavior: for final vals that have rhs as constant, we do not create a field
1192+
* and instead return the value. This seemingly minor optimization has huge effect on initialization
1193+
* order and the values that can be observed during superconstructor call
1194+
*
1195+
* see remark about idempotency in PostTyper#normalizeTree
1196+
*/
1197+
private def patchFinalVals(vdef: ValDef)(implicit ctx: Context): Unit = {
1198+
def isFinalInlinableVal(sym: Symbol): Boolean = {
1199+
sym.is(Final, butNot = Mutable) &&
1200+
isIdempotentExpr(vdef.rhs) /* &&
1201+
ctx.scala2Mode (stay compatible with Scala2 for now) */
1202+
}
1203+
val sym = vdef.symbol
1204+
sym.info match {
1205+
case info: ConstantType if isFinalInlinableVal(sym) && !ctx.settings.YnoInline.value => sym.setFlag(Inline)
1206+
case _ =>
1207+
}
1208+
}
1209+
11881210
def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") {
11891211
val DefDef(name, tparams, vparamss, tpt, _) = ddef
11901212
completeAnnotations(ddef, sym)

tests/run/final-var.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
false
2+
true
3+
false
4+
true

tests/run/final-var.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
println(Obj.myFinalVar)
4+
Obj.myFinalVar = true
5+
println(Obj.myFinalVar)
6+
7+
val o = new Cls
8+
println(o.myFinalVar)
9+
o.myFinalVar = true
10+
println(o.myFinalVar)
11+
}
12+
}
13+
14+
object Obj {
15+
final var myFinalVar: Boolean = false
16+
}
17+
18+
class Cls {
19+
final var myFinalVar: Boolean = false
20+
}

0 commit comments

Comments
 (0)