[
https://issues.apache.org/jira/browse/GROOVY-12417?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18131204#comment-18131204
]
Paul King commented on GROOVY-12417:
------------------------------------
h3. Assessment (Groovy 7 / master)
Lifting the parser check alone is not enough. With the "should be the first
statement" error in {{AstBuilder.createConstructorNodeForClass}} disabled,
every prologue case still fails, just with different errors:
||Case||Result with parser check disabled||
|{{if (age <= 0) throw ...; super(age)}}|Implicit super constructor P() is
undefined|
|{{def s = ...; super(s)}}|Cannot reference 's' before supertype constructor
has been called|
|{{def d = n * 2; this(); x = d}}|VerifyError (two {{<init>}} calls)|
|{{this.name = n; super()}} (early field assignment)|VerifyError|
|closure in prologue, result passed to {{super(...)}}|Cannot reference 'total'
before supertype constructor has been called|
|{{@CompileStatic}} prologue|\[Static type checking\] - Cannot find matching
constructor P()|
|{{if (b) super(1) else super(2)}} (must stay an error)|Implicit super
constructor P() is undefined|
h3. Causes
# *Implicit {{super()}} is still generated.*
{{AsmClassGenerator.visitStdMethod}} and the GROOVY-11274 check in
{{StaticTypeCheckingVisitor.visitConstructorOrMethod}} both test
{{ConstructorNode.firstStatementIsSpecialConstructorCall()}}. With a prologue
that is false, so an implicit {{super()}} is emitted in addition to the
explicit call. The compiled constructor contains {{invokespecial P.<init>()V}}
followed later by {{invokespecial P.<init>(I)V}}. This accounts for all of the
VerifyErrors and the "Implicit super constructor undefined" / "Cannot find
matching constructor" messages.
# *The uninitialized-{{this}} checks have the wrong scope, in both directions.*
#* {{Verifier.visitConstructor}} ({{isNonStaticMemberAccess}}) exempts only
{{Parameter}} and {{DynamicVariable}}, so a prologue local used in the
{{super(...)}} arguments is reported as instance member access. Today only
parameters can be visible there, so the shortcut was sufficient.
#* The {{inSpecialConstructorCall}} flag in {{Verifier}},
{{VariableScopeVisitor}}, {{StaticVerifier}}, {{StaticImportVisitor}} and
{{CompileStack}} (used by {{ClosureWriter}}, {{StaticInvocationWriter}},
{{WriterController}}) is only set while visiting the special call's arguments.
Prologue statements outside the arguments are not checked, so e.g. {{println
name; super()}} would compile and fail at class load, and a closure in the
prologue would be generated with {{this}} as its owner.
# *Field initializer placement.* {{Verifier.addInitialization}} moves the
special call to index 0 and places field initializers after it, but only when
the call is the first statement. With a prologue, the initializers would be
placed before the prologue and before {{super()}}. The JVM accepts {{putfield}}
on {{uninitializedThis}} for the class's own fields, so this would silently run
initializers before the superclass constructor instead of failing. The
{{this$0}} placement for inner classes (GROOVY-4471, GROOVY-7686) lives in the
same code.
# *Everything that locates the special call by position.*
{{getFirstStatement()}},
{{ConstructorNodeUtils.getFirstIfSpecialConstructorCall()}} and
{{firstStatementIsSpecialConstructorCall()}} are used in {{EnumVisitor}},
{{EnumCompletionVisitor}} (adds name/ordinal arguments to the call),
{{InnerClassVisitor}}, {{InnerClassCompletionVisitor}},
{{NullCheckASTTransformation}}, {{InheritConstructorsASTTransformation}},
{{StaticTypeCheckingVisitor}}, {{AsmClassGenerator}} and {{JavaStubGenerator}}
(a prologue constructor would get an empty {{{}}} stub body, so javac would
require a no-arg super constructor).
# *Grammar side.* {{AstBuilder}} converts any {{this(...)}}/{{super(...)}} call
into a special {{ConstructorCallExpression}} wherever it appears. The
first-statement rule is currently what rejects nested forms such as {{if (b)
super(1)}}. A replacement rule is needed: exactly one special call, as a
top-level statement of the constructor body, not inside blocks, loops, try,
switch or closures.
h3. Rules to adopt (JEP 513)
The prologue may declare locals, throw, use parameters and static members, and
assign fields of the class being constructed that have no initializer. It may
not read {{this}} or instance members (explicitly or implicitly), call instance
methods, create inner-class instances or closures that capture {{this}}, or
{{return}}. Records' canonical constructors and enum constructors cannot
contain explicit special calls anyway. The JVM has always permitted this
bytecode shape, so no JDK gate is needed at runtime; only javac required JDK 25.
h3. Shape of a fix
# {{ConstructorNode}}: replace the first-statement predicates with helpers that
find the special call's index / presence; migrate the sites listed above.
# {{AstBuilder}}: replace the error with the top-level / exactly-one
validation, plus a {{return}}-in-prologue check.
# {{Verifier.addInitialization}}: splice field initializers after the special
call wherever it is; keep {{this$0}} before it.
# A prologue context flag in {{Verifier}}, {{VariableScopeVisitor}},
{{StaticVerifier}}, {{StaticImportVisitor}} and {{CompileStack}} so analysis
and codegen treat the prologue like a static context (closure owner,
implicit-{{this}} calls, inner class creation); fix the local-variable false
positive in {{isNonStaticMemberAccess}}.
# STC/SC: the GROOVY-11274 implicit-super check, and {{StaticInvocationWriter}}
for early field assignment.
# {{JavaStubGenerator}}: locate the call wherever it is in the body.
# Tests: the three negative cases in {{ParserNegativeSyntaxTest}} become
positive; add dynamic and {{@CompileStatic}} cases for prologue locals, early
throw, early field assignment, closures in the prologue, inner classes and
enums, plus the must-fail cases (nested call, instance member read, {{return}}).
# Docs: constructor section of the language specification.
Rough size: 12-15 main source files plus tests. The risk is concentrated in
items 3 and 4, where incorrect handling produces VerifyErrors at class load
rather than compile errors.
> Support Flexible Constructor Bodies (JEP-513) Delivered in Java 25+
> -------------------------------------------------------------------
>
> Key: GROOVY-12417
> URL: https://issues.apache.org/jira/browse/GROOVY-12417
> Project: Groovy
> Issue Type: Improvement
> Components: Compiler
> Reporter: Matt Micheletti
> Priority: Major
> Fix For: 7.x
>
>
> h1. Summary
> Relatively recently, Java added support for allowing code before the
> obligatory `this(...)`/`super(...)` statements in constructors. Essentially,
> the JEP loosens the decades long restriction of _always_ forcing one of those
> aforementioned statements to be the mandatory very first statement. There may
> now be early constructor code that appears _before_ `this(...)`/`super(...)`
> albeit with some practical JLS limitations. This feature was fully accepted,
> delivered, and shipped as finalized in JDK 25.
> Groovy should continue aligning with supporting as much of Java syntax and
> semantics as possible. Subsequently, Groovy's internal handling of
> constructors should be enhanced to allow supporting JEP-513 rather than
> maintaining the previously held restriction.
> h1. Expected Behavior
> The following code should successfully compile and work as expected,
> intended, and/or desired as if it was written in Java.
> {code:java}
> class Child extends Parent
> {
> public Child(int age)
> {
> if (age <= 0)
> {
> throw new IllegalArgumentException(...);
> }
> super(age);
> //...
> }
> }{code}
>
> h1. Actual Behavior
> Currently, the Groovy compiler hard bakes in compliance checks to verify
> against the long standing restriction of enforcing constructors' special
> semantics around `this(...)`/`super(...)`.
> {code:java}
> class Child extends Parent
> {
> public Child(int age)
> {
> if (age <= 0) // Compiler error: "super (age) should be the first
> statement in the constructor[Child] @ line X, column Y."
> {
> throw new IllegalArgumentException(...);
> }
> super(age);
> //...
> }
> } {code}
> h6. Source/Attribution Credits:
> - [JEP-513 (Flexible Constructor Bodies)|https://openjdk.org/jeps/513]
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)