/*
* 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
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import polyglot.ast.Assign;
import polyglot.ast.Assign_c;
import polyglot.ast.Binary;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Precedence;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.ast.Assign.Operator;
import polyglot.frontend.Globals;
import polyglot.types.ClassDef;
import polyglot.types.Context;
import polyglot.types.ErrorRef_c;
import polyglot.types.Flags;
import polyglot.types.Matcher;
import polyglot.types.MethodDef;
import polyglot.types.SemanticException;
import polyglot.types.Name;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.types.UnknownType;
import polyglot.types.TypeSystem_c.MethodMatcher;
import polyglot.util.CodeWriter;
import polyglot.util.InternalCompilerError;
import polyglot.util.Pair;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.Translator;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeChecker;
import x10.errors.Errors;
import x10.errors.Warnings;
import x10.types.X10ClassDef;
import x10.types.MethodInstance;
import polyglot.types.TypeSystem;
import x10.types.checker.Checker;
import x10.types.checker.Converter;
import x10.visit.X10TypeChecker;
/** An immutable representation of an X10 array access update: a[point] op= expr;
* TODO
* Typechecking rules:
* (1) point must be of a type (region) that can be cast to the array index type.
* (2) expr must be of a type that can be implicitly cast to the base type of the array.
* (3) The operator, if any, must be permitted on the underlying type.
* (4) No assignment is allowed on a value array.
* @author vj Dec 9, 2004
*
*/
public class SettableAssign_c extends Assign_c implements SettableAssign {
protected Expr array;
protected List<Expr> index;
/**
* @param pos
* @param left
* @param op
* @param right
*/
public SettableAssign_c(NodeFactory nf, Position pos, Expr array, List<Expr> index, Operator op, Expr right) {
super(nf, pos, op, right);
this.array = array;
this.index = index;
}
public Type leftType() {
if (mi == null) return null;
return mi.formalTypes().get(mi.formalTypes().size()-1);
}
public Expr left() {
return left(nf, null);
}
//@Override
public Expr left(NodeFactory nf, ContextVisitor cv) {
Call c = nf.Call(position(), array, nf.Id(position(), ClosureCall.APPLY), index);
if (ami != null) {
c = c.methodInstance(ami);
}
if (type != null && ami != null) c = (Call) c.type(ami.returnType());
return c;
}
/** Get the precedence of the expression. */
public Precedence precedence() {
return Precedence.LITERAL;
}
/** Get the array of the expression. */
public Expr array() {
return this.array;
}
/** Set the array of the expression. */
public SettableAssign array(Expr array) {
SettableAssign_c n = (SettableAssign_c) copy();
n.array = array;
return n;
}
/** Get the index of the expression. */
public List<Expr> index() {
return TypedList.copy(this.index, Expr.class, false);
}
/** Set the index of the expression. */
public SettableAssign index(List<Expr> index) {
SettableAssign_c n = (SettableAssign_c) copy();
n.index = TypedList.copyAndCheck(index, Expr.class, true);
return n;
}
/** Reconstruct the expression. */
protected SettableAssign_c reconstruct( Expr array, List<Expr> index ) {
if (array != this.array || index != this.index) {
SettableAssign_c n = (SettableAssign_c) copy();
n.array = array;
n.index = TypedList.copyAndCheck(index, Expr.class, true);
return n;
}
return this;
}
/** Return the access flags of the variable. */
public Flags flags() {
return Flags.NONE;
}
/** Visit the children of the expression. */
public Assign visitLeft(NodeVisitor v) {
Expr array = (Expr) visitChild(this.array, v);
List<Expr> index = visitList(this.index, v);
return reconstruct(array, index);
}
MethodInstance mi;
MethodInstance ami; // the apply method is searched even for SettableAssign if the operator is not "=", e.g., a(1) += 2; If it is just assignment, then ami will be null, e.g., a(1)=2;
public MethodInstance methodInstance() {
return mi;
}
public SettableAssign methodInstance(MethodInstance mi) {
SettableAssign_c n = (SettableAssign_c) copy();
n.mi = mi;
return n;
}
public MethodInstance applyMethodInstance() {
return ami;
}
public SettableAssign applyMethodInstance(MethodInstance ami) {
SettableAssign_c n = (SettableAssign_c) copy();
n.ami = ami;
return n;
}
@Override
public Node buildTypes(TypeBuilder tb) {
SettableAssign_c n = (SettableAssign_c) super.buildTypes(tb);
TypeSystem ts = (TypeSystem) tb.typeSystem();
MethodInstance mi = ts.createMethodInstance(position(), position(),new ErrorRef_c<MethodDef>(ts, position(), "Cannot get MethodDef before type-checking settable assign."));
MethodInstance ami = ts.createMethodInstance(position(), position(), new ErrorRef_c<MethodDef>(ts, position(), "Cannot get MethodDef before type-checking settable assign."));
return n.methodInstance(mi).applyMethodInstance(ami);
}
static Pair<MethodInstance,List<Expr>> tryImplicitConversions(X10Call_c n, ContextVisitor tc,
Type targetType, List<Type> typeArgs, List<Type> argTypes) throws SemanticException {
final TypeSystem ts = (TypeSystem) tc.typeSystem();
final Context context = tc.context();
List<MethodInstance> methods = ts.findAcceptableMethods(targetType,
ts.MethodMatcher(targetType, SettableAssign.SET, typeArgs, argTypes, context, true));
Pair<MethodInstance,List<Expr>> p = Converter.<MethodDef,MethodInstance>tryImplicitConversions(n, tc,
targetType, methods, new X10New_c.MatcherMaker<MethodInstance>() {
public Matcher<MethodInstance> matcher(Type ct, List<Type> typeArgs, List<Type> argTypes) {
return ts.MethodMatcher(ct, SettableAssign.SET, typeArgs, argTypes, context);
}
});
return p;
}
@Override
public Assign typeCheckLeft(ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeFactory nf = (NodeFactory) tc.nodeFactory();
TypeSystem xts = ts;
MethodInstance mi = null;
List<Type> typeArgs = Collections.<Type>emptyList();
List<Type> actualTypes = new ArrayList<Type>(index.size()+1);
for (Expr ei : index) {
actualTypes.add(ei.type());
}
List<Expr> args = new ArrayList<Expr>();
args.addAll(index);
MethodInstance ami = null;
Expr right = this.right;
Expr val = this.right;
// FIXME: try to find the method with implicit conversions if this fails.
ami = Checker.findAppropriateMethod(tc, array.type(), ClosureCall.APPLY, typeArgs, actualTypes);
if (op != Assign.ASSIGN) {
if (ami.error() != null) { // it's an error only if op is not =, e.g., a(1)+=1;
Type bt = Types.baseType(array.type());
boolean arrayP = xts.isX10Array(bt) || xts.isX10DistArray(bt);
Errors.issue(tc.job(), new Errors.CannotAssignToElement(leftToString(), arrayP, right, Types.arrayElementType(array.type()), position(), ami.error()));
}
X10Call_c left = (X10Call_c) nf.X10Call(position(), array, nf.Id(position(),
ClosureCall.APPLY), Collections.<TypeNode>emptyList(),
index).methodInstance(ami).type(ami.returnType());
X10Binary_c n = (X10Binary_c) nf.Binary(position(), left, op.binaryOperator(), right);
X10Call c = X10Binary_c.desugarBinaryOp(n, tc);
MethodInstance cmi = (MethodInstance) c.methodInstance();
if (cmi.error() != null) {
Type bt = Types.baseType(array.type());
boolean arrayP = xts.isX10Array(bt) || xts.isX10DistArray(bt);
Errors.issue(tc.job(),
new Errors.CannotPerformAssignmentOperation(leftToString(), arrayP, op.toString(), right, Types.arrayElementType(array.type()), position(), cmi.error()));
}
if (cmi.flags().isStatic()) {
right = c.arguments().get(1);
} else if (c.name().id().equals(X10Binary_c.invBinaryMethodName(n.operator()))) {
right = (Expr) c.target();
} else {
right = c.arguments().get(0);
}
val = c;
}
actualTypes.add(val.type());
args.add(val);
// First try to find the method without implicit conversions.
mi = Checker.findAppropriateMethod(tc, array.type(), SET, typeArgs, actualTypes);
if (mi.error() != null) {
// Now, try to find the method with implicit conversions, making them explicit.
try {
X10Call_c n = (X10Call_c) nf.X10Call(position(), array, nf.Id(position(), SET), Collections.<TypeNode>emptyList(), args);
Pair<MethodInstance,List<Expr>> p = tryImplicitConversions(n, tc, array.type(), typeArgs, actualTypes);
mi = p.fst();
args = p.snd();
}
catch (SemanticException e) {
if (mi.error() instanceof Errors.CannotGenerateCast) {
throw new InternalCompilerError("Unexpected cast error", mi.error());
}
Type bt = Types.baseType(array.type());
boolean arrayP = xts.isX10Array(bt) || xts.isX10DistArray(bt);
Errors.issue(tc.job(), new Errors.CannotAssignToElement(leftToString(), arrayP, right, Types.arrayElementType(array.type()), position(), mi.error()));
}
}
if (op == Assign.ASSIGN) {
right = args.get(args.size()-1);
}
if (mi.flags().isStatic() ) {
Errors.issue(tc.job(), new Errors.AssignSetMethodCantBeStatic(mi, array, position()));
}
SettableAssign_c a = this;
a = (SettableAssign_c) a.methodInstance(mi);
a = (SettableAssign_c) a.applyMethodInstance(ami);
a = (SettableAssign_c) a.right(right);
a = (SettableAssign_c) a.index(args.subList(0, args.size()-1));
return a;
}
@Override
public Node typeCheck(ContextVisitor tc) {
SettableAssign_c a = (SettableAssign_c) x10.types.checker.Checker.typeCheckAssign(this, tc);
return a.type(a.mi.returnType());
}
public Term firstChild() {
return array;
}
protected void acceptCFGAssign(CFGBuilder v) {
v.visitCFG(array, listChild(index, right()), ENTRY);
v.visitCFGList(index, right(), ENTRY);
v.visitCFG(right(), this, EXIT);
}
protected void acceptCFGOpAssign(CFGBuilder v) {
v.visitCFG(array, listChild(index, right()), ENTRY);
v.visitCFGList(index, right(), ENTRY);
v.visitCFG(right(), this, EXIT);
}
public List<Type> throwTypes(TypeSystem ts) {
List<Type> l = new ArrayList<Type>(super.throwTypes(ts));
l.add(ts.NullPointerException());
l.add(ts.OutOfBoundsException());
return l;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(array.toString());
sb.append("(");
String sep = "";
for (Expr e : index) {
sb.append(sep);
sep = ", ";
sb.append(e);
}
sb.append(") ");
sb.append(op);
sb.append(" ");
sb.append(right.toString());
return sb.toString();
}
/** Write the expression to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
Type at = array.type();
printSubExpr(array, w, tr);
w.write ("(");
w.begin(0);
for(Iterator<Expr> i = index.iterator(); i.hasNext();) {
Expr e = i.next();
print(e, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.write (")");
w.write(" ");
w.write(op.toString());
w.write(" ");
print(right, w, tr);
w.end();
}
public String leftToString() {
String arg = index.toString();
return array.toString() + "(" + arg.substring(1, arg.length()-1) + ")";
}
}