type variables from enclosing types?

Dan Smith daniel.smith at oracle.com
Fri Dec 6 10:50:22 PST 2013

On Dec 1, 2013, at 7:07 AM, Stephan Herrmann <stephan.herrmann at berlin.de> wrote:

> Hi,
> I couldn't find how type inference would handle type variables
> of an enclosing type.
> Looking at this example:
>  class Outer<O> {
>    class Inner {}
>    static <I> void method(Outer<I>.Inner x) {}
>  }
>  public class ExtendedOuter<E> extends Outer<E> {
>    class ExtendedInner extends Inner {
>      void foo() {
>        method(this);
>      }
>    }
>  }
> Current compilers accept this program, but playing strictly
> by the inference spec, I get funny results:
> 1) We have an initial constraint ⟨this -> Outer<I#0>.Inner⟩
> where a naive implementation might consider "Inner"
> as a proper type. Should a note be added to the definition
> of "proper type", to the effect that enclosing types
> (explicit or implicit) should also be considered when looking
> for a mention of any inference variables?
> Otherwise the initial constraint directly reduces to FALSE.

I agree that the treatment of inner classes of parameterized types is generally lacking in JLS.  But in this case, I don't see a problem: "proper types" are defined as types that do not "mention inference variables".  The Type Outer<I#0>.Inner clearly mentions an inference variable.

> 2) If we interpret Outer<I#0>.Inner as an improper type
> the inference variable I#0 will be inferred to Object,
> but that seems to be the wrong outcome:
> - "this" is an instance of ExtendedOuter<E>.ExtendedInner
> - thus I#0 should be inferred to be E

The type of 'this' is ExtendedOuter<E>.ExtendedInner.  (Again, the spec here is somewhat lacking for inner classes of parameterized types, and for parameterized types in general, but I've tried to clarify in the lambda spec: "The type of 'this' is the class or interface type T within which the keyword this occurs.")

So we have
[ ExtendedOuter<E>.ExtendedInner <: Outer<I#0>.Inner ]
[ Outer<E>.Inner <: Outer<I#0>.Inner ]
E = I#0

I'll agree that the relationship between ExtendedOuter<E>.ExtendedInner and Outer<E>.Inner is not very well spelled out, but that's how subtyping has to work.

I also agree that 18.2.3 does not properly account for this case, because Outer<I#0>.Inner is not, per 4.5, a "parameterized type".  I've created a bug:

> 3) Interestingly, when I modify the program so that the
> inference of I#0 to Object would trigger an error, the
> usage of I forces the inference into better results.
> Does this mean, the "wrong" inference result is acceptable
> because it is irrelevant to the current inference task?

Not entirely clear about what you're saying, but here are some points that I think are relevant:

- If you mean that you tried returning the result and assigning it to something, then yes, it's possible for applicability testing (18.5.1) to come up with one answer for an ivar and for invocation type inference (18.5.2) to get a different one.  This is illustrated in the discussion in those sections.  But keep in mind that the fact that you've allowed Object as a result of 18.5.1 is a bug.

- A good way to observe inference behavior _without_ influencing the results is to invoke a method of the result rather than assigning it. (E.g., 'genericMethod(arg).foo()'.)

- It is always an error if, when we're all done, the type of an argument is incompatible with the corresponding parameter type of the invocation type.  (The invocation type is a proper type, and is the result of instantiating inference variables.)  See Part F, 15.12.3: "It is a compile-time error if an argument to a method invocation is not compatible with its target type, as derived from the invocation type."  For arguments that are "pertinent to applicability", you don't need to worry about that if the inference algorithm is sound -- it's implicit in the inferred bounds.  And if the inference algorithm is not sound, that's a bug.

> 4) I made experiments with pulling E into the inference
> by augmenting 18.2.3 bullet 5.2: I added corresponding
> constraints also for the type variables of enclosing types
> of C<A1, ..., An> and C<B1, ..., Bn>. This directed the
> inference into the expected solution,

Yep, that's the right approach.  The 5.2 bullet is meant to cover this case, too.

> but then it caused
> regressions in situations of nested diamonds like
>  new <>Outer.<>Inner()
>  new <X>Outer.<>Inner()
> I haven't yet analyzed the details of these regressions,
> though.

Looks like an implementation detail to fix.  That's clearly a syntax error in the JLS.  (As is new Outer<>.Inner().)


More information about the lambda-spec-experts mailing list