Lazy statics (was: Feedback / query on jextract for Windows 10)

Brian Goetz brian.goetz at
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 mailing list