[ 
https://issues.apache.org/jira/browse/GROOVY-12313?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137085#comment-18137085
 ] 

ASF GitHub Bot commented on GROOVY-12313:
-----------------------------------------

paulk-asert commented on PR #3002:
URL: https://github.com/apache/groovy/pull/3002#issuecomment-6599067181

   @blackdrag re "why is it legal to remove it?": for a *reference* to a local 
variable or parameter (as opposed to its declaration, which the `vexp != 
localVariable` guard leaves alone), the node's `INFERRED_TYPE` is only ever 
written by this method. Each visit computes it from the declaration's type plus 
the current flow (temporary) type info, and nothing earlier in the same visit 
puts a type on that node. An assignment target gets its type stored after the 
target has been visited, and the SC transformers and classgen write only after 
STC has finished.
   
   So when we get here, any `INFERRED_TYPE` already on the node was left by an 
earlier visit of the same node in a different flow context: a revisit, or a 
transform sharing the node across branches. The fresh computation is the 
authoritative one. Before this change the code was asymmetric. It overwrote the 
type when there was narrowing, but left the old value in place when there 
wasn't, and that stale value is the leak. "No narrowing here" is exactly what 
an absent `INFERRED_TYPE` means to readers: they fall back to the variable's 
declared or inferred type. So removing it makes the node say what the current 
flow context says.
   
   @daniellansun:
   1. Flow typing is keyed on the variable, not the node: 
`getInferredTypeFromTempInfo` looks up the accessed variable. So distinct 
reference nodes for the same variable still narrow independently, and the clear 
only touches the node being visited.
   2. GROOVY-12428 has landed. The PR merges cleanly and the STC/SC/typing/bugs 
suites pass on top of it. A shared node across switch cases (`case Map:` then 
`default:`) fails on master with a `GroovyCastException` because the case's Map 
narrowing reaches `default`, and passes with this change. I'll add that as a 
third test. The negated form (`!(p instanceof Map) ? … : …`), where the 
narrowing is in the branch visited last, works too.
   3. Agreed. Only `INFERRED_TYPE` is flow-derived here. 
`DECLARATION_INFERRED_TYPE` and other keys aren't touched.




> STC: instanceof-inferred type leaks to else-branch when the same 
> VariableExpression node is reused
> --------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12313
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12313
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 5.0.3, 4.0.31
>            Reporter: Björn Kautler
>            Priority: Major
>
> With this in groovyconsole.dev
> {code:java}
> @groovy.transform.CompileStatic
> class Foo extends Specification {
>    def foo() {
>       expect:
>          true
>       where:
>          [a, b] << [['a', 'b']]
>    }
> }
> {code}
> you get
> {code:java}
> Cannot cast object '[a, b]' with class 'java.util.ArrayList' to class 
> 'java.util.Map' due to: groovy.lang.GroovyRuntimeException: Could not find 
> matching constructor for: java.util.Map(String, String){code}
> The AST transformed code after class generation phase for this is
> {code:java}
>     @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = 
> ['a', 'b'])
>     public java.lang.Object $spock_feature_0_0proc(java.lang.Object 
> $spock_p0) {
>         java.lang.Object a = ((java.lang.Object) $spock_p0 instanceof 
> java.util.Map ? $spock_p0.getAt('a') : $spock_p0.getAt(0))
>         java.lang.Object b = ((java.lang.Object) $spock_p0 instanceof 
> java.util.Map ? $spock_p0.getAt('b') : $spock_p0.getAt(1))
>         return new java.lang.Object[]{ a , b }
>     } {code}
> where {{$spock_p0}} is given as {{{}['a', 'b']{}}}.
> Without static compilation this works fine and runs the expected iterations.
> With static compilation and indy switched off, the generated and then 
> decompiled bytecode is this:
> {code:java}
> @DataProcessorMetadata(
>     dataVariables = {"a", "b"}
> )
> public Object $spock_feature_0_0proc(Object $spock_p0) {
>     Object a = $spock_p0 instanceof Map ? 
> DefaultGroovyMethods.getAt((Map)ScriptBytecodeAdapter.castToType($spock_p0, 
> Map.class), "a") : 
> DefaultGroovyMethods.getAt((Map)ScriptBytecodeAdapter.castToType($spock_p0, 
> Map.class), 0);
>     Object b = $spock_p0 instanceof Map ? 
> DefaultGroovyMethods.getAt((Map)ScriptBytecodeAdapter.castToType($spock_p0, 
> Map.class), "b") : 
> DefaultGroovyMethods.getAt((Map)ScriptBytecodeAdapter.castToType($spock_p0, 
> Map.class), 1);
>     return new Object[]{a, b};
> } {code}
> So with static compilation the inferred {{Map}} type somehow leaks into the 
> else-branch of the ternary expression and causes this class-cast exception.



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

Reply via email to