[
https://issues.apache.org/jira/browse/GROOVY-12755?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18143319#comment-18143319
]
ASF GitHub Bot commented on GROOVY-12755:
-----------------------------------------
codeconsole commented on PR #3286:
URL: https://github.com/apache/groovy/pull/3286#issuecomment-6812790172
@daniellansun @paulk-asert thanks, both. I compiled each program against
javac 25.0.4, against master and against this branch. New commits on top of
f3e8d5dc01, after merging #3285 again:
**Generic calls in an assignment or return context** (4ef7a2f21d). A call of
a generic method is now given its type arguments from the target type as well,
as in JLS 18.5.2. The call is accepted when the type arguments the target
determines keep the method's bounds and every argument fits its parameter with
them; closures and lambdas are checked by their return type. This is not
containment: `List<Class<? extends String>>` is still not a `List<Class<?>>`.
- These compile again: `Collections.singletonList(x.getClass())`,
`Arrays.asList(a.getClass(), b.getClass())`, `Optional.of(x.getClass())`,
`Collections.singletonMap` and `Collections.singleton` as `Map<String,
Class<?>>` and `Set<Class<?>>`, a field initializer, a return, and
`list.collect { it.getClass() }` for a `List<Class<?>>`.
- The same rule also accepts `List<Number> l =
Collections.singletonList(1)`, `Arrays.asList(1, 2)` for a `List<Number>`,
`List<Object> l = List.of("a")` and a `Supplier` lambda, which master rejected
and javac accepts.
- These are still rejected, as in javac: `Collections.nCopies(2, "x")` for a
`List<Number>`, `singletonList(x.getClass())` for a `List<Class<? extends
Number>>`, `<T extends Number> List<T> numbers(T)` for a `List<Object>`, and a
`collect` whose closure returns a `String` for a `List<Integer>`.
`list*.getClass()` and `list*.class` stay rejected for a `List<Class<?>>`,
as Daniel asked, while `collect` compiles. Paul, you asked for the spread forms
to compile too. That would mean treating a `List<Class<? extends String>>`
value as a `List<Class<?>>`, which Daniel asked me not to do. I can make it
work for the spread forms alone if you agree between you that you want that.
Two differences from javac are left, and both predate this PR: `List<Object>
l = s.stream().collect(Collectors.toList())` is rejected (inference through the
nested `toList()` call), and `Comparator<Object> c = Comparator.naturalOrder()`
is accepted.
**Overload selection** (4ac91c0b79).
`GenericsSTCTest.testClassOfReceiverSelectsOverloadForItsType` records
`f(StringBuilder)` for `f(x.getClass().cast(o))`,
`f(x.getClass().getDeclaredConstructor().newInstance())` and
`f(create(x.getClass()))`; under `GenericsStaticCompileTest` it fails on
master, which selects `f(Object)`. `GenericsJavaCompatibilityTest` compares the
first two with javac. The COMPATIBILITY entry now describes this, and the
target-type rule above.
**`instanceof`** (471cb3b9da). `o.getClass()` and `o.class` on a variable
narrowed by `instanceof` now take the variable's type without the test, so your
`f(Object o)` is an error again. A pattern variable, a cast and a new local are
`Class<? extends String>`. `def store = new Impl()` keeps `Impl`, because that
is not an `instanceof` narrowing. `o.substring(0)` and `String s = o` in that
`if` are unchanged, as you said.
**A member of a raw type** (487b2eca2f). Your `holder.type` compiles again,
and so does a method of that type. I made it narrower than all of JLS 4.8: a
member of a raw type is erased when its type uses a type parameter whose bound
is a raw type, as `T extends List` does, which is the case where the
substituted bound became `Class<? extends List>`. Erasing every member of a raw
type, as javac does, changes two existing tests:
- `GenericsSTCTest.testIncompatibleArgumentsForPlaceholders3` expects errors
for `stringProperty.eq(1234)` and `numberProperty.eq("xx")` through a raw
`Holder` delegate. With erasure those calls compile, as they do in javac with
an unchecked warning.
- `BugsStaticCompileTest.testShouldAllowSubscriptOperatorOnSet` then fails
in instruction selection on `set[0]` for `new
LinkedHashMap([a:1,b:2]).keySet()`.
I'm happy to do the full erasure, with those two tests changed and the
instruction-selection failure fixed, if you want it here.
**`List<Inner>` within `Outer<T>`** (190b50dceb). The enclosing type now
counts for a type argument, so `List<Inner> a = raw` and `List<? extends Inner>
b = raw` are rejected for a `List<Outer.Inner> raw`, and so are
`List<Outer.Inner> = List<Inner>` and `Map<String, Inner> = Map<String,
Outer.Inner>`. An enclosing type that still has type variables is not compared.
A member's type read through a parameterized receiver keeps `Outer<T>.Inner`,
because the enclosing type is not substituted from the receiver, and comparing
it rejected `List<Outer<String>.Inner> l = outer.list` for an `Outer<String>
outer`. A test covers that form too.
**`Class<T>` and a class type parameter** (a48ffb4db7). `<T> Class<T>
typeOf(T t) { t.getClass() }` and `Class<T> c = t.getClass()` are errors.
Within `Box<T>`, `accept(t.getClass())` for `accept(Class<? extends T>)` and
`byType.put(t.getClass(), t)` for a `Map<Class<? extends T>, T>` are errors, as
in javac. Before, the class's `T` was inferred from the argument; a parameter
typed by type variables in scope is now checked against the argument first. Two
older accepts remain: `<T> void put(Class<T>, T)` called as `put(t.getClass(),
t)` from a generic method, and `accept(String)` within `Box<T>`. Both are
rejected by javac and accepted on master. The `accept` error prints the
parameter as `Class<?>`, because the message substitutes the inferred argument;
that is how the message already read for `Class<?>`.
**Not changed**, as you suggested: `o instanceof Inner`, `new Inner[n]`, and
the simple name in a static method. Nor the narrower declared-type rule for
`Class<ArrayList>`; I can add that as its own commit if you want it.
**`GROOVY_6_0_X`.** The commits by part, for the split you are discussing:
- `getClass()` typing: 2ce6046aa3, f2d84108eb, after GROOVY-12754. The fixes
for Daniel's points that come with it: 4ef7a2f21d (target typing) and
471cb3b9da (`instanceof`).
- Raw bound: 7807d56395, 0750119074, and 487b2eca2f (raw member).
- Resolver: 053e7ead2d, and 190b50dceb (`List<Inner>`).
- Unbounded and lower-bounded wildcards: 2b99f21e83.
- `Class<T>`: a48ffb4db7.
I have not cherry-picked the new commits onto `GROOVY_6_0_X` yet. I'll do
that and run the tests there for whichever set you choose.
> STC: e.getClass() is typed Class<?>, so since GROOVY-12714 it is rejected
> where a Class<? extends T> is expected
> ----------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12755
> URL: https://issues.apache.org/jira/browse/GROOVY-12755
> Project: Groovy
> Issue Type: Bug
> Components: Static Type Checker
> Affects Versions: 7.0.0-alpha-1, 6.0.1
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> JLS 4.3.2 gives a call {{e.getClass()}} the type {{Class<? extends |T|>}},
> where {{|T|}} is the erasure of the static type of {{e}}. Static type
> checking types it with the declared return type of {{Object#getClass()}},
> {{Class<?>}}. Until GROOVY-12714 the wildcard check accepted {{Class<?>}}
> where a {{Class<? extends T>}} was expected. Since GROOVY-12714 (GROOVY_6_0_X
> and master) it is rejected:
> {code:groovy}
> @groovy.transform.CompileStatic
> class C {
> interface Store {}
> static <T> T with(Class<? extends Store> type, Closure<T> body) {
> body.call() }
> static class Adapter { Adapter(Class<? extends Store> type) {} }
> static Object viaMethod(Store store) { with(store.getClass()) { 1 } }
> static Object viaProperty(Store store) { with(store.class) { 1 } }
> static Object viaConstructor(Store store) { new Adapter(store.getClass())
> }
> static Class<? extends Store> viaReturn(Store store) { store.getClass() }
> }
> {code}
> {noformat}
> [Static type checking] - Cannot call <T> C#with(java.lang.Class<? extends
> C.Store>, groovy.lang.Closure<T>) with arguments [java.lang.Class<?>,
> groovy.lang.Closure]
> [Static type checking] - Cannot call <T> C#with(java.lang.Class<? extends
> C.Store>, groovy.lang.Closure<T>) with arguments [java.lang.Class<?>,
> groovy.lang.Closure]
> [Static type checking] - Cannot call C$Adapter#<init>(java.lang.Class<?
> extends C.Store>) with arguments [java.lang.Class<?>]
> [Static type checking] - Incompatible generic argument types. Cannot assign
> java.lang.Class<?> to: java.lang.Class<? extends C.Store>
> {noformat}
> javac accepts the equivalent Java. Grails no longer compiles against
> GROOVY_6_0_X: it passes {{datastore.getClass()}} to a {{Class<? extends
> Datastore>}} parameter and {{registrar.getClass()}} to a {{Class<? extends
> BeanRegistrar>}} one
> (https://github.com/apache/grails-core/actions/runs/42702095261).
> Typing {{e.getClass()}} and {{e.class}} as {{Class<? extends |T|>}} makes
> more calls pass an upper-bounded wildcard for a bounded type parameter, such
> as {{foo(list.class)}} for {{<T extends List<Integer>> foo(Class<T>)}}, which
> GROOVY-12754 rejected. So this depends on GROOVY-12754.
> The inferred type of {{list.class}} for a raw {{List list}} changes from
> {{Class<? extends Object>}} to {{Class<? extends List>}}, and the GROOVY-5415
> error for {{a.bar(this.getClass())}} now names {{Class<? extends ClassB>}};
> the two tests that assert those types are updated.
> PR: https://github.com/apache/groovy/pull/3286
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)