/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.ast; import java.util.ArrayList; import java.util.List; import polyglot.frontend.Globals; import polyglot.types.*; import polyglot.util.CodeWriter; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.visit.*; import x10.errors.Errors; import x10.types.constants.ConstantValue; /** * A <code>ArrayAccessAssign_c</code> represents a Java assignment expression * to an array element. For instance, <code>A[3] = e</code>. * * The class of the <code>Expr</code> returned by * <code>ArrayAccessAssign_c.left()</code>is guaranteed to be an * <code>ArrayAccess</code>. */ public class ArrayAccessAssign_c extends Assign_c implements ArrayAccessAssign { Expr array; Expr index; public ArrayAccessAssign_c(NodeFactory nf, Position pos, Expr array, Expr index, Operator op, Expr right) { super(nf, pos, op, right); this.array = array; this.index = index; } public Expr array() { return array; } public Expr index() { return index; } public ArrayAccessAssign array(Expr array) { ArrayAccessAssign_c n = (ArrayAccessAssign_c) copy(); n.array = array; return n; } public ArrayAccessAssign index(Expr index) { ArrayAccessAssign_c n = (ArrayAccessAssign_c) copy(); n.index = index; return n; } @Override public Assign typeCheckLeft(ContextVisitor tc) { Type at = array.type(); if (!at.isArray()) Errors.issue(tc.job(), new SemanticException("Target of array assignment is not an array element.", array.position()), this); Type it = index.type(); if (!it.isInt()) Errors.issue(tc.job(), new SemanticException("Array element must be indexed by an int.", index.position()), this); return this; } /** Type check the expression. */ public Node typeCheck(ContextVisitor tc) { ArrayAccessAssign_c n = (ArrayAccessAssign_c) typeCheckLeft(tc); TypeSystem ts = tc.typeSystem(); Type t = n.leftType(); if (t == null) t = ts.unknownType(position()); Expr right = n.right(); Assign.Operator op = n.operator(); Type s = right.type(); Context context = tc.context(); if (op == ASSIGN) { if (! ts.isImplicitCastValid(s, t, context) && ! ts.typeEquals(s, t, context) && ! ts.numericConversionValid(t, ConstantValue.toJavaObject(right.constantValue()), context)) { Errors.issue(tc.job(), new SemanticException("Cannot assign " + s + " to " + t + ".", position())); } return n.type(t); } if (op == ADD_ASSIGN) { // t += s if (ts.typeEquals(t, ts.String(), context) && ts.canCoerceToString(s, context)) { return n.type(ts.String()); } if (t.isNumeric() && s.isNumeric()) { Type r; try { r = ts.promote(t, s); } catch (SemanticException e) { r = t; } return n.type(r); } Errors.issue(tc.job(), new SemanticException("Operator must have numeric or String operands.", position())); return n.type(t); } if (op == SUB_ASSIGN || op == MUL_ASSIGN || op == DIV_ASSIGN || op == MOD_ASSIGN) { if (t.isNumeric() && s.isNumeric()) { Type r; try { r = ts.promote(t, s); } catch (SemanticException e) { r = t; } return n.type(r); } Errors.issue(tc.job(), new SemanticException("Operator must have numeric operands.", position())); return n.type(t); } if (op == BIT_AND_ASSIGN || op == BIT_OR_ASSIGN || op == BIT_XOR_ASSIGN) { if (t.isBoolean() && s.isBoolean()) { return n.type(ts.Boolean()); } if (ts.isImplicitCastValid(t, ts.Long(), context) && ts.isImplicitCastValid(s, ts.Long(), context)) { Type r; try { r = ts.promote(t, s); } catch (SemanticException e) { r = t; } return n.type(r); } Errors.issue(tc.job(), new SemanticException("Operator must have integral or boolean operands.", position())); return n.type(t); } if (op == SHL_ASSIGN || op == SHR_ASSIGN || op == USHR_ASSIGN) { if (ts.isImplicitCastValid(t, ts.Long(), context) && ts.isImplicitCastValid(s, ts.Long(), context)) { // Only promote the left of a shift. Type r; try { r = ts.promote(t); } catch (SemanticException e) { r = t; } return n.type(r); } Errors.issue(tc.job(), new SemanticException("Operator must have integral operands.", position())); return n.type(t); } throw new InternalCompilerError("Unrecognized assignment operator " + op + "."); } public Type leftType() { Type at = array.type(); if (at.isArray()) return at.toArray().base(); return null; } public Expr left() { ArrayAccess aa = nf.ArrayAccess(position(), array, index); if (type != null) return aa.type(type); else return aa; } @Override public Assign visitLeft(NodeVisitor v) { Expr array = (Expr) visitChild(this.array, v); Expr index = (Expr) visitChild(this.index, v); return reconstruct(array, index); } protected Assign reconstruct(Expr array, Expr index) { if (array != this.array || index != this.index) { ArrayAccessAssign_c n = (ArrayAccessAssign_c) copy(); n.array = array; n.index = index; return n; } return this; } public Term firstChild() { return array; } protected void acceptCFGAssign(CFGBuilder v) { // a[i] = e: visit a -> i -> e -> (a[i] = e) v.visitCFG(array, index, ENTRY); v.visitCFG(index, right(), ENTRY); v.visitCFG(right(), this, EXIT); } protected void acceptCFGOpAssign(CFGBuilder v) { v.visitCFG(array, index, ENTRY); v.visitCFG(index, right(), ENTRY); v.visitCFG(right(), this, EXIT); } public List<Type> throwTypes(TypeSystem ts) { List<Type> l = new ArrayList<Type>(super.throwTypes(ts)); if (throwsArrayStoreException()) { l.add(ts.ArrayStoreException()); } l.add(ts.NullPointerException()); l.add(ts.OutOfBoundsException()); return l; } /** Get the throwsArrayStoreException of the expression. */ public boolean throwsArrayStoreException() { return op == ASSIGN && array.type().isReference(); } public String toString() { return array + "(" + index + ") " + op + " " + right; } public void prettyPrint(CodeWriter w, PrettyPrinter tr) { printSubExpr(array, w, tr); w.write ("["); printBlock(index, w, tr); w.write ("]"); w.write(" "); w.write(op.toString()); w.allowBreak(2, 2, " ", 1); // miser mode w.begin(0); printSubExpr(right, false, w, tr); w.end(); } }