Loosening requirements for super() invocation

Archie Cobbs archie.cobbs at gmail.com
Tue Nov 1 21:13:06 UTC 2022


On Tue, Nov 1, 2022 at 11:45 AM Brian Goetz <brian.goetz at oracle.com> wrote:

> So, what you're saying is that we can't immediately promote these to
> errors, but that doesn't mean we can't have warnings.  (We are fairly
> disciplined about zero-warnings in the JDK, using @SuppressWarnings to
> augment type checking with human judgment.)
>
> Warnings are an important part of the safety story.  For example, generics
> are subject to heap pollution because of erasure, but we make a strong
> guarantee anyway: if the entire codebase compiles without raw or unchecked
> warnings, then the synthetic casts introduced by the compiler at the
> boundary of generic and non-generic code are guaranteed to succeed.
> Similarly, while it is a high bar, if we can reliably warn about
> this-escape, we can make a similar guarantee for the final-field
> initialization safety guarantees, which are voided by this-escape.
>

Yes, I really like this idea... so you could compile with something like javac
-Xlint:thisEscape and get all the 'this' escapes reported as warnings.
Developers could control it using the usual methods, i.e., command line
flags and/or @SuppressWarnings annotations.

Of course the devil is in the details...

First of all, when should this rule apply?

At any point in a constructor (one that successfully compiles), the new
object is always definitively in one of three states:

State 0: neither super() nor this() has been invoked; neither the
superclass nor this class are initialized
State 1: super() has been invoked, so the superclass is initialized, but at
least one final field in this class has not been initialized
State 2: super() has been invoked and all final fields in this class have
been initialized

BTW, the current state is easily tracked within the existing DA/DU analysis
of the prototype patch.

The possible state transitions are:

- super() takes you from:
  - State 0 → State 1 if there remain any uninitialized final fields
  - State 0 → State 2 if there are no remaining uninitialized final fields
- this() takes you from State 0 → State 2
- Initializing the last final field takes you from:
  - State 0 → State 0
  - State 1 → State 2

Presumably the 'this' escape analysis should only apply in State 1. This is
when the object is vulnerable because it's half initialized.

One might also argue that "uninitialized final fields" should be replaced
by "fields that are yet to be assigned later in the constructor" or
somesuch. After all, just because a field isn't final, that doesn't mean
it's not requiring initialization. Or maybe there should be some other way
to denote such fields, e.g., a new @RequiresInit annotation.

Assuming you're in State 1, what exactly would constitute a detectable
'this' escape?

Could be something like this:

- Invoking any non-static method on an object known to be equal to 'this'.
- Passing an object known to be equal to 'this' as a parameter to any
method or constructor (including implicit outer instance and free variable
proxy constructor parameters).
- Throwing 'this'

In other words, the 'this' instance literally cannot escape the
constructor's stack frame.

For the meaning of "known to be equal to 'this'", we would be limited in
our ability to infer it in the usual ways (Turing complete, blah blah), so
you'd still be able to evade the warning e.g.:

    public class MyClass implements Iterator<Object> {

        private final int limit;
        private int count;

        public MyClass(int limit) throws IOException {
            ((Iterator<?>)(Object)this).hasNext();   // always returns
false!
            this.limit = limit;
        }

        @Override
        public boolean hasNext() {
            return this.count < this.limit;
        }
    }

So it doesn't seem like an ironclad guarantee like "no 'this' escape
warnings implies no uninitialized final variables" analogous to the "no
unchecked warnings implies no ClassCastExceptions" is possible. But still,
knowing your constructor generates zero warnings would be really useful.

So what do you think would be reasonable definitions for "detectable 'this'
escape" and "known to be equal to 'this'" ?


>
> So this change does not make the problem of "this" escape any worse than
> it already is. That means the two problems are really separate and can be
> addressed separately and independently.
>
>
> I get where you're coming from; you have a patch that you'd like to see
> integrated, so you're trying to keep the requirements narrow.  But
> realistically, this patch surely needs a JEP, because it affects the
> language spec and the user-visible programming model.  And such a JEP is
> going to be more compelling if it addresses the closely-related issue of
> this-escape.
>

That sounds fine to me as long as we can satisfy ourselves with the
requisite details.

-Archie

-- 
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20221101/7764e671/attachment-0001.htm>


More information about the amber-dev mailing list