Dotty ver: 0.27.0-RC1
// compile with -source 3.1 -Ycheck-init
class X:
val some = Some(this)
[warn] -- Warning:
[warn] 4 | val some = Some(this)
[warn] | ^^^^
[warn] |Promote the value under initialization to fully-initialized. Calling trace:
[warn] | -> val some = Some(this)
No warning
Thanks for the report @japgolly .
Escape of this in the constructor (in this case Some.apply) is rare in practice. I am curious about the actual use case. Could you please elaborate? Thanks.
It's a micro-optimisation. Consider this example:
final case class Id(value: Long) {
val some: Some[this.type] =
Some(this)
}
It might look odd but sometimes I do this for real performance benefits when there's a very-hot loop (eg. graph traversal / calculation) and your application's logic is heavily affected by the presence or lack of a related node's ID. By storing Some(this) on all instances you can avoid allocations during that hot-spot logic. You might find yourself needing an average of, say, 10 instances of Option[Id] per node so avoiding those allocations adds up.
Oh and btw, if it makes a difference, the reason I used this.type instead of just Id is just habit in this case, but very important when there are existential types involved, and/or you're building up value-level proofs.
@japgolly Thanks for the detailed explanation. For now, you can use Some(this: @unchecked) to suppress the warning. In the future, we might enhance the checker with type checking to avoid the usage of @unchecked in such cases.
Most helpful comment
It's a micro-optimisation. Consider this example:
It might look odd but sometimes I do this for real performance benefits when there's a very-hot loop (eg. graph traversal / calculation) and your application's logic is heavily affected by the presence or lack of a related node's ID. By storing
Some(this)on all instances you can avoid allocations during that hot-spot logic. You might find yourself needing an average of, say, 10 instances ofOption[Id]per node so avoiding those allocations adds up.