Scott Murphy Heiberg created GROOVY-12712:
---------------------------------------------

             Summary: for await names its source variable with a JVM-wide 
counter, so class files depend on compile order
                 Key: GROOVY-12712
                 URL: https://issues.apache.org/jira/browse/GROOVY-12712
             Project: Groovy
          Issue Type: Bug
          Components: parser-antlr4
    Affects Versions: 6.0.0
         Environment: Groovy 6.0.0 binary distribution and master 7379c5720d; 
BellSoft JDK 21.0.12; macOS 15 arm64
            Reporter: Scott Murphy Heiberg


Since 6.0.0 the parser desugars a {{for await}} loop into a block that declares 
the iterable in a variable named {{$__forAwaitSource_<n>}} 
({{AsyncTransformHelper.wrapForAwaitLoop}}, called from 
{{AstBuilder.visitForStmtAlt}}). The number comes from a {{static AtomicLong}} 
shared by every compilation in the JVM, not from the source unit, so the same 
file compiles to different class bytes depending on what was compiled before it 
in the same {{groovyc}}, build daemon or IDE session (the name reaches the 
local variable table), and two parses of one file in one JVM give two different 
ASTs.

Reproducer (6.0.0, JDK 21):
{code:groovy}
// Beta.groovy
class Beta {
    static List<String> names() {
        def gen = async { for (String s in ['a', 'b']) { yield return s } }
        List<String> seen = []
        for await (n in gen) { seen << (n as String) }
        seen
    }
}
{code}
{{Alpha.groovy}} is any other class with one {{for await}} loop.
{noformat}
$ groovyc -d one Beta.groovy && javap -p -l -cp one Beta | grep -o 
'\$__forAwaitSource_[0-9]*' | sort -u
$__forAwaitSource_1
$ groovyc -d two Alpha.groovy Beta.groovy && javap -p -l -cp two Beta | grep -o 
'\$__forAwaitSource_[0-9]*' | sort -u
$__forAwaitSource_2
$ cmp one/Beta.class two/Beta.class
one/Beta.class two/Beta.class differ: char 1743, line 9
{noformat}
Consequences: non-reproducible builds; build caches and incremental compilers 
keyed on outputs see a change that is not in the source; and tools that compare 
the CONVERSION-phase AST of a file with a re-parse of it (a formatter checking 
that it preserved the AST) report a difference for every file with a {{for 
await}} loop. The other names generated for {{async}} and {{yield return}} were 
stable across parses.

Suggested fix: number the loops per source unit in {{AstBuilder}} and pass the 
number to {{wrapForAwaitLoop}}, so the name depends on the source alone. Pull 
request to follow.



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

Reply via email to