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

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

codeconsole opened a new pull request, #3091:
URL: https://github.com/apache/groovy/pull/3091

   The type checker narrowed a variable after any `instanceof` check on it, for 
the rest of the statement or condition, wherever the check was. Under 
`@CompileStatic` that compiled code that fails:
   
   ```groovy
   @CompileStatic
   String f(CharSequence cs) { 'x' + (cs instanceof String) + cs.length() }
   f(new StringBuilder('ab'))  // ClassCastException
   
   @CompileStatic
   int h(Object x) { if ((x instanceof String) == false) { return x.length() }; 
0 }
   h(42)                       // ClassCastException
   
   @CompileStatic
   int g(Object x) { while (x instanceof String) { break }; x.length() }
   g(42)                       // ClassCastException
   ```
   
   Three commits:
   
   - An instanceof check now narrows only where its outcome guards what 
follows: the condition of an `if`, `while`, `assert` or conditional expression, 
an operand of `&&`, `||` or `!` there, and an operand of `&&` or `||` anywhere. 
In `foo(cs instanceof String, cs.length())`, `[cs instanceof String, cs]` or 
`(cs instanceof String) == cs.isEmpty()`, the later `cs` is no longer taken for 
a `String`. The right operand of `&` no longer sees the narrowing of the left 
one; both still apply after the `&`.
   - A comparison of a check with a boolean literal, on either side, with `==`, 
`!=`, `===` or `!==`, narrows like the check when it is true exactly when the 
check is (`== true`, `!= false`), and like `!(check)` otherwise. Before, `== 
false` narrowed its own branch to `T`, so the second example compiled and 
failed; it is now a compile error, as with `!`, and the else branch and the 
code after an `if` that returns are narrowed. An assigned check, a check cast 
to `boolean`, and the condition of a classic `for` loop keep narrowing as they 
did.
   - The condition and the body of a `while`, `do`/`while` or classic `for` 
loop get their own scope of temporary type information, as the branches of an 
`if` do: the condition narrows in the loop and no longer after it. Assignments 
in the loop still cancel, after it, what an enclosing check told about the 
variable.
   
   Code that relied on the narrowing outside a guard stops compiling. In each 
such case, it compiled to a cast that fails for the other types.
   
   ## Tests
   
   `TypeInferenceSTCTest.testInstanceOf30`, `testInstanceOf31` and 
`testInstanceOf32`, also run statically compiled, fail without the change. 
11,630 tests in the `groovy`, `gls`, `bugs` and STC/SC packages pass, apart 
from six `@Grab` tests that pass on rerun. The Checkstyle and CodeNarc gates 
pass.
   
   The loop and `== false` cases fail in 4.0.33, 5.0.8 and 6.0.0; the first 
example compiles and runs in 4.0.33 and fails in 5.0.8 and 6.0.0.
   




> STC: instanceof narrows a variable where the check does not guard the code
> --------------------------------------------------------------------------
>
>                 Key: GROOVY-12562
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12562
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static Type Checker
>    Affects Versions: 4.0.33, 5.0.8, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> The type checker narrows a variable after any {{instanceof}} check on it, for 
> the rest of the expression or statement, wherever the check is. Under 
> {{@CompileStatic}} the program then fails with a ClassCastException:
> {code:groovy}
> @groovy.transform.CompileStatic
> String f(CharSequence cs) { 'x' + (cs instanceof String) + cs.length() }
> f(new StringBuilder('ab')) // ClassCastException: StringBuilder cannot be 
> cast to String (5.0.8, 6.0.0)
> {code}
> The same holds for {{foo(cs instanceof String, cs.length())}}, {{[cs 
> instanceof String, cs]}} and {{(cs instanceof String) == cs.isEmpty()}}. A 
> comparison with {{false}} narrows the wrong branch, and the condition of a 
> loop narrows after the loop:
> {code:groovy}
> @groovy.transform.CompileStatic
> int h(Object x) { if ((x instanceof String) == false) { return x.length() }; 
> 0 }
> h(42) // ClassCastException: Integer cannot be cast to String
> @groovy.transform.CompileStatic
> int g(Object x) { while (x instanceof String) { break }; x.length() }
> g(42) // ClassCastException
> {code}
> The last two fail in 4.0.33, 5.0.8 and 6.0.0, the first in 5.0.8 and 6.0.0.
> Expected: a check narrows only where its outcome guards what follows (the 
> condition of an if, while, for, assert or conditional expression, and 
> operands of &&, || and ! there); {{(x instanceof T) == true}} narrows like 
> the check and {{== false}} like its negation; a loop condition narrows inside 
> the loop only.



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

Reply via email to