[
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)