Lazy statics (was: Feedback / query on jextract for Windows 10)
brian.goetz at oracle.com
Thu Feb 4 14:45:59 UTC 2021
> The compilation strategy requires no new VM features.
> You need a source modifier of some sort on the once-only/cached/lazy
> method M, and then M’s body gets desugared into a separate private
> static method P, with M containing “ldc condy P; areturn;”
While I don't want to weigh down the requirements, I'd like to make sure
we're paying at least some attention to the instance side of the story.
I fully acknowledge that the upside for statics is higher, since it will
benefit much more from constant-propagation optimizations. And maybe
we'll eventually decide that the instance side is not worth it, but I'd
like to at least understand what additional constraints it adds.
From a user-model perspective, a cached instance method has more ways
to mess up, as it could accidentally compute from mutable state, and end
up freezing an arbitrary value. Perhaps warnings when accessing
non-final fields would help here.
The biggest difference that I see is an artifact of the obvious
translation strategies; condy gives us a "free bit" to represent "not
initialized yet", in the form of the resolution status of the CP. So a
condy could evaluate to the default for it's return type (null, zero,
etc), and not confuse the lazy initialization code into thinking that it
is uninitialized. (Such issues have plagued lazy initialization from
time immemorial; witness the addition of `hashIsZero` to String.) There
are a few ways out here:
- Strictly outlaw the default value, treating returning this as an error.
- Outlaw only null -- with Valhalla, this becomes more attractive, as
we can erase the cache to Object
- Try to support null/default values with an extra bit
- Use an extra bit, but on an opt-in basis (requires more syntax)
- Use an out-of-band sentinel (erase cache field to Object; use an
Object of a known different type as "initialized but returned default.")
Once again, we're in diminishing-returns territory; increasing effort
spent on an irritating corner case. Again, not wanting to deep dive on
translation strategies, as much as identify the user-model tradeoffs:
either the user-visible semantics for the instance version will be
different, or the performance benefits will be less, or the surface
complexity will be greater.
I'll also put a third way to expose this on the table: a field whose
initializer is not an expression, but some kind of lambda:
lazy static Foo f -> expr;
(syntax purely suggestive, but it matches up with one form of CMB
suggested separately, which is `int m() -> expr`.)
More information about the panama-dev