package x10.types.checker;
import java.util.List;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.Receiver;
import polyglot.types.ClassDef;
import polyglot.types.ConstructorDef;
import polyglot.types.Context;
import polyglot.types.FieldDef;
import polyglot.types.FieldInstance;
import polyglot.types.Flags;
import polyglot.types.InitializerDef;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import x10.Configuration;
import x10.ast.X10Call;
import x10.ast.X10Call_c;
import x10.ast.X10CanonicalTypeNode_c;
import x10.ast.X10Field_c;
import x10.constraint.XEQV;
import x10.constraint.XFailure;
import x10.constraint.XLit;
import x10.constraint.XLocal;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.errors.Errors;
import x10.errors.Errors.PlaceTypeErrorMethodShouldBeLocalOrGlobal;
import x10.types.FunctionType_c;
import polyglot.types.Context;
import x10.types.ClosureDef;
import x10.types.ConstrainedType;
import x10.types.X10FieldInstance;
import x10.types.MethodInstance;
import polyglot.types.TypeSystem;
import x10.types.XTypeTranslator;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CConstraint;
import x10.types.constraints.XConstrainedTerm;
import x10.types.matcher.Subst;
import x10.util.Synthesizer;
/**
* The core routines that implement the place type checker. These are called from various AST nodes.
* @author vj
*
*/
public class PlaceChecker {
static final XVar HERE = ConstraintManager.getConstraintSystem().makeUQV("synthetic here");
//public static final XLit GLOBAL_PLACE = new XLit_c("globalPlace");
public static XVar here() {
return HERE;
}
private static final String PLACE_HAME = "here"; // temporary XTENLANG-2674 hack
/**
*
* @return a newly constructed UQV representing a fixed but unknown place.
*/
public static XTerm makePlace() {
XTerm place = ConstraintManager.getConstraintSystem().makeUQV(PLACE_HAME);
return place;
}
public static boolean isGlobalPlace(XTerm term) {
return (term instanceof XEQV && term.toString().startsWith(PLACE_HAME));
}
static XTerm thisHomeVar(Context xc) {
return homeVar(((Context) xc).thisVar(), (TypeSystem) xc.typeSystem());
}
static FieldInstance GlobalRefHome(TypeSystem xts) {
return ((ContainerType) xts.GlobalRef()).fieldNamed(xts.homeName());
}
public static final String HOME_NAME = "$$here";
/**
* The key move in adapting the 2.0 place checking system to 2.1 is to continue
* to pretend that for each object (other than instances of GlobalRef --
* for that we have globalRefHomeVar(..)) we track the place where it was created in a fake
* field called "$$here". This field does not exist in the object, but it serves as a way
* to record the place at which an object was created so that fields that may be initialized
* with the indexical constant "here" can still be tracked through the fake field "this.$$here".
* For instance this lets us infer that the following is place safe
* class C {
* private val root = GlobalRef[C](this);
* // this records the type of root as GlobalRef[C]{self.home == C#this.here} .. note C#this.here is a fake field.
* }
* val x = (new C()).root;
* // this computes the type of new C() as C{this.here == _place269}, where _place269 is a constant standing for "here"
* // so it computes the type of (new C()).root as GlobalRef[C]{self.home == _place269}.
* x(); // now this is legal because _place269 is the current place, hence the guard x.home == here is satisfied.
*
* todo: Yoav notes that the implementation is broken, see XTENLANG-1905.
*
* Note that the user cannot specify any constraint in the X10 source program to refer to this fake field.
* This field is purely an internal contrivance of the type system to enable us to reuse the machinery
* we had developed for 2.0 to track where an object is created.
*
* @param target
* @param xts
* @return
*/
static XTerm homeVar(XTerm target, TypeSystem xts) {
return xts.xtypeTranslator().translateFakeField(target, HOME_NAME);
}
static XTerm globalRefHomeVar(XTerm target, TypeSystem xts) {
return xts.xtypeTranslator().translate(target, GlobalRefHome(xts));
}
public static XTerm placeTerm(Type t) {
TypeSystem xts = (TypeSystem) t.typeSystem();
CConstraint cc = Types.xclause(t);
return cc==null ? null : cc.bindingForSelfField(GlobalRefHome(xts));
}
public static CConstraint ThisHomeEqualsHere(XTerm thisVar, TypeSystem ts) {
XTerm h = PlaceChecker.homeVar(thisVar, ts);
CConstraint c = ConstraintManager.getConstraintSystem().makeCConstraint();
if (h != null) {
c.addBinding(h, here());
}
return c;
}
/**
* Caller must ensure that self.home==here constraint can be consistently added to type.
* @param type
* @param cxt
* @return
*/
public static Type AddIsHereClause(Type type, Context cxt) {
if (Types.isX10Struct(type))
return type;
ConstrainedType type1 = Types.toConstrainedType(type);
XVar selfVar = Types.selfVar(type1);
assert selfVar != null;
/*if (selfVar == null) {
selfVar = ConstraintManager.getConstraintSystem().makeEQV("self");
try {
type = X10TypeMixin.setSelfVar(type, selfVar);
} catch (SemanticException e) {
throw new InternalCompilerError("Cannot set self var for type "+type, e);
}
}*/
XTerm locVar = homeVar(selfVar, (TypeSystem) cxt.typeSystem());
XConstrainedTerm pt = (((Context) cxt).currentPlaceTerm());
if (locVar != null && pt != null)
type = Types.addBinding(type1, locVar, pt.term()); // here());// here, not pt); // pt, not PlaceChecker.here()
return type;
}
/**
* Replace occurrences of here in type by the context's place term.
* @param type
* @param xct
* @return
*/
public static Type ReplaceHereByPlaceTerm(Type type, Context xct) {
if (xct.currentPlaceTerm() == null)
assert true;
try {
if (xct.currentPlaceTerm() !=null)
type = Subst.subst(type, xct.currentPlaceTerm().term(), here());
} catch (SemanticException z) {
throw new InternalCompilerError("Unexpectedly inconsistent constraint.");
}
return type;
}
public static Type ReplaceHereByPlaceTerm(Type type, XConstrainedTerm pt) {
try {
type = Subst.subst(type, pt.term(), here());
} catch (SemanticException z) {
throw new InternalCompilerError("Unexpectedly inconsistent constraint.");
}
return type;
}
/**
* Replace occurrences of here in type with term.
* @param type
* @param term
* @return
*/
public static Type ReplaceHereByPlaceTerm(Type type, XTerm term) {
// Do not replace for closure types. The ! in such types is
// evaluated dynamically, i.e. at the point of evaluation of the closure.
if (type instanceof FunctionType_c)
return type;
//assert term != null;
try {
if (term != null)
type = Subst.subst(type, term, here());
} catch (SemanticException z) {
throw new InternalCompilerError("Unexpectedly inconsistent constraint.");
}
return type;
}
public static Type ReplacePlaceTermByHere(Type type, Context context) {
XConstrainedTerm h = ((Context) context).currentPlaceTerm();
if (h == null)
return type;
XTerm term = h.term();
try {
if (term instanceof XVar)
type = Subst.subst(type, here(), (XVar) term);
} catch (SemanticException z) {
throw new InternalCompilerError("Unexpectedly inconsistent constraint.");
}
return type;
}
public static void AddHereEqualsPlaceTerm(CConstraint c, Context xc) {
if (! c.consistent())
return;
XConstrainedTerm placeTerm = xc.currentPlaceTerm();
if (placeTerm != null)
c.addBinding(here(), placeTerm.term());
}
static XConstrainedTerm firstPlace = XConstrainedTerm.make(ConstraintManager.getConstraintSystem().makeUQV("FIRST_PLACE"));
public static XConstrainedTerm firstPlace(TypeSystem xts) {
return firstPlace;
}
public static void setHereTerm(MethodDef md, Context c) {
c = c.pushBlock();
if (isGlobalCode(md)) {
c.setPlace(XConstrainedTerm.make(makePlace()));
} else {
setHereIsThisHome(c);
}
}
public static void setHereTerm(FieldDef fd, Context c) {
Flags flags = fd.flags();
if (flags.isStatic()) {
c.setPlace(firstPlace(fd.typeSystem()));
return;
}
if (Types.isX10Struct(fd.container().get())) {
c.setPlace(XConstrainedTerm.make(makePlace()));
return;
}
TypeSystem xts = (TypeSystem) c.typeSystem();
Context xc = (Context) c;
ClassDef cd = c.currentClassDef();
// bypass GlobalRef to avoid infinite recursion (pushPlace will again look for GlobalRef.home..)
// This means that the types in GlobalRef cannot reference here.
if (cd != null)
if ( ! xts.hasSameClassDef(Types.baseType(cd.asType()), xts.GlobalRef())) {
XTerm h = homeVar(xc.thisVar(),xts);
if (h != null) // null for structs.
c.setPlace(XConstrainedTerm.make(h));
}
}
public static XConstrainedTerm methodPlaceTerm(MethodDef md) {
CConstraint d = ConstraintManager.getConstraintSystem().makeCConstraint();
// XTENLANG-2725: in X10 2.2, all methods are "global"
boolean isGlobal = true; // || md.flags().isStatic() || Types.isX10Struct(ct.asType());
XTerm term = isGlobal ? makePlace() : homeVar(md.thisVar(), md.typeSystem());
try {
return XConstrainedTerm.instantiate(d, term);
} catch (XFailure z) {
throw new InternalCompilerError("Cannot construct placeTerm from term and constraint.");
}
}
public static XConstrainedTerm constructorPlaceTerm(ConstructorDef cd) {
CConstraint d = ConstraintManager.getConstraintSystem().makeCConstraint();
XTerm term = homeVar(cd.thisVar(), cd.typeSystem());
try {
return XConstrainedTerm.instantiate(d, term);
} catch (XFailure z) {
throw new InternalCompilerError("Cannot construct placeTerm from term and constraint.");
}
}
public static XConstrainedTerm closurePlaceTerm(ClosureDef cd) {
CConstraint d = ConstraintManager.getConstraintSystem().makeCConstraint();
XTerm term = makePlace();
try {
return XConstrainedTerm.instantiate(d, term);
} catch (XFailure z) {
throw new InternalCompilerError("Cannot construct placeTerm from term and constraint.");
}
}
/**
* The method is global if it declared with a global flag or if it is a static method, or
* if it a method of a struct
* @param md
* @return
*/
static boolean isGlobalCode(MethodDef md) {
Flags flags = md.flags();
// XTENLANG-2725: in X10 2.2, all methods are "global"
boolean isGlobal = true || flags.isStatic() || Types.isX10Struct(md.container().get());
return isGlobal;
}
/**
*
* @param xc -- the current context
* @return -- the current context (if within a struct), else a context
* obtained by setting the new place term to be this.home.
*/
public static void setHereIsThisHome(Context c) {
XTerm h = thisHomeVar(c);
if (h != null) // null for structs.
c.setPlace(XConstrainedTerm.make(h));
}
public static Receiver makeReceiverLocalIfNecessary(ContextVisitor tc, Receiver target, Flags flags) {
return target;
}
public static X10Call makeReceiverLocalIfNecessary(X10Call n, ContextVisitor tc) throws SemanticException {
Receiver res =
makeReceiverLocalIfNecessary(tc, n.target(), n.methodInstance().flags());
if (res != null) {
if (res != n.target()) n = (X10Call) n.target(res).targetImplicit(false);
return n;
}
XConstrainedTerm h = ((Context) tc.context()).currentPlaceTerm();
if (h != null && PlaceChecker.isGlobalPlace(h.term())) {
throw new Errors.PlaceTypeErrorMethodShouldBeGlobal(n, n.position());
} else {
throw new Errors.PlaceTypeErrorMethodShouldBeLocalOrGlobal(n,
h == null ? null : h.term(),
placeTerm(n.target().type()),
n.position());
}
}
public static X10Field_c makeFieldAccessLocalIfNecessary(X10Field_c n, ContextVisitor tc) throws SemanticException {
Receiver res =
makeReceiverLocalIfNecessary(tc, n.target(), n.fieldInstance().flags());
if (res != null) {
if (res != n.target()) n = (X10Field_c) n.target(res).targetImplicit(false);
return n;
}
XConstrainedTerm h = ((Context) tc.context()).currentPlaceTerm();
if (h != null && PlaceChecker.isGlobalPlace(h.term())) {
throw new Errors.PlaceTypeErrorFieldShouldBeGlobal(n, n.position());
} else {
throw new Errors.PlaceTypeErrorFieldShouldBeLocalOrGlobal(n,
h == null ? null : h.term(),
placeTerm(n.target().type()),
n.position());
}
}
public static XConstrainedTerm computePlaceTerm(Expr place, Context xc, TypeSystem ts) throws SemanticException {
Type placeType = place.type();
CConstraint d = Types.xclause(placeType);
d = (d==null) ? ConstraintManager.getConstraintSystem().makeCConstraint() : d.copy();
CConstraint pc = null;
XTerm term = null;
XConstrainedTerm pt = null;
boolean placeIsPlace = ts.isImplicitCastValid(placeType, ts.Place(), xc);
if (placeIsPlace) {
term = ts.xtypeTranslator().translate(pc, place, xc);
if (term == null) {
term = makePlace();
}
try {
pt = XConstrainedTerm.instantiate(d, term);
} catch (XFailure z) {
throw new InternalCompilerError("Cannot construct placeTerm from " +
term + " and constraint " + d + ".");
}
} else {
throw new InternalCompilerError("The place argument of an \"at\" must be of type Place", place.position());
}
return pt;
}
}