EVALUATION
The problem turned out worse than it seemed at first. The code in TransTypes that deals with the translation of unary/binary operator is quite messy and non-uniform. Here is some example of inconsistencies:
*) In a binary expression, both arguments are cast to the expected type (if needed) after erasure
*) In a compound assigment expression, only the right hand side is cast to the expeced type (if needed) after erasure
*) In an unary expression, the argument is cast to the expeced type (if needed) after erasure
Let's see some examples:
void foo(Foo<Integer> f) {
int i = f.x + 2; //erased to: ((Integer)f.x) + 2
int i2 = f.x += 1; //erased to: (f.x) + 2
int i3 = f.x++; //erased to: ((Integer)f.x)++
}
The interesting case seems to be the third one (pre/post fix increment operators) as in that case javac is generating a synthetic cast that is not generated for compound assignments; however, since the underlying code dealing with both expression is the same, it turns out that javac can potentially end up in a situation in which the LHS of a pre/postix inc operator is neither a CAST nor a SELECT/IDENT. Consider the following case:
int i = (f.x)++; //erased to: (((Integer)f.x))++
In this case the LHS is a parenthesized expression enclosing a cast expression. Since the cast is enclosed in parenthesis, it follows that the code in Lower.lowerBoxedPostop cannot handle it properly. In particular, that method fails to skip the cast expression - usually when the LHS is something of the kind (X)expr, method Lower.abstractLval is invoked only on 'expr'. This, in turn causes the assertion error described in this report, as the code in Lower.abstractLval is not able to cope with AST nodes other than SELECT, IDENT, INDEXED. In this case we have (after skipping parenthesis) a TYPECAST, which is not correct given the current implementation.
I think that the implementation of Lower.abstractLval should be made more general. in particular, it should handle type-cast AST nodes; we should avoid checking for type cast AST nodes *before* going inside abstractLval, as this might not always be possible, due to parenthesized expressions. Another important point is that, once we have a better implementation of Lower.abstractLval we can also make the code in TransTypes more uniform than it is now - e.g. we could insert synthetic casts on *all* kinds of operator arguments.
|