JDK-8069544 : 18.5.2: Define method invocation compatibility without forcing lambdas to be checked
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 8
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2015-01-20
  • Updated: 2022-07-22
  • Resolved: 2016-09-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Fixed
Related Reports
Relates :  
Relates :  
Description
Overload resolution generally tests that each argument expression is compatible with the corresponding parameter type.  In the case in which the argument is a poly method (or constructor) invocation, it is incorrect to test for full compatibility, because this requires forcing all nested implicit lambdas, etc., to be type-checked, per 18.5.2.  To preserve the intended invariant that every lambda is type-checked exactly once, the lambdas should not be type-checked until overload resolution is complete.

There are already exceptions to the full compatibility test for arguments during overload resolution:
- Certain lambdas and method references are not considered "pertinent to applicability" (15.12.2.2)
- If the outer invocation requires inference, and one of the parameter types involves a type parameter, the corresponding argument is subject to only part of the compatibility test (18.2.1, 18.5.2).

Unfortunately, neither of these exceptions applies to a nested poly invocation in which the targeted parameter type is a proper type.

Example:

    interface Box<T> {
        T get();
        <R> R map(Function<T,R> f);
    }

    void print(Object arg) { }
    void print(String arg) { }

    void test(Box<String> b) {
        print(b.map(s -> s.getClass()));
    }

Expected behavior: the lambda is ignored during overload resolution of 'map' and 'print'; the 'b.map(...)' argument is tested for compatibility via 18.5.2, but the test stops when bound set B3 is produced.  Since B3 is valid for both target types 'Object' and 'String', both 'print' methods are applicable, and 'print(String)' is selected as the most specific.  This leads to a type error in the lambda body (found Class, expected String).
Comments
Revised text for 18.5.2 ------------------------------ Given a method invocation that provides no explicit type arguments, and a corresponding most specific applicable generic method m, the process to infer the invocation type (§15.12.2.6) of the chosen method ***may require resolving additional constraints, both to assert compatibility with a target type and to assert the validity of the invocation's arguments.*** ***[Move some non-normative text to here:]*** It is important to note that ***multiple*** "rounds" of inference are involved in finding the type of a method invocation. This is necessary ***, for example,*** to allow a target type to influence the type of the invocation without allowing it to influence the choice of an applicable method. The first round (18.5.1) produces a bound set and tests that a resolution exists, but does not commit to that resolution. ***Subsequent rounds reduce*** additional constraints ***until we perform a final resolution step to determine the "real" type of the expression***. ***18.5.2.1 Poly Method Invocation Compatibility*** ***If the method invocation is a poly expression (15.12.2), its compatibility with a target type T is determined as follows.*** ***If the method invocation appears in a strict invocation context and T is a primitive type, the expression is not compatible with T.*** ***Otherwise, let*** θ be the substitution [P1:=α1, ..., Pp:=αp] defined in §18.5.1 to replace the type parameters of m with inference variables. Let B2 be the bound set produced by reduction in order to demonstrate that m is applicable in §18.5.1. (While it was necessary in §18.5.1 to demonstrate that the inference variables in B2 could be resolved, in order to establish applicability, the instantiations produced by this resolution step are not considered part of B2.) ***A bound set B3 is*** derived from B2 as follows. Let R be the return type of m, let T be the invocation's target type, and then: - If unchecked conversion was necessary... - Otherwise, if R θ is a parameterized type, G<A1, ..., An>... - Otherwise, if R θ is an inference variable α... - Otherwise, the constraint formula ‹R θ → T› is reduced and incorporated with B2. ***The invocation is compatible with T if B3 does not contain the bound 'false' and resolution of all the inference variables in B3 succeeds (§18.4).*** ***[Move some non-normative text to here:]*** Consider the example from the previous section: ... This inference strategy is different than the Java SE 7 Edition... Under various special circumstances, based on the bounds appearing in B2, we eagerly resolve an inference variable that appears as the return type of the invocation.... ***18.5.2.2 Additional Argument Constraints*** ***The invocation type of a method invocation is determined after considering additional constraints that may be implied by the invocation's arguments.*** ***Let θ be the substitution [P1:=α1, ..., Pp:=αp] defined in §18.5.1 to replace the type parameters of m with inference variables.*** ***Let B3 be the bound set generated in 18.5.2.1 to demonstrate compatibility with the actual target type of the method invocation; or, if the invocation is not a poly expression, let B3 be the same as the bound set produced by reduction in order to demonstrate that m is applicable in §18.5.1. (While it was necessary in §18.5.1 and 18.5.2.1 to demonstrate that the inference variables in the bound set could be resolved, the instantiations produced by these resolution steps are not considered part of B3.)*** A set of constraint formulas, C, is constructed as follows. Let e1, ..., ek be the actual argument expressions of the invocation.... While C is not empty, the following process is repeated... Finally, if B4 does not contain the bound false, the inference variables in B4 are resolved. If resolution succeeds with instantiations T1, ..., Tp for inference variables α1, ..., αp, let θ' be the substitution [P1:=T1, ..., Pp:=Tp]. Then: ... If B4 contains the bound false, or if resolution fails, then a compile-time error occurs. ***The process of reducing additional argument constraints*** may require carefully ***ordering*** constraint formulas of the forms ‹Expression → T›, ‹LambdaExpression →throws T›, and ‹MethodReference →throws T›. To facilitate this ***ordering***, the input variables of these constraints are defined as follows: ... The output variables of these constraints are all inference variables mentioned by the type on the right-hand side of the constraint, T, that are not input variables.
08-01-2016

Supplementary changes to Chapter 15: ------ 15.8.5 If a parenthesized expression appears in a context of a particular kind with target type T (§5 (Conversions and Contexts)), its contained expression similarly appears in a context of the same kind with target type T. If the contained expression is a poly expression (§15.2), the parenthesized expression is also a poly expression. Otherwise, it is a standalone expression. ***A poly parenthesized expression is compatible with a target type T if its contained expression is compatible with T.*** ------ 15.9.3 Otherwise, where mj is the selected method, cj is the chosen constructor. The return type and throws clause of cj are the same as the return type and throws clause determined for mj (§15.12.2.6). ***If the class instance creation expression is a poly expression, its compatibility with a target type is as determined by 18.5.2.1, using mj as the selected method. This test may occur before making a final determination of the expression's invocation type.*** ------ 15.12.2.6 If the chosen method is generic and the method invocation does not provide explicit type arguments, the invocation type is inferred as specified in §18.5.2. ***In this case, if the method invocation is a poly expression, its compatibility with a target type is as determined by 18.5.2.1. This test may occur before making a final determination of the expression's invocation type.*** ------ 15.25.3 Where a poly reference conditional expression appears in a context of a particular kind with target type T, its second and third operand expressions similarly appear in a context of the same kind with target type T. ***A poly reference conditional expression is compatible with a target type T if its second and third operand expressions are compatible with T.*** The type of a poly reference conditional expression is the same as its target type.
18-11-2015

Supplementary changes to Chapter 18: ------ 18.2.1 If the expression is a class instance creation expression or a method invocation expression, the constraint reduces to the bound set B3 which would be used to determine the expression's ***compatibility with target type T***, as defined in ***18.5.2.1***. (For a class instance creation expression, the corresponding "method" used for inference is defined in §15.9.3). ... This condition never arises in practice, due to the handling of implicitly typed lambda expressions in §18.5.1 and the substitution applied to the target type in ***18.5.2.2***. [Same sentence is repeated later.] ... We do not attempt to produce bounds on inference variables that appear in the target function type's throws clause. This is because exception containment is not part of compatibility (§15.27.3) - in particular, it must not influence method applicability (§18.5.1). However, we do get bounds on these variables later, because invocation type inference (***18.5.2.2***) produces exception containment constraint formulas (§18.2.5). Note that if the target type is an inference variable, or if the target type's parameter types contain inference variables, we produce false. During invocation type inference (***18.5.2.2***), extra substitutions are performed in order to instantiate these inference variables, thus avoiding this scenario. (In other words, reduction will, in practice, never be "invoked" with a target type of one of these forms.) ... Otherwise, if the method reference expression elides TypeArguments, and the compile-time declaration is a generic method, and the return type of the compile-time declaration mentions at least one of the method's type parameters, then the constraint reduces to the bound set B3 which would be used to determine the method reference's ***compatibility*** when targeting the return type of the function type, as defined in ***18.5.2.1***. B3 may contain new inference variables, as well as dependencies between these new variables and the inference variables in T. ------ 18.2.2 We avoid this problem in most cases by giving special treatment to inference-variable return types that we know are already constrained to be certain boxed primitive types. See ***18.5.2.1***. ------ 18.2.5 This condition never arises in practice, due to the substitution applied to the target type in ***18.5.2.2***. [Same sentence is repeated later.]
17-11-2015

Refining what I suggested above: really, we don't need a new concept -- "compatibility" is a fine way to describe the process that produces bound set B3. We just need to identify it as such and consistently use the term. Observe that, for lambdas (15.27.3) and method references (15.13.2), there's a distinction between compatibility testing and type checking. A lambda may be found compatible with many types before we assert its actual type (by choosing a target type). Similarly, a poly method invocation or class instance creation might be considered compatible with many types, and crucially that test must not depend on resolution of lambda expression arguments. In fact, *every* poly expression needs to define, independently: i) under what conditions it is compatible with a type, and ii) given an actual target type, the expression's type.
17-11-2015

Changes to the JLS will likely take the following form: - 15.12.2.2-4: Arguments are tested for a more limited form of compatibility (which in most cases is the same as full compatibility). - 18.5.2: For poly method invocations, this more limited form of compatibility is defined as the process that produces bound set B3 (rather than the bound set B4). - 18.5.1, 18.2.1: This limited form of compatibility is applied in inference during the equivalent of 15.12.2.2-4.
04-02-2015