Loosening requirements for super() invocation
Brian Goetz
brian.goetz at oracle.com
Wed Jan 25 18:49:25 UTC 2023
Thinking more on this -- I think there is significant simplicity
dividend here if we relax our goals a little bit. Supporting constructs
such as
if (bar)
this(f(bar));
else
this(g(bar), 0);
would require a full DA/DU analysis, but if the goal is really "allow
statements before the this/super", we can get by with a considerably
simpler feature.
On the latter path, we define a grammar production for "constructor
body" that amounts to:
statement*
[super | this]?
statement*
We further constrain the statement grammar / description to exclude
`this(...)` or `super(...)` calls, since the only place they can appear
is in a constructor, and now there's a place for them to explicitly live.
We can use similar language to say "if there isn't a super/this, you get
one for free at the top" as we do today.
We can use similar "introduces a static context" approach for the
statements prior to the super/this, and the super/this, as we do today.
This is enough to prevent use of `this`, explicitly or implicitly, in
the statements preceding the explicit super/this, including in lambdas,
try blocks, etc.
I don't think there's any need to update DA/DU.
This brings the spec footprint down considerably, while still
eliminating complex/risky workarounds around "super first".
On 1/24/2023 6:16 PM, Brian Goetz wrote:
> Hit "send" a little too soon.
>
> The assertion that governs "no use of `this` in a super/this call" is
> in 8.8.7.1:
>
>> An explicit constructor invocation statement introduces a static
>> context (§8.1.3),
>> which limits the use of constructs that refer to the current object.
>> Notably, the
>> keywords this and super are prohibited in a static context (§15.8.3,
>> §15.11.2),
>> as are unqualified references to instance variables, instance
>> methods, and type
>> parameters of lexically enclosing declarations (§6.5.5.1, §6.5.6.1,
>> §15.12.3).
>
> This needs to be generalized to speak about the use of `this`
> throughout the constructor body; basically, that you can never use
> `this` (explicitly or implicitly) prior to to the super/this call.
>
> There are a few ways you could do this:
>
> - A full-blown treatment of DA/DU for `this`;
> - An extension of the "introduces a static context" used in this
> section, that covers the entire initial segment of the constructor
> body up through and including the this/super-call.
>
> Similarly, you'll need a way to restate the "if it doesn't start with
> `this/super`, you get an implicit super" to the more flexible notion
> being outlined here. This is tricky because the obvious way to do
> this is to run DA/DU on the explicit body of the constructor and ask
> if `this` is DA at all points where the constructor could complete
> normally, and if not, prepend a super() call and rerun DA/DU. This
> moves us from a strictly syntactic rule to one that appeals to a
> complex analysis.
>
> We can simplify this analysis a lot by lowering the target, from one
> that is based on DA to funneling through a single super/this choke
> point, where a ctor body with an explicit super/this looks like:
>
> statement*
> super/this call
> statement*
>
> This would rule out constructs like:
>
> Foo() {
> if (bar)
> this(f(bar));
> else
> this(g(bar), 0);
> }
>
> but is a lot simpler to specify and still offers the "stuff before
> super" benefit.
>
>
>
>
>
> On 1/24/2023 6:01 PM, Brian Goetz wrote:
>> The basic design seems sound enough. Probably time to dig to the
>> next level, which is assessing the spec impact.
>>
>> The places to start are Ch16 (definite assignment) and 8.8.7
>> (constructor bodies).
>>
>> On 1/24/2023 4:24 PM, Archie Cobbs wrote:
>>> On Tue, Jan 24, 2023 at 2:21 PM Brian Goetz <brian.goetz at oracle.com>
>>> wrote:
>>>
>>>
>>>> This made me realize there is another missing requirement:
>>>>
>>>> * Upon normal return from a constructor, the `this` reference
>>>> must be DA
>>>>
>>>
>>> So, I think this is true by definition; on normal return from a
>>> super/this/implicit super call, then `this` *is* DA.
>>>
>>> You can simplify the rule about try blocks to:
>>>
>>> - if `this` is DU on entry to a try block, it must be DU on
>>> normal completion of the try block.
>>>
>>> Can you simplify further, while still preventing `this` calls
>>> inside a try block? I don't think so, since we might want to
>>> allow a try block before the super/this call.
>>>
>>>
>>> Ok thanks. I admit I'm getting a little out of my league here.
>>>
>>> Does this sound correct?
>>>
>>> The JLS will be modified as follows:
>>>
>>> * Remove the requirement that `super()` or `this()` appear as the
>>> first statement in a constructor
>>> * Add superclass initialization to the existing constructor
>>> dataflow analysis as follows:
>>> * The `this` reference is considered DU on entry to the constructor
>>> * Before a `this()` or `super()` call, the `this` reference
>>> must be DU
>>> * After a `this()` or `super()` call, the `this` reference is
>>> considered DA
>>> * Before normal completion of the constructor, the `this`
>>> reference must be DA
>>> * If `this` is DU on entry to a `try` block, it must be DU on
>>> normal completion of the `try` block
>>> * If no explicit `this()` or `super()` appears in a
>>> constructor, it is treated as if the first line of the constructor
>>> were `super()`
>>> * Add the following restrictions on constructors:
>>> * No access to `this`, other than assignments to fields, may
>>> occur unless `this` is DA
>>> * `super()` and `this()` may not appear within any `try { }` block
>>> * Clarify that non-static field initializers and initialization
>>> blocks are executed immediately after `super()` invocation, wherever
>>> it occurs (see "Initialization Order" below)
>>>
>>>
>>> Thanks,
>>> -Archie
>>>
>>> --
>>> Archie L. Cobbs
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230125/0eea4af0/attachment.htm>
More information about the amber-dev
mailing list