/* * 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.types; import polyglot.types.ObjectType; import polyglot.types.Ref; import polyglot.types.SemanticException; import polyglot.types.Type; import x10.types.constraints.CConstraint; import x10.types.constraints.ConstraintMaker; import x10.types.constraints.XConstrainedTerm; import java.util.Collections; import java.util.List; import polyglot.types.Context; import polyglot.types.JavaArrayType; import polyglot.types.ClassType; import polyglot.types.FieldInstance; import polyglot.types.LazyRef_c; import polyglot.types.FieldDef; import polyglot.types.Name; import polyglot.types.NullType; import polyglot.types.ObjectType; import polyglot.types.JavaPrimitiveType; import polyglot.types.QName; import polyglot.types.Ref; import polyglot.types.ReferenceType_c; import polyglot.types.Resolver; import polyglot.types.SemanticException; import polyglot.types.ContainerType; import polyglot.types.Type; import polyglot.types.TypeObject; import polyglot.types.Types; import polyglot.types.TypeSystem; import polyglot.types.UnknownType; import polyglot.util.CodeWriter; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.util.Transformation; import polyglot.util.TransformingList; import x10.constraint.XFailure; import x10.constraint.XLit; import x10.constraint.XTerm; import x10.types.constraints.ConstraintManager; import x10.constraint.XVar; import x10.types.constraints.CConstraint; import x10.types.matcher.Subst; /** * 09/11/09 * A ConstrainedType_c represents the type T{c}. It has a basetype (of type Ref<? extends Type>) * and a constraint (of type Ref<CConstraint>). * * @author njnystrom * @author vj * */ public class ConstrainedType extends ReferenceType_c implements ObjectType, X10ThisVar { private static final long serialVersionUID = -3797674072640450629L; private Ref<CConstraint> constraint; // yoav todo: what about type constraints? We should keep the original expression as well so we will have accurate position info. private Ref<? extends Type> baseType; protected CConstraint realXClause; protected SemanticException realClauseInvalid; public ConstrainedType(TypeSystem ts, Position pos, Position errorPos, Ref<? extends Type> baseType, Ref<CConstraint> constraint) { super(ts, pos, errorPos); assert ts != null; this.baseType = baseType; // todo: we currently use UnknownType as an InvalidType //if ((baseType.known() && baseType.getCached() instanceof UnknownType)) // throw new InternalCompilerError("Base type must be known."); //assert : // " baseType is " + baseType; this.constraint = constraint; } public boolean isGloballyAccessible() { return false; } @Override public ConstrainedType copy() { ConstrainedType result = (ConstrainedType) super.copy(); result.constraint = Types.ref(constraint.get().copy()); result.baseType = (Ref<? extends Type>) Types.ref((Type) baseType.get().copy()); result.realXClause = realXClause==null ? null : realXClause.copy(); // no need to copy realClauseInvalid because it is used to cache if a realXClause is inconsistent. return result; } /** * Check that the basetype and constraint agree on thisVar. */ public XVar thisVar() { if (realXClause == null) realXClause = realX(); if (! realXClause.consistent()) return null; return realXClause.thisVar(); } public Ref<? extends Type> baseType() { return baseType; } /*public X10Type clearFlags(Flags f) { ConstrainedType_c c = (ConstrainedType_c) this.copy(); X10Type t = (X10Type) Types.get(c.baseType); if (t==null) throw new InternalCompilerError("Cannot remove flags " + f + " from null type."); t = t.clearFlags(f); c.baseType = Types.ref(t); // ((Ref<Type>)c.baseType).update(t); return c; } */ public Type makeX10Struct() { Type t = Types.get(baseType); assert t!=null; if (Types.isX10Struct(t)) return this; ConstrainedType c = (ConstrainedType) this.copy(); c.baseType = Types.ref(Types.makeX10Struct(t)); return c; } /* public Flags flags() { X10Type t = (X10Type) Types.get(this.baseType); assert t != null : "Cannot get flags on null type."; if (t==null) throw new InternalCompilerError("Cannot get flags on null type."); return t.flags(); } */ public ConstrainedType baseType(Ref<? extends Type> baseType) { if (baseType == this.baseType) return this; ConstrainedType n = (ConstrainedType) copy(); n.baseType = baseType; return n; } public Ref<CConstraint> constraint() { return constraint; } public ConstrainedType constraint(Ref<CConstraint> constraint) { if (constraint == this.constraint) return this; ConstrainedType n = (ConstrainedType) copy(); n.constraint = constraint; return n; } /** Returns the real clause for this constrained type. The self variable for the returned constraint is the same as the self variable for the depclause. */ public CConstraint getRealXClause() { if (realXClause == null) { realXClause = realX(); } return realXClause; } /*public void setRealXClause(CConstraint c, SemanticException error) { this.realXClause = c; this.realClauseInvalid = error; }*/ protected CConstraint realX() { // Now get the root clause and join it with the dep clause. CConstraint rootClause = Types.realX(Types.get(this.baseType())); if (!rootClause.consistent()) return rootClause; CConstraint depClause = Types.xclause(this); if (depClause==null) { return rootClause; } try { XVar thisVar = Types.getThisVar(rootClause, depClause); } catch (XFailure z) { rootClause.setInconsistent(); return rootClause; } // Need to ensure that returned clause has same self var as Types.xclause(this). //if (depClause.valid()) // return rootClause; depClause.addIn(rootClause); return depClause; } public void checkRealClause() throws SemanticException { // Force real clause to be computed. if (realXClause == null) realXClause = realX(); if (! realXClause.consistent()) { if (realClauseInvalid != null) { realClauseInvalid = new SemanticException(this + " has an inconsistent real clause " + realXClause); } throw realClauseInvalid; } } @Override public String translate(Resolver c) { return baseType().get().translate(c); } /*public boolean isSafe() { return ((X10Type) baseType.get()).isSafe(); }*/ @Override public String typeToString() { Type type = baseType.getCached(); String typeName = type.toString(); String cString = constraintString(); if (type instanceof FunctionType_c && cString.length() > 0) typeName = "("+typeName+")"; return typeName + cString; } private String constraintString() { StringBuilder sb = new StringBuilder(); Type base = baseType.getCached(); CConstraint c = constraint.getCached(); if (c != null && ! c.valid() && (!c.consistent() || !(c.extConstraintsHideFake().size() == 0))) { sb.append(c); } return sb.toString(); } // vj 08/11/09 // todo: For each FieldInstance fi of baseType, need to return a new FieldInstance fi' obtained // by adding this: this.constraint. @Override public List<FieldInstance> fields() { Type base = baseType.get(); if (base instanceof ContainerType) { final List<FieldInstance> fis = ((ContainerType) base).fields(); return fis; } return Collections.emptyList(); } /* public void ensureSelfBound() { assert constraint != null; XVar self = X10TypeMixin.selfVarBinding(this); if (self == null) { self = XTerms.makeUQV(); CConstraint c = constraint.get(); try { c.addSelfBinding(self); } catch (XFailure z) { System.out.println("failure " + z); } constraint.update(c); } } */ // vj: Revised substantially 08/11/09 @Override public List<Type> interfaces() { final Type base = baseType.get(); if (! (base instanceof ObjectType)) return Collections.emptyList(); List<Type> l = ((ObjectType) base).interfaces(); CConstraint c = constraint.get(); // Get or make a name tt for self. XTerm t = c.bindingForVar(c.self()); if (t == null) { t = ConstraintManager.getConstraintSystem().makeEQV(); } final XTerm tt = t; return new TransformingList<Type, Type>(l, new Transformation<Type, Type>() { public Type transform(Type o) { CConstraint c2 = Types.xclause(o); c2 = c2 != null ? c2.copy() : ConstraintManager.getConstraintSystem().makeCConstraint(); if (c2.thisVar() != null) c2.addBinding(c2.thisVar(), tt); //c2.addSelfBinding(tt); // c2.substitute(tt, XTerms.) // vj: 1/27/11 -- change from past behavior // if c2 is inconsistent, we dont return o. return Types.xclause(o, c2); } }); } @Override public List<MethodInstance> methods() { Type base = baseType.get(); if (base instanceof ContainerType) { return ((ContainerType) base).methods(); } return Collections.emptyList(); } @Override public Type superClass() { Type base = baseType.get(); if (base instanceof ObjectType) { Type o = ((ObjectType) base).superClass(); if (o != null) { CConstraint c = constraint.get(); final XTerm t = c.bindingForVar(c.self()); if (t != null) { CConstraint c2 = Types.xclause(o); c2 = c2 != null ? c2.copy() : ConstraintManager.getConstraintSystem().makeCConstraint(); TypeSystem xts = (TypeSystem) o.typeSystem(); c2.addSelfBinding(t); return Types.xclause(o, c2); } } return o; } return null; } public QName fullName() { Type base = baseType.get(); return base.fullName(); } public Name name() { Type base = baseType.get(); return base.name(); } @Override public boolean isJavaPrimitive() { Type base = baseType.get(); return base.isJavaPrimitive(); } public boolean isX10Struct() { return Types.isX10Struct(baseType.get()); } @Override public boolean isClass() { Type base = baseType.get(); return base.isClass(); } @Override public boolean isNull() { Type base = baseType.get(); return base.isNull(); } @Override public boolean isArray() { Type base = baseType.get(); return base.isArray(); } @Override public boolean isReference() { Type base = baseType.get(); return base.isReference(); } @Override public JavaPrimitiveType toPrimitive() { Type base = baseType.get(); return base.toPrimitive(); } @Override public X10ClassType toClass() { Type base = baseType.get(); return base.toClass(); } @Override public NullType toNull() { Type base = baseType.get(); return base.toNull(); } @Override public JavaArrayType toArray() { Type base = baseType.get(); return base.toArray(); } public void print(CodeWriter w) { Type base = baseType.get(); base.print(w); } public void printConstraint(CodeWriter w) { Type base = baseType.getCached(); CConstraint c = constraint.getCached(); } public boolean equalsNoFlag(Type t2) { return false; } // Methods below this have been moved from polyglot.type.Types /** * Add the constraint self.rank==x to this unless that causes an inconsistency. * @param x * @return */ public ConstrainedType addRank(int x) { return addRank(ConstraintManager.getConstraintSystem().makeLit(x, ts.Int())); } public ConstrainedType addSize(int x) { return addIntProperty(x, Name.make("size")); } public ConstrainedType addIntProperty(int x, Name name) { return addProperty(ConstraintManager.getConstraintSystem().makeLit(x, ts.Int()), name); } /** * Add the constraint self.rank==x to this unless that causes an inconsistency. * @param x * @return */ public ConstrainedType addRank(XTerm x) { return addProperty(x, Name.make("rank")); } public ConstrainedType addProperty(XTerm x, Name name) { XTerm xt = findOrSynthesize(name); if (xt == null) { throw new InternalCompilerError("*******(" + this + ") Could not find property " + name); } return addBinding(xt, x); } public XTerm findOrSynthesize(Name n) { // todo: why do we have both findOrSynthesize and find if they do the same thing? return find(n); } /** * Find the term t, if any, such that t entails {self.propName==t}. */ public XTerm findProperty(Name name) { CConstraint c = Types.realX(this); if (c == null || ! c.consistent()) return null; FieldInstance fi = Types.getProperty(this, name); // TODO: check dist.region.p and region.p if (fi != null) return c.bindingForSelfField(fi.def()); MethodInstance mi = Types.getPropertyMethod(this, name); if (mi != null) { return c.bindingForSelfField(mi.def()); } return null; } /** * Find a property in self with a given name. The term returned may contain self as receiver. * @param name * @return */ public XTerm find(Name name) { XTerm val = findProperty(name); if (val == null) { TypeSystem xts = typeSystem(); CConstraint c = Types.realX(this); if (c != null) { // build the synthetic term. FieldInstance fi = Types.getProperty(this, name); XTerm var = selfVar(); if (var !=null) { XTypeTranslator translator = xts.xtypeTranslator(); if (fi != null) { val = translator.translate(var, fi); } else { MethodInstance mi = Types.getPropertyMethod(this, name); if (mi != null) { val = translator.translate(var, mi); } } } } } if (val!=null) { // expand it in order to handle Dist.rank() val = XTypeTranslator.expandSelfPropertyMethod(val); } return val; // todo: val can be null! if we build a synthetic term, then why not always build it??? } public XVar selfVar() { return Types.get(constraint()).self(); } public ConstrainedType addBinding(XTerm t1, XTerm t2) { CConstraint c = Types.xclause(this); c = c == null ? ConstraintManager.getConstraintSystem().makeCConstraint() :c.copy(); c.addBinding(t1, t2); return (ConstrainedType) Types.xclause(Types.baseType(this), c); } public ConstrainedType addBinding(XTerm t1, XConstrainedTerm t2) { CConstraint c = ConstraintManager.getConstraintSystem().makeCConstraint(); c.addBinding(t1, t2); return (ConstrainedType) Types.xclause(this, c); } public ConstrainedType addSelfBinding(XTerm t1) { CConstraint c = Types.xclause(this); c = c == null ? ConstraintManager.getConstraintSystem().makeCConstraint() :c.copy(); c.addSelfBinding(t1); return (ConstrainedType) Types.xclause(Types.baseType(this), c); } public ConstrainedType addSelfDisBinding(XTerm t1) { CConstraint c = Types.xclause(this); c = c == null ? ConstraintManager.getConstraintSystem().makeCConstraint() :c.copy(); c.addSelfDisBinding(t1); return (ConstrainedType) Types.xclause(Types.baseType(this), c); } /** * Add t1 != t2 to t. Note: The type returned may have an inconsistent * constraint. * @param t * @param t1 * @param t2 * @return public ConstrainedType addDisBinding(Type t, XTerm t1, XTerm t2) { assert (! (t instanceof UnknownType)); CConstraint c = Types.xclause(t); c = c == null ? ConstraintManager.getConstraintSystem().makeCConstraint() :c.copy(); c.addDisBinding(t1, t2); return (ConstrainedType) Types.xclause(Types.baseType(t), c); } */ // vj: 08/11/09 -- have to recursively walk the // type parameters and add the constraint to them. public static ConstrainedType xclause(final Ref<? extends Type> t, final Ref<CConstraint> c) { if (t == null) { return null; } if (t.known() && c != null && c.known()) { Type tx = Types.get(t); assert tx != null; TypeSystem ts = (TypeSystem) tx.typeSystem(); tx = ts.expandMacros(tx); CConstraint oldc = Types.xclause(tx); CConstraint newc = Types.get(c); if (newc == null) return new ConstrainedType(ts, tx.position(), tx.position(), Types.ref(tx), Types.ref(ConstraintManager.getConstraintSystem().makeCConstraint())); if (oldc == null) { return new ConstrainedType(ts, tx.position(), tx.position(), t, c); } else { newc = newc.copy(); newc.addIn(oldc); // may become inconsistent return new ConstrainedType(ts, tx.position(), tx.position(), Types.ref(Types.baseType(tx)), Types.ref(newc)); } } final LazyRef_c<Type> tref = new LazyRef_c<Type>(null); tref.setResolver(new Runnable() { public void run() { Type oldt = Types.baseType(Types.get(t)); tref.update(oldt); } }); final LazyRef_c<CConstraint> cref = new LazyRef_c<CConstraint>(null); cref.setResolver(new Runnable() { public void run() { CConstraint oldc = Types.xclause(Types.get(t)); if (oldc != null) { CConstraint newc = Types.get(c); if (newc != null) { newc = newc.copy(); newc.addIn(oldc); // newc may have become inconsistent cref.update(newc); } else { cref.update(oldc); } } else { cref.update(oldc); } } }); Type tx = t.getCached(); assert tx != null; return new ConstrainedType((TypeSystem) tx.typeSystem(), tx.position(), tx.position(), t.known()? t: tref, cref); } /** * Returns a copy of this's constraint, if it has one, null otherwise. * @return */ public CConstraint xclause() { return Types.get(constraint()).copy(); } public static ConstrainedType xclause(Type t, CConstraint c) { if (t == null) return null; if (c == null /*|| c.valid()*/) { c = ConstraintManager.getConstraintSystem().makeCConstraint(); } return xclause(Types.ref(t), Types.ref(c)); } public ConstrainedType addRect() { ConstrainedType result=this; XTerm xt = findOrSynthesize(Name.make("rect")); if (xt != null) result = addBinding(xt, ConstraintManager.getConstraintSystem().xtrue()); return result; } /** * Add the constraint self.zeroBased==true; * @return */ public ConstrainedType addZeroBased() { ConstrainedType result = this; XTerm xt = findOrSynthesize(Name.make("zeroBased")); if (xt != null) result = addBinding(xt, ConstraintManager.getConstraintSystem().xtrue()); return result; } public ConstrainedType addNonNull() { return addSelfDisBinding(ConstraintManager.getConstraintSystem().xnull()); } /** * Return the term self.rank, where self is the selfvar for this. */ public XTerm rank(Context context) { return findOrSynthesize(Name.make("rank")); } /** * Return the term self.size, where self is the selfvar for this. */ public XTerm size(Context context) { return findOrSynthesize(Name.make("size")); } /** * Does this type entail self.rect? Use sigma * from the context if necessary. * @param context * @return */ public boolean isRect(Context context) { return amIProperty(Name.make("rect"), context); } /** * Does this type entail self.zeroBased? Use sigma * from the context if necessary. * @param context * @return */ public boolean isZeroBased(Context context) { return amIProperty(Name.make("zeroBased"), context); } /** * Does this type entail self.rank==1? Use sigma * from the context if necessary. */ public boolean isRankOne(Context context) { return isRank(typeSystem().ONE(), context); } /** * Does this type entail self.rank==2? Use sigma * from the context if necessary. */ public boolean isRegionRankTwo(Context context) { return isRank(typeSystem().TWO(), context); } /** * Does this type entail self.rank==3? Use sigma * from the context if necessary. */ public boolean isRegionRankThee(Context context) { return isRank(typeSystem().THREE(), context); } /** * Does this type entail self.rank==lit? Use sigma * from the context if necessary. */ public boolean isRank(XLit lit, Context context) { return hasPropertyValue(Name.make("rank"), lit, context); } /** * Does this type entail self.propName? Use sigma * from the context if necessary. */ public boolean amIProperty(Name propName, Context context) { return hasPropertyValue(propName, XTypeTranslator.translate(true, ts), context); } /** * Does this type entail self.propName == lit? Use sigma * from the context if necessary. */ public boolean hasPropertyValue(Name propName, XLit lit, final Context context) { TypeSystem xts = typeSystem(); final CConstraint r = realX(); if (! r.consistent()) return false; // first try self.p X10FieldInstance fi = Types.getProperty(this, propName); if (fi != null) { final CConstraint c = ConstraintManager.getConstraintSystem().makeCConstraint(); XTerm term = xts.xtypeTranslator().translate(c.self(), fi); c.addBinding(term, lit); if (! c.consistent()) return false; return r.entails(c, new ConstraintMaker() { public CConstraint make() throws XFailure { return context.constraintProjection(r, c); } }); } else { // try self.p() try { MethodInstance mi = xts.findMethod(this, xts.MethodMatcher(this, propName, Collections.<Type>emptyList(), xts.emptyContext())); XTerm body = mi.body(); final CConstraint c = ConstraintManager.getConstraintSystem().makeCConstraint(); body = body.subst(c.self(), mi.x10Def().thisVar()); c.addTerm(body); return r.entails(c, new ConstraintMaker() { public CConstraint make() throws XFailure { return context.constraintProjection(r, c); } }); } catch (XFailure f) { return false; } catch (SemanticException f) { return false; } } } public XTerm makeProperty( String propStr) { Name propName = Name.make(propStr); CConstraint c = realX(); if (c != null) { // build the synthetic term. XTerm var = selfVar(); if (var !=null) { X10FieldInstance fi = Types.getProperty(this, propName); if (fi != null) { TypeSystem xts = typeSystem(); XTerm val = xts.xtypeTranslator().translate(var, fi); return val; } } } return null; } public XTerm makeZeroBased() { return makeProperty("zeroBased"); } public XTerm makeRail() { return makeProperty("rail"); } // Ensure type equality works correctly when a type has vacuous constraints. @Override public boolean equalsImpl(TypeObject o) { if (this == o) return true; if (baseType().get().equalsImpl(o)) { CConstraint c = ConstraintManager.getConstraintSystem().makeCConstraint(); if (c.entails(constraint().get())) return true; } if (o instanceof ConstrainedType) { ConstrainedType other = (ConstrainedType) o; Type bt = baseType().get(); Type obt = other.baseType().get(); if ((bt != obt)) return false; CConstraint c = constraint().get(); CConstraint oc = other.constraint().get(); if (! c.entails(oc) || ! oc.entails(c)) return false; return true; } return false; } }