[ 
https://issues.apache.org/jira/browse/GROOVY-12727?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18140590#comment-18140590
 ] 

ASF GitHub Bot commented on GROOVY-12727:
-----------------------------------------

daniellansun commented on PR #3265:
URL: https://github.com/apache/groovy/pull/3265#issuecomment-6718282980

   REQUEST_CHANGES
   
   The Pekko assignment and return both type-check, for a lambda and for a 
closure, and GenericsSTCTest is green including the three new methods. The 
witness is written onto StaticMethodCallExpression and then read only from the 
enclosing-call stack, which visitStaticMethodCallExpression never pushes. By 
INSTRUCTION_SELECTION, StaticImportVisitor has already turned a same-class 
static call and an `import static` call into StaticMethodCallExpression, so 
those still fail with the original error:
   
       static Beh<String> go(int max) { setup(c -> new Bot(c, max)) }
       // Cannot call Bot#<init>(Ctx<String>, int) with arguments [Ctx<? 
extends Object>, int]
   
   javac accepts that. `import static java.util.Comparator.comparing` then 
`Comparator<String> c = comparing(s -> s.length())` fails here with 
Object#length() and Comparator<Object> assigned to Comparator<String>. 
Qualified Behaviors.setup(...) stays a MethodCallExpression, which is the only 
shape the new tests hit.
   
   Please push/pop the static call in visitStaticMethodCallExpression the same 
way visitMethodCallExpression does, and add those two calls beside 
testReturnTypeInferenceWithClosure5. makeArgumentList returns the same 
ArgumentListExpression the parser stored, so the identity check in 
extractGenericsConnectionsFromCallTarget will then succeed.
   
   A few targets the helper’s comment says it determines are still unresolved. 
I compared them with javac on this tip:
   
   - `<T extends CharSequence> List<T> ids(Function<T,T>)` assigned to 
`List<String>` leaves the lambda parameter as CharSequence 
(`CharSequence#substring` is missing; the assignment is List<CharSequence> to 
List<String>). javac uses T = String. An out-of-bound target (`T extends 
Number` against a call whose parameter needs String) stays at Number, so this 
is under-acceptance, not a bad instantiation. putIfAbsent still does not check 
the witness against T’s upper bounds.
   - `List<List<T>>` assigned to `List<List<String>>`, and `Map<A,B>` assigned 
to `Map<String,Integer>`, both leave the variables at Object 
(`Object#length()`, List<List<Object>>, Map<Object,Object>). The return type 
still carries placeholders named T, A, and B. ArrayList<T> versus List<String> 
works; the nested and two-variable cases do not. Map<T,T> versus 
Map<String,Integer> is rejected, which matches javac, but only as a last-write 
in storeGenericsConnection rather than a conflict error.
   - `Beh<? super String> b = Behaviors.setup(c -> new Bot(c))` is still Ctx<? 
extends Object>. javac accepts it: ? super String pins T to String, and 
Beh<String> is a subtype of Beh<? super String>. Skipping every wildcard is 
right for Beh<? extends Object> (test 7, and javac agrees) and too coarse for a 
concrete ? super or ? extends bound.
   - A ternary in an assignment, a call passed to take(Beh<String>), and 
Optional.of(setup(...)) still show Ctx<? extends Object>. The target is applied 
to the ternary node and not to the branch, and checkForTargetType still has the 
TODO for argument parameters. A cast does infer T, because visitCastExpression 
calls applyTargetType; javac does not treat a cast as a poly context for the 
invocation (JLS 15.12) and rejects `(Beh<String>) setup(c -> new Bot(c))`. The 
Groovy result is sound. Worth a comment so the cast hook is intentional.
   - inferReturnTypeGenerics does not see CALL_TARGET_TYPE. `List<CharSequence> 
xs = cons(s -> 'x')` for `Function<? super T, ? extends T>` is inferred as 
List<String> and rejected. javac uses T = CharSequence.
   
   GROOVY-11446 still type-checks (`List<Number> x = m(d)` for `List<?> d` and 
`<T> List<T> m(List<? extends T>)`). There is no SAM argument, so this patch 
does not touch it. javac rejects it. The lambda form of that pattern is 
rejected here. Not a regression; not fixed either.
   
   I would keep Closure7 as the unbounded-wildcard rejection, and add: 
same-class static and import static; T extends CharSequence against 
List<String>; List<List<T>> and Map<A,B>; Beh<? super String> must pass; the 
Map<T,T> / Map<String,Integer> conflict must fail; one ternary and one argument 
context. String as a target is a weak assertion on its own, because STC will 
accept almost any value as a String. The Bot constructor in Closure5 is the 
right kind of check.
   
   The ReturnAdder widening looks safe: the listener constructor does not 
rewrite the method, and an explicit return is stamped only from 
visitReturnStatement. I do not see a merge conflict with the PECS work on the 
lines this patch adds; that branch is not an ancestor of 60711c748721. Once a ? 
super bound is stored as a witness, it will meet the tighter 
compatibleConnection on that branch and should be rechecked.
   




> STC: a generic method's type parameter isn't inferred from the return-type 
> target when the argument is a lambda
> ---------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12727
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12727
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Priority: Major
>
> Under {{@TypeChecked}} (and {{@CompileStatic}}), when a generic method's only 
> argument is an implicitly typed lambda, the type parameter is not inferred 
> from the expected return type. The lambda's parameter is typed with a 
> wildcard ({{Ctx<? extends Object>}}), and a call inside the lambda that needs 
> the concrete type is rejected. javac infers {{T = String}} from the 
> return-type target and compiles the equivalent Java.
> This is the standard Apache Pekko (and Akka) typed-actor factory, 
> {{Behaviors.setup(context -> new MyActor(context))}}, so statically compiled 
> Pekko code needs a type witness or an explicitly typed lambda parameter.
> h3. Repro (no dependencies)
> {code:groovy}
> import java.util.function.Function
> import groovy.transform.TypeChecked
> class Ctx<T> {}
> class Beh<T> {}
> class Behaviors {
>     static <T> Beh<T> setup(Function<Ctx<T>, Beh<T>> factory) { 
> factory.apply(new Ctx<T>()) }
> }
> class Bot extends Beh<String> {
>     Bot(Ctx<String> c, int max) {}
> }
> @TypeChecked
> class M {
>     static Beh<String> lambda(int max) { Behaviors.setup(c -> new Bot(c, 
> max)) }
> }
> {code}
> h3. Actual
> {noformat}
> [Static type checking] - Cannot call Bot#<init>(Ctx<java.lang.String>, int) 
> with arguments [Ctx<? extends java.lang.Object>, int]
> {noformat}
> h3. Expected
> Compiles, as the Java equivalent does with javac ({{T}} inferred as 
> {{String}} from the {{Beh<String>}} return type).
> h3. Workarounds (both compile on 6.0.0)
> {code:groovy}
> Behaviors.<String>setup(c -> new Bot(c, max))                 // explicit 
> type witness
> Behaviors.setup((Ctx<String> c) -> new Bot(c, max))          // explicitly 
> typed lambda parameter
> {code}
> h3. Notes
> * The closure form, Behaviors.setup \{ c -> new Bot(c, max) }, fails with the 
> same message.
> * An assignment target fails the same way as a return target: {{Beh<String> b 
> = Behaviors.setup(c -> new Bot(c, max))}}.
> * Possibly related, not duplicates:
> ** GROOVY-11446: target-type handling for a generic method with wildcards, in 
> the opposite direction (accepted when it should fail).
> ** GROOVY-11077: lambda parameter types from a wildcard SAM; no generic 
> method or inference involved.
> ** GROOVY-12714 / PR #3246: javac-compatibility changes to wildcard 
> containment in the same visitor; they don't appear to supply this inference 
> (not verified against the branch).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to