RFR: JDK-8222795: post inc operator inside compute function of HashMap results in Exception

Jan Lahoda jan.lahoda at oracle.com
Tue May 14 14:19:43 UTC 2019


Hi Vicente,

This looks very similar to:
JDK-8222169
https://bugs.openjdk.java.net/browse/JDK-8222169

I think the main issue here is that the stack size gets recorded in 
visitLetExpr, but the recorded stack size is wrong: there is a jump 
pending (the jump when "v > 5" is false, that jumps over the "v--" into 
"v++"), and when the jump is eventually resolved (in Code.addLocalVar in 
this case, probably) these will change (reduce) the stack size, and the 
asserts will fail.

My plan for JDK-8222169 was to do "code.resolvePending();" at the 
beginning of Gen.visitLetExpr, which should ensure the recorded stack 
size is correct.

I apologize for introducing this problem.

Thanks,
     Jan

On 14. 05. 19 0:07, Vicente Romero wrote:
> ping,
> 
> Thanks,
> Vicente
> 
> On 5/8/19 6:25 PM, Vicente Romero wrote:
>> Please review fix for regression [1] at [2]. The regression was 
>> introduced by [3] which introduced the support for switch expressions. 
>> The compiler is failing with an assertion error for code:
>>
>> import java.util.HashMap;
>> import java.util.Map;
>>
>> public class LetExpressionAssertionTest {
>>     void m() {
>>         Map<String, Integer> m = new HashMap<>();
>>         m.put("a",2);
>>         m.compute("a", (k, v) -> (v > 5) ? v-- : v++);
>>     }
>> }
>>
>> Where `v--` and `v++` are converted to let expressions. Recall that in 
>> this case `v` is of type integer so the let expressions are similar to:
>>
>> `(let /*synthetic*/ final Integer $373182087 = v in (let v = 
>> Integer.valueOf((int)(v.intValue() - 1)); in $373182087))`
>>
>> notice the cast, the ::intValue invocation etc.
>>
>> This patch, [3], also made some changes related to let expressions. In 
>> particular it changed a common idiom in Gen:
>>
>> `Assert.check(code.state.stacksize == 0);` by:
>> Assert.check(code.isStatementStart());
>>
>> where Code::isStatementStart is:
>> public boolean isStatementStart() {
>>      return !alive ||state.stacksize ==letExprStackPos;
>> }
>> which is basically the same check as before, modulo the `alive` check. 
>> The problem is that at Gen::visitVarDef we had a more relaxed 
>> assertion check before [3]:
>>
>> Assert.check(letExprDepth != 0 || code.state.stacksize == 0); which 
>> was also substituted by:
>> Assert.check(code.isStatementStart());
>>
>> this implies a semantic change. In the old code `letExprDepth` was 
>> just a counter of "nestedness" of let expressions and the original 
>> condition in Gen::visitVarDef was mostly to check if the compiler was 
>> generating a let expression or not. Enforcing that 
>> code.state.stacksize has to be equal to the stack position of the let 
>> expression when we started generating the let expression, which is 
>> what the current assertion is checking is a too strong condition. 
>> Which can be false if, as in this case, the let expression has several 
>> method invocations inside, etc that will move the stack as get 
>> generated. This patch is proposing going back to a more relaxed 
>> assertion at Gen::visitVarDef,
>>
>> Thanks,
>> Vicente
>>
>> [1] https://bugs.openjdk.java.net/browse/JDK-8222795
>> [2] http://cr.openjdk.java.net/~vromero/8222795/webrev.00/
>> [3] https://bugs.openjdk.java.net/browse/JDK-8206986
> 


More information about the compiler-dev mailing list