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)

Reply via email to