On Thu, 12 Jan 2023 19:01:10 GMT, Maurizio Cimadamore <mcimadam...@openjdk.org> 
wrote:

>> The code you quoted has nothing specifically to do with method invocations. 
>> This code is simply handing the evaluation of the expressions `this` and 
>> `super`. For example, `this` could just be a parameter we're passing to 
>> another method.
>> 
>> When `this` or `super` expressions are evaluated, the thing left on top of 
>> the stack is a direct/indirect reference (i.e., an `ExprRef`) exactly when 
>> the there is a direct/indirect reference of type `ThisRef` in the current 
>> reference set.
>> 
>> In cases where `this` is then "invoked", e.g., `this()`, the `ExprRef` (top 
>> of Java stack) becomes the method receiver, and when the method is "invoked" 
>> it turns back into a `ThisRef` (see earlier question).
>> 
>> Regarding the optimization you mention, in light of the above I'm not sure 
>> it would still work.
>
> My point is about who puts ThisRef in the set to begin with. It seems to me 
> that ThisRef is put there at the start of a method analysis. After which, 
> there's several code points where we say "if there's a direct ThisRef in the 
> set, do this, otherwise, if there's an indirect ThisRef in the set, do that". 
> But the ThisRef (whether indirect or direct) seems set once and for all (when 
> we start the analysis, and then inside visitApply). 
> 
> There is also this bit in `visitReference`:
> 
> 
> case SUPER:
>             if (this.refs.contains(ThisRef.direct()))
>                 receiverRefs.add(ThisRef.direct());
>             if (this.refs.contains(ThisRef.indirect()))
>                 receiverRefs.add(ThisRef.indirect());
>             break;
> 
> 
> But this doesn't change what I'm saying - there seems to be a general 
> property when we execute this analysis which says whether the current 
> execution context has a direct `this` or not. This seems to me better 
> captured with a boolean, which is then fed back to all the other various 
> downstream ref creation.
> 
> The main observation, at least for me, is that the code unifies everything 
> under refs, when in reality there are different aspects:
> 
> * the set of variables that can point to this, whether directly or indirectly 
> (this is truly a set)
> * whether the current context has a direct or indirect this (this seems more 
> a flag to me)
> * whether the expression on top of stack is direct/indirect this reference or 
> not (again, 3 possible values here) - but granted, there `depth` here to take 
> into account, so you probably end up with a set (given that we don't want to 
> model a scope with its own set)
> 
> When reading the code, seeing set expression like containment checks or 
> removals for things that doesn't seem to be truly sets (unless I'm missing 
> something) was genuinely confusing me.

I get what you're saying - it seems silly to model what is essentially a fixed, 
boolean property with the membership of a singleton in a set field, rather than 
with a simple boolean field.

There is a conceptual trade-off though... a lot of the code relates to 
converting `Ref`'s from one type to another. For example, as pointed out above, 
a method invocation might convert a `ExprRef` to a `ThisRef`, then to a 
`ReturnRef`'s, etc. Having these things all be considered part of the same 
family helps conceptually. The fact that a direct `ThisRef` is a singleton is 
just a coincidence in this way of looking at things.

The benefit is the simplicity of being able to think of the data model as "just 
a set of references".

For example, methods like `RefSet.replaceExprs()` become less elegant (or 
basically impossible) if there have to be special cases for each type of 
reference, whereas currently we can do clean stuff like this:


    @Override
    public void visitReturn(JCReturn tree) {
        scan(tree.expr);
        refs.replaceExprs(depth, ReturnRef::new);
    }


But I'm also realizing now that several places can be cleaned up by taking this 
event further. E.g., we should replace this code:

            if (refs.contains(ThisRef.direct()))
                outerRefs.add(OuterRef.direct());
            if (refs.contains(ThisRef.indirect()))
                outerRefs.add(OuterRef.indirect());

with something like this:

            refs.mapInto(outerRefs, ThisRef.class, OuterRef::new);


I will go through and refactor to do that and clean things up a little more.

> When reading the code, seeing set expression like containment checks or 
> removals for things that doesn't seem to be truly sets (unless I'm missing 
> something) was genuinely confusing me.

Probably my fault for not providing better documentation of the overall "set of 
references" conceptual approach. FWIW I added a little bit more in f83a9cf0.

-------------

PR: https://git.openjdk.org/jdk/pull/11874

Reply via email to