[
https://issues.apache.org/jira/browse/GROOVY-12513?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Scott Murphy Heiberg updated GROOVY-12513:
------------------------------------------
Description:
When a variable is declared with a class that has a private field, and the
variable is assigned an instance of a subclass, the type checker uses the
narrowed type for the field and rejects it, although the declared type has the
field. A private method of the same class is accepted through the same variable:
{code:groovy}
@groovy.transform.CompileStatic
class Outer {
static class Box {
private String f = 'f'
private String im() { 'im' }
}
static class Sub extends Box { }
static test() {
Box b = new Sub()
b.im() + b.f
}
}
Outer.test()
{code}
{code}
[Static type checking] - No such property: f for class: Outer$Sub
{code}
Through a parameter declared as {{Box}}, the same {{b.f}} compiles under
{{@CompileStatic}} and returns {{f}} for a {{Sub}}. javac accepts {{Box b = new
Sub(); b.f}} (the static type of {{b}} is {{Box}}, which declares {{f}}), and
also a write to it.
The same happens under {{@TypeChecked}}, for a write ({{b.f = 'w'}}), a
compound assignment ({{b.f += 'x'}}), a private getter used as a property
({{b.x}} with {{private String getX()}}: "No such property: x for class:
Outer$Sub"), attribute access ({{b.@f}}: "Cannot access field: f of class:
Outer$Box"), inside a closure or lambda, and when a {{Box}} parameter is
narrowed by {{if (b instanceof Sub)}}. Seen in 5.0.8, 6.0.0 and master.
Dynamic Groovy throws {{MissingPropertyException}} for {{b.f}} whenever the
run-time class is {{Sub}}, also for the parameter, because it looks at the
run-time class; {{@TypeChecked}} dispatches the property access dynamically and
therefore does the same at run time once it compiles.
Expected: like a private method, a private field or accessor of the variable's
declared type is accessible through the variable, from every class that may
access the member, even when flow typing narrows the variable to a subclass. A
receiver whose type is only the subclass ({{new Sub().f}}, or a {{def}}
variable) stays rejected, because a private member is not inherited.
was:
When a variable is declared with a class that has a private field, and the
variable is assigned an instance of a subclass, the type checker uses the
narrowed type for the field and rejects it, although the declared type has the
field. A private method of the same class is accepted through the same variable:
{code:groovy}
@groovy.transform.CompileStatic
class Outer {
static class Box {
private String f = 'f'
private String im() { 'im' }
}
static class Sub extends Box { }
static test() {
Box b = new Sub()
b.im() + b.f
}
}
Outer.test()
{code}
{code}
[Static type checking] - No such property: f for class: Outer$Sub
{code}
Through a parameter declared as {{Box}}, the same {{b.f}} compiles under
{{@CompileStatic}} and returns {{f}} for a {{Sub}}. javac accepts {{Box b = new
Sub(); b.f}} (the static type of {{b}} is {{Box}}, which declares {{f}}), and
also a write to it.
The same happens under {{@TypeChecked}}, for a write ({{b.f = 'w'}}), a
compound assignment ({{b.f += 'x'}}), a private getter used as a property
({{b.x}} with {{private String getX()}}: "No such property: x for class:
Outer$Sub"), attribute access ({{b.@f}}: "Cannot access field: f of class:
Outer$Box"), inside a closure or lambda, and when a {{Box}} parameter is
narrowed by {{if (b instanceof Sub)}}. Seen in 5.0.8, 6.0.0 and master.
Dynamic Groovy throws {{MissingPropertyException}} for {{b.f}} whenever the
run-time class is {{Sub}}, also for the parameter, because it looks at the
run-time class; {{@TypeChecked}} dispatches the property access dynamically and
therefore does the same at run time once it compiles.
Expected: like a private method, a private field or accessor of the variable's
declared type is accessible through the variable, from every class that may
access the member, even when flow typing narrows the variable to a subclass. A
receiver whose type is only the subclass ({{new Sub().f}}, or a {{def}}
variable) stays rejected, because a private member is not inherited.
PR: https://github.com/apache/groovy/pull/3044
> STC: private field of a variable's declared type is not accessible when flow
> typing narrows the variable to a subclass
> ----------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12513
> URL: https://issues.apache.org/jira/browse/GROOVY-12513
> Project: Groovy
> Issue Type: Bug
> Components: Static Type Checker
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> When a variable is declared with a class that has a private field, and the
> variable is assigned an instance of a subclass, the type checker uses the
> narrowed type for the field and rejects it, although the declared type has
> the field. A private method of the same class is accepted through the same
> variable:
> {code:groovy}
> @groovy.transform.CompileStatic
> class Outer {
> static class Box {
> private String f = 'f'
> private String im() { 'im' }
> }
> static class Sub extends Box { }
> static test() {
> Box b = new Sub()
> b.im() + b.f
> }
> }
> Outer.test()
> {code}
> {code}
> [Static type checking] - No such property: f for class: Outer$Sub
> {code}
> Through a parameter declared as {{Box}}, the same {{b.f}} compiles under
> {{@CompileStatic}} and returns {{f}} for a {{Sub}}. javac accepts {{Box b =
> new Sub(); b.f}} (the static type of {{b}} is {{Box}}, which declares {{f}}),
> and also a write to it.
> The same happens under {{@TypeChecked}}, for a write ({{b.f = 'w'}}), a
> compound assignment ({{b.f += 'x'}}), a private getter used as a property
> ({{b.x}} with {{private String getX()}}: "No such property: x for class:
> Outer$Sub"), attribute access ({{b.@f}}: "Cannot access field: f of class:
> Outer$Box"), inside a closure or lambda, and when a {{Box}} parameter is
> narrowed by {{if (b instanceof Sub)}}. Seen in 5.0.8, 6.0.0 and master.
> Dynamic Groovy throws {{MissingPropertyException}} for {{b.f}} whenever the
> run-time class is {{Sub}}, also for the parameter, because it looks at the
> run-time class; {{@TypeChecked}} dispatches the property access dynamically
> and therefore does the same at run time once it compiles.
> Expected: like a private method, a private field or accessor of the
> variable's declared type is accessible through the variable, from every class
> that may access the member, even when flow typing narrows the variable to a
> subclass. A receiver whose type is only the subclass ({{new Sub().f}}, or a
> {{def}} variable) stays rejected, because a private member is not inherited.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)