Closed pvlugter closed 8 months ago
This was a hard one to pin down...
Example
trait T { val foo = "foo" }
class C {
def m = {
lazy val t = new T {}
/* expands to
lazy val t = {
class anon extends T {
private var _foo
def foo = _foo
def T$_setter_$foo_= (s: String) = { _foo = s}
}
new anon
}
*/
t
}
}
Fields info transformer:
Fields tree transformer:
afterOwnPhase(clazz.info.decls)
on the anonymous class, this forces the fields
info transform
tailcalls -> ClassInfoType(...)
(tailcalls is after fields)t$lzycompute
accessor and calls typer on it
t$lzy.synchronized { if (t$lzy.initialized) t$lzy.value else { t$lzy.initialize({ <anon> }}
typedApply
of synchronized(...)
-> inferMethodInstance
-> TreeTypeSubstituter
for type parameter of synchronized
TypeMapTreeSubstituter
(parent of TreeTypeSubstituter
) calls if (tree.isDef) tree.symbol modifyInfo typeMap
modifyInfo
on the anonymous class symbol is destructive for the type history:
=> setInfo(f(info))
takes the current info and creates a new TypeHistory with just one element
=> the type history has the entry fields -> ClassInfoType(...)
(the type itself is unchanged)When obtaining the anonymous class info
in a later phase, the fields
info transformer will run again because the current type is only valid until fields. A new foo
field symbol is created, it doesn't have the MUTABLE flag, so it ends up final in bytecode.
Uncovered by https://github.com/akka/akka/issues/32128 when dropping JDK 8 in Akka (https://github.com/akka/akka/pull/32127).
Additional case for https://github.com/scala/scala-dev/issues/408 and https://github.com/scala/bug/issues/12340.
Trait fields are still marked final when implemented by an anonymous class in a lazy val.
Reproduction steps
Problem
Field marked final:
When it's either not a lazy val or not an anonymous class, the field is non-final: