[
https://issues.apache.org/jira/browse/GROOVY-12616?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18136287#comment-18136287
]
ASF GitHub Bot commented on GROOVY-12616:
-----------------------------------------
paulk-asert opened a new pull request, #3164:
URL: https://github.com/apache/groovy/pull/3164
Closures, lambdas, method references and method pointers now share the
method-return check (assignment rules plus loss of precision); a method
reference returning a type parameter is resolved and checked by the type
checker, and static compilation converts what LambdaMetafactory cannot. Adds
ReturnConversionConsistencyTest to keep the return forms in step.
Scott's PRs had Java-like behavior for some char conversions. I have
reverted those to existing behavior. The restructuring would allow us to
reconsider such decisions and make the change in one place.
## Return conversions after this change
A value returned to a primitive functional interface type is now checked as
the `return` of a method declared with that type. A method's `return`, a
lambda, a closure, a method reference (`H::v`), a method reference to a generic
method (`H::id` with `static <T> T id(T t)`) and a method pointer (`H.&v`) give
the same result under both `@TypeChecked` and `@CompileStatic` (checked by
`ReturnConversionConsistencyTest`):
| returned type ↓ / return type → | `int` | `long` | `double` | `boolean` |
`char` |
|---|---|---|---|---|---|
| `byte`, `short`, `Integer` | ✓ | ✓ | ✓ | ✓ | ✗ |
| `int` | ✓ | ✓ | ✓ | ✓ | ✓ ¹ |
| `long`, `Long` | ✗ ² | ✓ | ✓ | ✓ | ✗ |
| `float`, `double` | ✗ ² | ✗ ² | ✓ | ✓ | ✗ |
| `char` | ✗ | ✗ | ✗ | ✓ | ✓ |
| `boolean` | ✗ | ✗ | ✗ | ✓ | ✗ |
| `BigDecimal` | ✗ | ✗ | ✓ | ✓ | ✗ |
| `BigInteger` | ✗ | ✗ | ✗ | ✓ | ✗ |
| `String` | ✗ | ✗ | ✗ | ✓ | ✗ |
✓ compiles and converts the value at run time (`boolean` by Groovy truth); ✗
compile error.
¹ Except a method reference to a generic method: its type argument is
`Integer`, and a method cannot return an `Integer` as a `char` either.
² "Possible loss of precision". A constant that fits is accepted, as for a
method's `return`, for example `IntSupplier s = () -> 7L`.
## Relationship to #3142, #3143 and #3149
This builds on @codeconsole #3142 (GROOVY-12616), #3143 (GROOVY-12617) and
#3149 (GROOVY-12623), whose commits are included unchanged, apart from
resolving the conflict #3143 and #3149 described. Those PRs gave closures and
lambdas JLS widening and method references cast-like conversion. The commit on
top makes them all follow the rule for a method's `return` instead, so that
every form agrees (see the table above).
**Closures and lambdas (#3142)**
- `isWideningPrimitive` is replaced by the shared `checkReturnValue`, which
is also used for a method's `return`.
- `char` is no longer widened to `int` (it isn't for an assignment or a
method's `return` either).
- Newly accepted: a constant that fits (`IntSupplier s = () -> 7L`) and
`BigDecimal` for `double`/`float`; the GROOVY-8168 case now compiles and runs.
- A narrowing is reported as "Possible loss of precision", as for a method.
**Method references with a concrete return type (#3143)**
- The type check uses `checkReturnValue`, so narrowing is now an error
(`IntSupplier s = C::longValue`, as for `int m() { longValue() }`).
- The code writer's adapter is kept for what `LambdaMetafactory` cannot
convert: Groovy truth, and `BigDecimal` to `double`/`float`.
- The adapter decision is merged with #3149's into one
`isReturnConversionRequired`, which uses `isAssignableTo` rather than its own
widening table.
**Method references returning a type parameter (#3149)**
- The type checker now resolves the method's type parameter from the
functional interface's parameter types and checks it. Previously it skipped the
check.
- The code writer reads the resolved type, instead of resolving it a second
time.
- Newly rejected: `ToIntFunction<Character> f = C::id`,
`ToIntFunction<String> f = C::id`, `ToChar<String> f = C::id` and
`ToIntFunction<Long> f = C::id`. The last one compiles and runs on master; see
`COMPATIBILITY.md`.
- Groovy truth, serialization and the cases needing no conversion are
unchanged.
**Also included**
- A method pointer (`.&`) assigned to a functional interface is checked as
it already is when passed as an argument.
- `ReturnConversionConsistencyTest` checks a method's `return` against
lambdas, closures, method references and method pointers, with deliberate
exceptions listed.
- Javadoc on `checkCompatibleAssignmentTypes` and `isAssignableTo` says
which context each serves.
- The `groovy-internals` skill gains a failure mode about adding a separate
compatibility rule.
- A Groovy 7 `COMPATIBILITY.md` entry.
> STC rejects a closure or lambda that returns a primitive needing only a
> widening conversion to the SAM's primitive return type
> ------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12616
> URL: https://issues.apache.org/jira/browse/GROOVY-12616
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 4.0.33, 5.0.8, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> {code:groovy}
> import java.util.function.ToLongFunction
> @groovy.transform.CompileStatic
> class C {
> static void main(args) {
> ToLongFunction<String> f = (String s) -> s.length()
> assert f.applyAsLong('abc') == 3L
> }
> }
> {code}
> Actual: {{[Static type checking] - Cannot return value of type int for lambda
> expecting long}}. The same happens under @TypeChecked, with a closure (passed
> for a ToLongFunction parameter, or a closure returning 42 assigned to a
> LongSupplier), for ToDoubleFunction from int, long or float, for char to int,
> and for Integer to long.
> Expected: compiles and runs, as in Java (JLS 5.2 allows a widening primitive
> conversion, optionally after unboxing, in an assignment context) and as with
> the method reference {{C::len}} or method pointer {{C.&len}}. Narrowing (long
> to int) should still be rejected.
> Cause: {{StaticTypeCheckingVisitor.checkReturnType}} accepts a closure or
> lambda return value only when the wrapper of the SAM return type is
> compatible with the wrapper of the value type, and Long is not compatible
> with Integer.
> Seen in 4.0.33, 5.0.8 and 6.0.0.
> PR: https://github.com/apache/groovy/pull/3142
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)