/*
* 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.List;
import polyglot.ast.Expr;
import polyglot.ast.Formal;
import polyglot.ast.Node;
import polyglot.ast.Stmt;
import polyglot.ast.Stmt_c;
import polyglot.ast.Term;
import polyglot.types.ClassType;
import polyglot.types.CodeDef;
import polyglot.types.CodeInstance;
import polyglot.types.Context;
import polyglot.types.Def;
import polyglot.types.FieldDef;
import polyglot.types.Flags;
import polyglot.types.LocalDef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.FlowGraph;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.PruningVisitor;
import polyglot.visit.TypeBuilder;
import x10.constraint.XConstraint;
import x10.constraint.XFailure;
import x10.constraint.XTerm;
import x10.errors.Errors;
import x10.extension.X10Del;
import x10.extension.X10Del_c;
import x10.extension.X10Ext;
import x10.types.AtDef;
import x10.types.ClosureDef;
import x10.types.ParameterType;
import x10.types.ThisDef;
import x10.types.X10ClassDef;
import x10.types.X10MemberDef;
import x10.types.X10MethodDef;
import x10.types.X10ProcedureDef;
import x10.types.checker.Converter;
import x10.types.checker.PlaceChecker;
import x10.types.constraints.CConstraint;
import x10.types.constraints.ConstraintManager;
import x10.types.constraints.XConstrainedTerm;
/**
* Created on Oct 5, 2004
*
* @author Christian Grothoff
* @author Philippe Charles
* @author vj
* @author nystrom
*/
public class AtStmt_c extends Stmt_c implements AtStmt {
protected Expr place;
protected Stmt body;
protected AtDef atDef;
protected List<Node> captures;
public AtStmt_c(Position pos, Expr place, Stmt body) {
this(pos, place, null, body);
}
public AtStmt_c(Position pos, Expr place, List<Node> captures, Stmt body) {
super(pos);
assert body!=null;
this.place = place;
this.body = body;
this.captures = captures;
}
public List<Node> captures() {
return captures;
}
public AtStmt_c captures(List<Node> captures) {
AtStmt_c n = (AtStmt_c) copy();
n.captures = captures;
return n;
}
/**
* Get the body of the statement.
*/
public Stmt body() {
return body;
}
/**
* Set the body of the statement.
*/
public AtStmt body(Stmt body) {
AtStmt_c n = (AtStmt_c) copy();
n.body = body;
return n;
}
/** Get the RemoteActivity's place. */
public Expr place() {
return place;
}
/** Set the RemoteActivity's place. */
public AtStmt place(Expr place) {
if (place != this.place) {
AtStmt_c n = (AtStmt_c) copy();
n.place = place;
return n;
}
return this;
}
public AtDef atDef() {
return this.atDef;
}
public AtStmt atDef(AtDef ci) {
if (ci == this.atDef) return this;
AtStmt_c n = (AtStmt_c) copy();
n.atDef = ci;
return n;
}
/** Reconstruct the statement. */
protected AtStmt_c reconstruct(Expr place, Stmt body) {
if (place != this.place || body != this.body) {
AtStmt_c n = (AtStmt_c) copy();
n.place = place;
n.body = body;
return n;
}
return this;
}
public boolean isFinishPlace() {
boolean isFinishPlace = false;
AtDef def = atDef();
if (null != def.finishPlaceTerm()) {
XConstraint constraint = ConstraintManager.getConstraintSystem().makeConstraint();;
constraint.addBinding(def.finishPlaceTerm().term(),def.placeTerm().term());
if (def.placeTerm().constraint().entails(constraint)) {
isFinishPlace = true;
}
}
return isFinishPlace;
}
@Override
public Node buildTypesOverride(TypeBuilder tb) {
TypeSystem ts = (TypeSystem) tb.typeSystem();
X10ClassDef ct = (X10ClassDef) tb.currentClass();
assert ct != null;
Def def = tb.def();
if (def instanceof FieldDef) {
// FIXME: is this possible?
FieldDef fd = (FieldDef) def;
def = fd.initializer();
}
if (!(def instanceof CodeDef)) {
Errors.issue(tb.job(),
new Errors.CannotOccurOutsideCodeBody(Errors.CannotOccurOutsideCodeBody.Element.At, position()));
// Fake it
def = ts.initializerDef(position(), Types.ref(ct.asType()), Flags.STATIC);
}
CodeDef code = (CodeDef) def;
AtDef mi = (AtDef) createDummyAsync(position(), ts, ct.asType(), code, code.staticContext(), false);
AtStmt_c n = (AtStmt_c) X10Del_c.visitAnnotations(this, tb);
List<AnnotationNode> as = ((X10Del) n.del()).annotations();
if (as != null) {
List<Ref<? extends Type>> ats = new ArrayList<Ref<? extends Type>>(as.size());
for (AnnotationNode an : as) {
ats.add(an.annotationType().typeRef());
}
mi.setDefAnnotations(ats);
}
// Unlike methods and constructors, do not create new goals for resolving the signature and body separately;
// since closures don't have names, we'll never have to resolve the signature. Just push the code context.
TypeBuilder tb2 = tb.pushCode(mi);
n = (AtStmt_c) n.del().visitChildren(tb2);
if (code instanceof X10MemberDef) {
assert mi.thisDef() == ((X10MemberDef) code).thisDef();
}
return n.atDef(mi);
}
@Override
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeVisitor v = tc.enter(parent, this);
if (v instanceof PruningVisitor) {
return this;
}
ContextVisitor childtc = (ContextVisitor) v;
Expr place = (Expr) visitChild(this.place, childtc);
place = Converter.attemptCoercion(tc, place, ts.Place());
if (place == null) {
Errors.issue(tc.job(),
new Errors.AtArgMustBePlace(this.place, ts.Place(), this.place.position()));
place = tc.nodeFactory().Here(this.place.position()).type(ts.Place());
}
Context c = tc.context();
AtDef def = this.atDef();
if (def.placeTerm() == null) {
XConstrainedTerm placeTerm;
XConstrainedTerm finishPlaceTerm = c.currentFinishPlaceTerm();
CConstraint d = ConstraintManager.getConstraintSystem().makeCConstraint();
XTerm term = PlaceChecker.makePlace();
try {
placeTerm = XConstrainedTerm.instantiate(d, term);
} catch (XFailure z) {
throw new InternalCompilerError("Cannot construct placeTerm from term and constraint.");
}
try {
XConstrainedTerm realPlaceTerm = PlaceChecker.computePlaceTerm(place, c, ts);
d.addBinding(placeTerm, realPlaceTerm);
} catch (SemanticException e) { }
def.setPlaceTerm(placeTerm);
def.setFinishPlaceTerm(finishPlaceTerm);
}
// now that placeTerm is computed for this node, install it in the context
// and continue visiting children
Context oldC = c;
c = super.enterChildScope(body, childtc.context());
XConstrainedTerm pt = def.placeTerm();
if (pt != null) {
if (c == oldC)
c = c.pushBlock();
c.setPlace(pt);
}
Stmt body = (Stmt) visitChild(this.body, childtc.context(c));
AtStmt_c n = this.reconstruct(place, body);
List<AnnotationNode> oldAnnotations = ((X10Ext)n.ext()).annotations();
if (oldAnnotations != null && !oldAnnotations.isEmpty()) {
List<AnnotationNode> newAnnotations = visitList(oldAnnotations, v);
if (! CollectionUtil.allEqual(oldAnnotations, newAnnotations)) {
n = (AtStmt_c) ((X10Del) n.del()).annotations(newAnnotations);
}
}
return tc.leave(parent, this, n, childtc);
}
@Override
public Node typeCheck(ContextVisitor tc) {
Context c = tc.context();
AtDef def = this.atDef();
//if (!def.capturedEnvironment().isEmpty()) {
// System.out.println(this.position() + ": " + this + " captures "+def.capturedEnvironment());
//}
Closure_c.propagateCapturedEnvironment(c, def);
return this;
}
@Override
public Context enterChildScope(Node child, Context c) {
if (child != this.body) return c.pop();
Context oldC = c;
c = super.enterChildScope(child, c);
if (c == oldC)
c = c.pushBlock();
c.setPlace(atDef.placeTerm());
return c;
}
/** Visit the children of the statement. */
public Node visitChildren(NodeVisitor v) {
Expr place = (Expr) visitChild(this.place, v);
Stmt body = (Stmt) visitChild(this.body, v);
return reconstruct(place, body);
}
public static X10MethodDef createDummyAsync(Position pos, TypeSystem ts, ClassType cc, CodeDef cd, boolean isStaticContext, boolean isAsync) {
ThisDef thisDef = null;
List<ParameterType> capturedTypes = Collections.<ParameterType>emptyList();
CodeInstance<?> ci = cd.asInstance();
if (cd instanceof X10ProcedureDef) {
X10ProcedureDef outer = (X10ProcedureDef) cd;
thisDef = outer.thisDef();
capturedTypes = outer.typeParameters();
}
X10MethodDef asyncInstance =
isAsync ?
ts.asyncCodeInstance(pos, thisDef, capturedTypes, Types.ref(ci), Types.ref(cc), isStaticContext)
:
ts.atCodeInstance(pos, thisDef, capturedTypes, Types.ref(ci), Types.ref(cc), isStaticContext);
return asyncInstance;
}
@Override
public Context enterScope(Context c) {
c = c.pushBlock();
c = c.pushAt(atDef);
c.x10Kind = Context.X10Kind.At;
return c;
}
public String toString() {
return "at (" + place + ") " + body;
}
/** Write the statement to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
w.write("at (");
printBlock(place, w, tr);
w.write(") ");
printSubStmt(body, w, tr);
}
/**
* Return the first (sub)term performed when evaluating this
* term.
*/
public Term firstChild() {
return place;
}
/**
* Visit this term in evaluation order.
*/
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
v.visitCFG(place, body, ENTRY);
v.visitCFG(body, this, EXIT);
return succs;
}
}