[
https://issues.apache.org/jira/browse/GROOVY-12292?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18107211#comment-18107211
]
Paul King edited comment on GROOVY-12292 at 8/24/26 1:29 AM:
-------------------------------------------------------------
paulk-asert opened a new pull request, #2829:
URL: https://github.com/apache/groovy/pull/2829
GROOVY-12292: https://issues.apache.org/jira/browse/GROOVY-12292
A method-level `@TypeChecked` or `@CompileStatic` annotation with the default
(non-SKIP) mode was silently ignored when the declaring class carried a
SKIP-mode annotation. Four combinations were affected:
- `@CompileDynamic` class + `@CompileStatic` method
- `@CompileStatic(SKIP)` class + `@CompileStatic` method
- `@TypeChecked(SKIP)` class + `@CompileStatic` method
- `@TypeChecked(SKIP)` class + `@TypeChecked` method
The cause is in `StaticTypeCheckingVisitor#isSkipMode`, which recursed to the
declaring class without first considering that the method's own annotation
had
already answered the question. Nested classes take a different code path and
already honour the more specific annotation (GROOVY-10238), as does the
opt-out direction (checked class + SKIP method), making methods the lone
anomaly. The behaviour is longstanding (reproduced identically on 4.0.27,
5.0.6 and 6.0.0-alpha-1).
The fix stops the walk up to the declaring class when the node itself carries
one of the visitor's type-checking annotations with a non-SKIP mode: the most
specific annotation wins, and a class-level SKIP remains the default for
members without their own annotation. Because `StaticCompileTransformation`
derives the `STATIC_COMPILE_NODE` metadata from the same method, bytecode
generation follows: an opted-in method under a `@CompileDynamic` class is now
statically compiled, not just checked (asserted in the new bytecode test).
Deliberately unchanged, now ratified by tests and documentation:
- opt-out direction: SKIP-mode methods inside checked classes are skipped as
before;
- cross-family behaviour: method-level `@CompileStatic(SKIP)` /
`@CompileDynamic` disables static compilation but does not exempt the
method from an enclosing class's `@TypeChecked` checking; only
`@TypeChecked(SKIP)` does.
Since the change means previously-dynamic (and unchecked) method bodies are
now checked and statically compiled, it is behaviour-changing: a
COMPATIBILITY.md entry is included and the JIRA issue should get the
`breaking` label, targeting 6.0 only. Documentation of the precedence rules
is added to the "Skipping sections" part of core-semantics.adoc.
was (Author: githubbot):
paulk-asert opened a new pull request, #2829:
URL: https://github.com/apache/groovy/pull/2829
GROOVY-12292: https://issues.apache.org/jira/browse/GROOVY-12292
A method-level `@TypeChecked` or `@CompileStatic` annotation with the default
(non-SKIP) mode was silently ignored when the declaring class carried a
SKIP-mode annotation. Four combinations were affected:
- `@CompileDynamic` class + `@CompileStatic` method
- `@CompileStatic(SKIP)` class + `@CompileStatic` method
- `@TypeChecked(SKIP)` class + `@CompileStatic` method
- `@TypeChecked(SKIP)` class + `@TypeChecked` method
The cause is in `StaticTypeCheckingVisitor#isSkipMode`, which recursed to the
declaring class without first considering that the method's own annotation
had
already answered the question. Nested classes take a different code path and
already honour the more specific annotation (GROOVY-10238), as does the
opt-out direction (checked class + SKIP method), making methods the lone
anomaly. The behaviour is longstanding (reproduced identically on 4.0.27,
5.0.6 and 6.0.0-alpha-1).
The fix stops the walk up to the declaring class when the node itself carries
one of the visitor's type-checking annotations with a non-SKIP mode: the most
specific annotation wins, and a class-level SKIP remains the default for
members without their own annotation. Because `StaticCompileTransformation`
derives the `STATIC_COMPILE_NODE` metadata from the same method, bytecode
generation follows: an opted-in method under a `@CompileDynamic` class is now
statically compiled, not just checked (asserted in the new bytecode test).
Deliberately unchanged, now ratified by tests and documentation:
- opt-out direction: SKIP-mode methods inside checked classes are skipped as
before;
- cross-family behaviour: method-level `@CompileStatic(SKIP)` /
`@CompileDynamic` disables static compilation but does not exempt the
method from an enclosing class's `@TypeChecked` checking; only
`@TypeChecked(SKIP)` does.
Since the change means previously-dynamic (and unchecked) method bodies are
now checked and statically compiled, it is behaviour-changing: a
COMPATIBILITY.md entry is included and the JIRA issue should get the
`breaking` label, targeting 6.0 only. Documentation of the precedence rules
is added to the "Skipping sections" part of core-semantics.adoc.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
> Various combinations of type-checking and static-compilation do not correctly
> combine
> -------------------------------------------------------------------------------------
>
> Key: GROOVY-12292
> URL: https://issues.apache.org/jira/browse/GROOVY-12292
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 2.5.23, 3.0.25, 5.0.3, 4.0.31
> Reporter: Björn Kautler
> Assignee: Paul King
> Priority: Major
> Labels: breaking
>
> Given this code:
> https://groovyconsole.dev/?g=groovy_5_0&codez=eNrtkl8LgjAUxd_3KS4-6YvSv_fAXiKCQL_AmqtG6WQbgYTfPTXNWqUWRAXt8XLO2bk_LgtjLhSsBef7xFYCR3LFRWi7PIzZjk6SCIeMINYs8xRWDSo_iam7oWRLg0ojC0eDlEXrOQ-o7c2mC4TGWh-yw1KCT6XqwQFB9sbXVfJRQFeATasUABiGrbinRBYtTasYpuiO2cz_tM4Ry24Rl0tWVvLQCo4D5PQpUCG4eJSllQnayqQ1rBJFzaqvsapo_mFp29TIBm9A9sOwLqvUkIYvQ2ou-yy1J9K05T-P8-byRl9wea8haz3eDmyOjbPXaQ
> {code:groovy}
> import groovy.transform.CompileDynamic
> import groovy.transform.CompileStatic
> import groovy.transform.TypeChecked
> import static groovy.transform.TypeCheckingMode.SKIP
> @CompileDynamic
> class Test1 {
> @CompileStatic
> def a() {
> "".toStrings()
> }
> @CompileStatic(SKIP)
> def b() {
> "".toStrings()
> }
> @TypeChecked
> def c() {
> "".toStrings() // compile error
> }
> @TypeChecked(SKIP)
> def d() {
> "".toStrings()
> }
> }
> @CompileStatic
> class Test2 {
> @CompileDynamic
> def a() {
> "".toStrings()
> }
> @CompileStatic(SKIP)
> def b() {
> "".toStrings()
> }
> @TypeChecked
> def c() {
> "".toStrings() // compile error
> }
> @TypeChecked(SKIP)
> def d() {
> "".toStrings()
> }
> }
> @CompileStatic(SKIP)
> class Test3 {
> @CompileDynamic
> def a() {
> "".toStrings()
> }
> @CompileStatic
> def b() {
> "".toStrings()
> }
> @TypeChecked
> def c() {
> "".toStrings() // compile error
> }
> @TypeChecked(SKIP)
> def d() {
> "".toStrings()
> }
> }
> @TypeChecked
> class Test4 {
> @CompileDynamic
> def a() {
> "".toStrings() // compile error
> }
> @CompileStatic
> def b() {
> "".toStrings() // compile error
> }
> @CompileStatic(SKIP)
> def c() {
> "".toStrings() // compile error
> }
> @TypeChecked(SKIP)
> def d() {
> "".toStrings()
> }
> }
> @TypeChecked(SKIP)
> class Test5 {
> @CompileDynamic
> def a() {
> "".toStrings()
> }
> @CompileStatic
> def b() {
> "".toStrings()
> }
> @CompileStatic(SKIP)
> def c() {
> "".toStrings()
> }
> @TypeChecked
> def d() {
> "".toStrings()
> }
> }
> {code}
> I'd say the annotations do not combine correctly.
> For example {{@CompileDynamic}} or {{@CompileStatic(SKIP)}} or
> {{@TypeChecked(SKIP)}} on class and {{@CompileStatic}} on method does not do
> type-check?
> And also {{@TypeChecked(SKIP)}} on class and {{@TypeChecked}} on method does
> not do type-check?
> At least those 4 combinations appear to be fishy to me.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)