/*
* 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.Collections;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import polyglot.ast.Binary;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Formal;
import polyglot.ast.Loop;
import polyglot.ast.Loop_c;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.types.ClassDef;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.LocalDef;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.types.UnknownType;
import polyglot.util.ErrorInfo;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeCheckPreparer;
import polyglot.visit.TypeChecker;
import x10.constraint.XFailure;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.types.X10ClassType;
import polyglot.types.Context;
import x10.types.ConstrainedType;
import x10.types.X10FieldInstance;
import x10.types.X10LocalDef;
import x10.types.MethodInstance;
import x10.types.X10TypeEnv;
import x10.types.X10TypeEnv_c;
import x10.types.X10ParsedClassType_c;
import polyglot.types.TypeSystem;
import polyglot.types.LazyRef_c;
import x10.types.checker.Converter;
import x10.types.constraints.CConstraint;
import x10.types.matcher.Subst;
import x10.util.Synthesizer;
import x10.visit.X10TypeChecker;
/**
* Captures the commonality of for, foreach and ateach loops in X10.
* TODO:
* (1) formal must be a variable whose type will be that of the region underlying domain.
* (2) domain must be an array, distribution or region. If it is an array or distribution a,
* the system must behave as if the user typed a.region.
* (3) Perhaps we can allow continue statements within a for loop, but not within
* a foreach or an ateach.
*
* COMMENTS / TODO (added by Christian)
* (1) for now, continue/break should work as usual
* for foreach;
* (2) ateach is broken in many respects, including break/continue,
* see comments in ateach.xcd
* (3) this AST node does not seem to support the 'correct' syntax for
* multi-dimensional arrays (ateach(i,j:D) { S }). But that's
* probably ok, for now the XCD files expect to see
* ateach(i:D) { S } and type 'i' as 'int[]' for multi-dimensional
* arrays, and as 'int' for single-dimensional arrays.
*
* @author vj Dec 9, 2004
*/
public abstract class X10Loop_c extends Loop_c implements X10Loop {
protected Formal formal;
protected Expr domain;
protected Stmt body;
protected List<Stmt> locals;
protected LoopKind loopKind;
/**
* @param pos
*/
protected X10Loop_c(Position pos) {
super(pos);
}
protected X10Loop_c(Position pos, Formal formal, Expr domain, Stmt body) {
super(pos);
this.formal = formal;
this.domain = domain;
this.body = body;
}
/** Reconstruct the expression. */
protected X10Loop_c reconstruct(Formal formal, Expr domain, Stmt body) {
if (formal != this.formal || domain != this.domain || body != this.body) {
X10Loop_c n = (X10Loop_c) copy();
n.formal = formal;
n.domain = domain;
n.body = body;
return n;
}
return this;
}
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
TypeChecker tc1 = (TypeChecker) tc.enter(parent, this);
Expr domain = (Expr) this.visitChild(this.domain, tc1);
domainTypeRef.update( domain.type() );
Formal f = formal;
if (formal.type() instanceof UnknownTypeNode) {
resolveIndexType(tc);
final NodeFactory nf = tc.nodeFactory();
final TypeNode tn = this.formal.type();
f = f.type(nf.CanonicalTypeNode(tn.position(), tn.typeRef()));
}
f = (Formal) this.visitChild(f, tc1);
return tc.visitEdgeNoOverride(parent, this.domain(domain).formal(f));
}
private Type getIndexType(Type domainType, ContextVisitor tc) {
final TypeSystem ts = tc.typeSystem();
Type base = Types.baseType(domainType);
if (ts.isUnknown(base)) {
return ts.unknownType(base.position());
}
if (base instanceof X10ClassType) {
if (((X10ClassType) base).error() != null) {
return ts.unknownType(base.position());
}
if (ts.hasSameClassDef(base, ts.Iterable())) {
return Types.getParameterType(base, 0);
}
if (ts.hasSameClassDef(base, ts.JLIterable())) {
return ts.Any();
}
else {
Type sup = ts.superClass(domainType);
if (sup != null) {
Type t = getIndexType(sup, tc);
if (t != null) return t;
}
for (Type ti : ts.interfaces(domainType)) {
Type t = getIndexType(ti, tc);
if (t != null) {
return t;
}
}
}
}
return null;
}
private void resolveIndexType(ContextVisitor tc) {
final LazyRef<Type> r = (LazyRef<Type>) formal.type().typeRef();
final NodeFactory nf = tc.nodeFactory();
final TypeSystem ts = tc.typeSystem();
Type indexType;
int length = ((X10Formal) formal).vars().size();
if (length > 0) {
indexType = ts.Point(); // todo: not true, it can also be an Array, e.g., for (x[i,j] in [[1,2],[3,4]])
// Add a self.rank=n clause, if the formal
// has n components.
XVar self = Types.xclause(indexType).self();
Synthesizer synth = new Synthesizer(nf, ts);
XTerm v = synth.makePointRankTerm((XVar) self);
XTerm rank = ConstraintManager.getConstraintSystem().makeLit(length, ts.Int());
indexType = Types.addBinding(indexType, v, rank);
r.update(indexType);
return;
}
Type domainType = domainTypeRef.get();
indexType = getIndexType(domainType, tc);
if (indexType == null) {
r.update(ts.unknownType(position()));
return;
}
Type base = Types.baseType(domainType);
XVar selfValue = Types.selfVarBinding(domainType);
boolean generated = false;
if (selfValue == null) {
selfValue = ConstraintManager.getConstraintSystem().makeUQV();
generated = true;
}
XVar thisVar = base instanceof X10ClassType ?
((X10ClassType) base).x10Def().thisVar() :null;
// Now the problem is that indexType may
// have a non-null thisVar (e.g. Foo#this). We need to
// replace thisVar with var.
if (thisVar != null) {
try {
indexType = Subst.subst(indexType, selfValue, thisVar);
if (generated) {
CConstraint c = Types.xclause(domainType);
c=c.instantiateSelf(selfValue);
indexType = Types.addConstraint(indexType, c);
assert Types.consistent(indexType);
indexType = Subst.subst(indexType, ConstraintManager.getConstraintSystem().makeEQV(), selfValue);
}
} catch (SemanticException z) {
}
}
r.update(indexType);
}
/** Type check the statement. */
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = tc.typeSystem();
Type domainType = domainTypeRef.get();
if (domainType == null ) {
// aha, in this case the type inferencer did not run, since an explicit type was givem.
domainType = domain.type();
}
ConstrainedType formalType = Types.toConstrainedType(formal.declType());
assert domainType != null
: "formal=" + formal + " domain = " + domain + " position = " + position();
final Context context = tc.context();
HashSet<Type> iterableIndex = Types.getIterableIndex(domainType, context);
boolean newRes = false;
for (Type tt : iterableIndex)
newRes |= ts.isSubtype(tt, formalType, context);
//assert newRes==ts.isSubtype(domainType, ts.Iterable(formalType), tc.context()); // when Iterable was covariant (i.e., Iterable[+T])
if (newRes) {
// if (X10TypeMixin.areConsistent(formalType, domainType)
return this;
}
// // Check if there is a method with the appropriate name and type with the left
// operand as receiver.
// X10MethodInstance mi = ts.findMethod(domainType, ts.MethodMatcher(domainType, Name.make("iterator"), Collections.EMPTY_LIST), tc.context().currentClassDef());
// Type rt = mi.returnType();
// if (! mi.flags().isStatic() && ts.isSubtype(rt, Iterator))
// return this;
if (ts.isSubtype(formalType, ts.Point(), context)) {
ConstrainedType Region = Types.toConstrainedType(ts.Region());
final XTerm rankTerm = formalType.rank(context);
if (rankTerm!=null) {
Region = Region.addRank(rankTerm);
Expr newDomain = Converter.attemptCoercion(tc, domain, Region);
if (newDomain != null && newDomain != domain) {
domainTypeRef = Types.lazyRef(null);
Node nn = this.domain(newDomain).del().typeCheck(tc);
return nn;
}
ConstrainedType Dist = Types.toConstrainedType(ts.Dist());
Dist = Dist.addRank(rankTerm);
newDomain = Converter.attemptCoercion(tc, domain, Dist);
if (newDomain != null && newDomain != domain) {
domainTypeRef = Types.lazyRef(null);
return this.domain(newDomain).del().typeCheck(tc);
}
}
Errors.issue(tc.job(), new SemanticException("The loop iterator is a Point whose rank is not the same as the rank of the loop domain.", position()));
} else {
// The expected type is Iterable[Foo]. The constraints on domainType do matter
// for this failure, so don't strip them.
Errors.issue(tc.job(),
new Errors.LoopDomainIsNotOfExpectedType(formalType, domainType, iterableIndex, position()));
}
return this;
}
/* (non-Javadoc)
* @see polyglot.ast.Term#entry()
*/
public Term firstChild() {
return formal;
}
/* (non-Javadoc)
* @see polyglot.ast.Term#acceptCFG(polyglot.visit.CFGBuilder, java.util.List)
*/
@Override
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
v.visitCFG(formal, domain, ENTRY);
v.visitCFG(domain, body, ENTRY);
v.visitCFG(body, this, EXIT);
return succs;
}
@Override
public Context enterScope(Context c) {
return c.pushBlock();
}
/** Visit the children of the expression. */
@Override
public Node visitChildren(NodeVisitor v) {
Formal formal = (Formal) visitChild(this.formal, v);
Expr domain = (Expr) visitChild(this.domain, v);
Stmt body = (Stmt) visitChild(this.body, v);
return reconstruct(formal, domain, body);
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#body()
*/
public Stmt body() {
return this.body;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#formal()
*/
public Formal formal() {
return this.formal;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#domain()
*/
public Expr domain() {
return this.domain;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#locals()
*/
public List<Stmt> locals() {
return this.locals == null ? Collections.<Stmt>emptyList() : this.locals;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#body(polyglot.ast.Stmt)
*/
public X10Loop body(Stmt body) {
X10Loop_c n = (X10Loop_c) copy();
n.body = body;
return n;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#formal(polyglot.ast.Formal)
*/
public X10Loop formal(Formal formal) {
X10Loop_c n = (X10Loop_c) copy();
n.formal = formal;
return n;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#domain(polyglot.ast.Expr)
*/
public X10Loop domain(Expr domain) {
X10Loop_c n = (X10Loop_c) copy();
n.domain = domain;
return n;
}
/* (non-Javadoc)
* @see x10.ast.X10Loop#locals(java.util.List)
*/
public X10Loop locals(List<Stmt> locals) {
X10Loop_c n = (X10Loop_c) copy();
n.locals = locals;
return n;
}
public Term continueTarget() {
return formal;
}
public Expr cond() { return null; }
/*
* Type inference works as follows.
*
* We first seek to infer the type of the index expression i in the for loop for (i in D) S
* (if this type is not specified explicitly).
*
* There are two source of information about index type. First, the index expression itself.
* If it is of the form (k1,..., kn) we should infer that the type is Point(n).
*
* Second, we look to find if the type DT of the domain expression D is of the form Iterable[T].
* In this case, the inferred type of i is T.
*
* The subtlety here is that T may have a reference to Foo@this; this needs to be replaced with
* a logical variable corresponding to D. However, D may not have a self var binding. So we have
* to create such a variable.
*
* Examples that should work:
*
def g(g:Dist(1)): Dist(1) = g;
def m(gridDist:Dist(1)) {
for ((i) in g(gridDist) ) ;
for ((i) in 0..9) ;
for ((i) in gridDist | here);
//for ((i,j) in 0..9) ;
}
*
*/
LazyRef<Type> domainTypeRef = Types.lazyRef(null);
public Type domainType() {
Type domainType = domainTypeRef.get();
if (domainType == null ) {
// aha, in this case the type inferencer did not run, since an explicit type was givem.
domainType = domain.type();
}
return domainType;
}
@Override
public Node setResolverOverride(final Node parent, final TypeCheckPreparer v) {
final Expr domain = this.domain;
final X10Loop loop = this;
final Formal f = this.formal;
if (f.type() instanceof UnknownTypeNode) {
UnknownTypeNode tn = (UnknownTypeNode) f.type();
NodeVisitor childv = v.enter(parent, loop);
childv = childv.enter(loop, domain);
if (childv instanceof TypeCheckPreparer) {
// Create a ref and goal for computing the domain type.
final LazyRef<Type> domainTypeRef = this.domainTypeRef;
domainTypeRef.setResolver(LazyRef_c.THROW_RESOLVER);
final LazyRef<Type> r = (LazyRef<Type>) tn.typeRef();
// Now, infer index Type.
r.setResolver(LazyRef_c.THROW_RESOLVER);
}
}
return super.setResolverOverride(parent, v);
}
public Node buildTypes(TypeBuilder tb) {
X10Loop n = (X10Loop) super.buildTypes(tb);
// Set the final flag on all formals introduced in the loop.
Formal f = (Formal) n.formal().visit(new NodeVisitor() {
public Node leave(Node old, Node n, NodeVisitor v) {
if (n instanceof Formal) {
Formal f = (Formal) n;
LocalDef li = f.localDef();
Flags flags = f.flags().flags();
flags = flags.Final();
li.setFlags(flags);
return f.flags(f.flags().flags(flags)).localDef(li);
}
return n;
}
});
return n.formal(f);
}
}