Paul King created GROOVY-12494:
----------------------------------
Summary: SC: a reassigned local whose declaration flow-types it
narrower than its declared type stores a polluted generic value with a Groovy
cast, silently converting it
Key: GROOVY-12494
URL: https://issues.apache.org/jira/browse/GROOVY-12494
Project: Groovy
Issue Type: Task
Components: Static compilation
Affects Versions: 2.5.23, 3.0.25, 4.0.33, 5.1.3, 6.0.0
Reporter: Paul King
Description:
GROOVY-12469 stops static compilation from casting to the type inferred from
generics when a declaration stores a value of a wider runtime type. The
bytecode slot of a local is typed from the type that flow typing infers across
all of its assignments, though, so two related shapes still store through a
cast to that narrower type:
{code:groovy}
import groovy.transform.CompileStatic
@CompileStatic
CharSequence reassigned(List<String> list) {
CharSequence cs = 'init'
cs = list.get(0)
cs
}
@CompileStatic
CharSequence assignedLater(List<String> list) {
CharSequence cs
cs = list.get(0)
cs
}
List polluted = [new StringBuilder('sb')]
assert reassigned(polluted) instanceof StringBuilder // fails: String
assert assignedLater(polluted) instanceof StringBuilder // fails: String
{code}
The slot for {{cs}} is typed {{String}}. Storing the StringBuilder from the
polluted list into it applies a Groovy cast, which converts the value with
{{toString()}} rather than failing, so the method returns a different object of
a different class. Where no such conversion exists, the store throws a
GroovyCastException that javac-compiled code would not throw, since javac
stores to the declared type and casts only where the narrower type is used.
When the declaration's own value is not of the narrow type (for example
{{CharSequence cs = null; cs = list.get(0)}}), the slot keeps the declared type
and the value is stored unchanged.
Possible direction: give a local with an explicit declared type a slot of that
declared type, and cast only where a use relies on the flow-typed type, as
GROOVY-12469 does for declarations. That changes code generation for every
typed local whose flow type is narrower, so it needs a look at bytecode size
and performance, and at tests that assert on generated code.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)