/* * 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.constraints; import java.util.HashMap; import polyglot.ast.Field; import polyglot.types.FieldDef; import polyglot.types.FieldInstance; import polyglot.types.LocalDef; import polyglot.types.MethodDef; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.Types; import x10.constraint.XConstraint; import x10.constraint.XFailure; import x10.constraint.XTerm; import x10.constraint.XVar; import polyglot.types.Context; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import polyglot.ast.Field; import polyglot.types.FieldInstance; import polyglot.types.SemanticException; import x10.constraint.XConstraint; import x10.constraint.XDisEquals; import x10.constraint.XEQV; import x10.constraint.XEquals; import x10.constraint.XFailure; import x10.constraint.XField; import x10.constraint.XFormula; import x10.constraint.XLit; import x10.constraint.XLocal; import x10.constraint.XUQV; import x10.constraint.XVar; import x10.constraint.XTerm; import x10.constraint.XVar; import x10.constraint.visitors.XGraphVisitor; import x10.constraint.xnative.XNativeConstraint; import x10.constraint.xnative.XNativeTerm; import x10.constraint.xnative.XPromise; import x10.types.X10ClassDef; import polyglot.types.Context; import x10.types.X10FieldDef; import x10.types.X10LocalDef; import polyglot.types.TypeSystem; import x10.types.checker.PlaceChecker; import x10.types.constraints.visitors.AddInVisitor; import x10.types.constraints.visitors.CEntailsVisitor; /** * The compiler's notion of a constraint. * * <p> A CConstraint is an XConstraint, together with machinery to track two * special variables of interest to the compiler for this constraint, namely * the self variable and the this variable. * * <p>Further, the XTerms occurring in an XConstraint are created using static * methods on the class XTerms. In particular they carry type information in * their internal XName. This information is used to recursively materialize * more constraints from the given constraint. * * @author vj * */ public class CNativeConstraint extends XNativeConstraint implements CConstraint { /** Variable to use for self in the constraint. */ XVar self; XVar thisVar; public CNativeConstraint(XVar self) {this.self = self;} public CNativeConstraint() { this(ConstraintManager.getConstraintSystem().makeSelf()); } /** * Variable to use for self in the constraint. */ public XVar self() {return self;} /** * Return what, if anything, self is bound to in the current constraint. * @return */ public XVar selfVarBinding() {return bindingForVar(self());} public XVar thisVar() {return thisVar;} public boolean hasPlaceTerm() { if (roots==null) return false; for (XTerm t : roots.keySet()) if (PlaceChecker.isGlobalPlace(t)) return true; return false; } /** * Copy this constraint logically; that is, create a new constraint * that contains the same equalities (if any) as the current one. * vj: 08/12/09 * Copying also the consistency, and validity status, and thisVar and self. * It is critical that the selfVar for the constraint's copy is the same * as the selfVar for the original constraint. * <p> Always returns a non-null constraint. */ @Override public CNativeConstraint copy() { CNativeConstraint result = new CNativeConstraint(); result.self = this.self(); result.thisVar = this.thisVar(); result.addIn(this); return result; //return copyInto(result); } /** * Add constraint c into this, substituting this.self for c.self. Return this. * * Note: this is possibly side-effected by this operation. * * No change is made to this if c==null * * @param c -- the constraint to be added in. * @return the possibly modified constraint */ public void addIn(CConstraint c) {addIn(self(), c);} /** * Add constraint c into this, substituting newSelf for c.self. * Return this. * * Note: this is possibly side-effected by this operation. * * No change is made to this if c==null or c is valid. * * @param c -- the constraint to be added in. * @return the possibly modified constraint * * */ public void addIn(XTerm newSelf, CConstraint c) { if (c== null) return; if (! c.consistent()) {setInconsistent();return;} if (c.valid()) return; AddInVisitor v = new AddInVisitor(true, false, this, newSelf, c.self()); // hideFake==false to permit the "place checking" to work. // This ensures that multiple objects created at the same place // e.g. GlobalRef's, are treated as being at the same place by the // type-checker. c.visit(v); // vj: What about thisVar for c? Should that be added? // thisVar = getThisVar(this, c); return; } /** * Add the binding selfVar == var to this constraint, possibly * modifying it in place. * @param var */ public void addSelfBinding(XTerm var) {addBinding(self(), var);} /** * * Add the binding selfVar != term to this constraint, possibly * modifying it in place. * @param var */ public void addSelfDisBinding(XTerm term) {addDisBinding(self(), term);} /** * Add the binding selfVar == var to this constraint, possibly * modifying it in place. * @param var */ public void addSelfBinding(XConstrainedTerm var) {addBinding(self(), var);} /** * Add the binding thisVar == term to this constraint, possibly * modifying it in place. * @param var */ public void addThisBinding(XTerm term) {addBinding(thisVar(), term);} /** * Set thisVar to var (if var is non-null). To be used extremely carefully. Does not change * terms in the constraint. So there should not be terms referring to the old thisVar. * @param var */ public void setThisVar(XVar var) { if (var == null) return; thisVar = var; } /** * Add the binding s=t.term(), and add in the constraints of t into this. This constraint * is possibly modified in place. * @param s * @param t */ public void addBinding(XTerm s, XConstrainedTerm t) { addBinding(s, t.term()); addIn(s, t.constraint()); } /** * Add the binding s=t to this. This constraint is possibly modified in place. * @param s * @param t */ public void addBinding(XConstrainedTerm s, XTerm t) {addBinding(t,s);} /** * Add the binding s.term()=t.term() to this, and add in s.constraint() and t.constraint(). * This constraint is possibly modified in place. * @param s * @param t */ public void addBinding(XConstrainedTerm s, XConstrainedTerm t) { addBinding(s.term(), t.term()); addIn(s.term(), s.constraint()); addIn(t.term(), t.constraint()); } /** * Substitute y for x in this, returning a new constraint. * // Redeclare with the right return type */ public CNativeConstraint substitute(XTerm y, XVar x) throws XFailure { return substitute(new XTerm[] { y }, new XVar[] { x }); } /** * Return a new constraint obtained from the current one by substituting * newSelf for this.self(). * * Consequently, the self variable of the returned constraint does not * occur in it. * * Note, the resulting constraint may be marked inconsistent. * * FIXME: Yoav, this should also take a term to substitute for this, and return an XConstraint * @param newSelf * @return */ public CNativeConstraint instantiateSelf(XTerm newSelf) { CNativeConstraint result = new CNativeConstraint(); List<XTerm> terms = constraints(); for (XTerm term : terms) { XTerm t = term.subst(newSelf,self); try { result.addTerm(t); } catch (XFailure z) { result.setInconsistent(); return result; } } return result; } /** * Return those subset of constraints in the base set of other that are * <em>not</em> implied by this. That is, return the residue * r such that (r and this) implies other. * @param other -- must be checked for consistency before call is made * @return */ public CNativeConstraint residue(CConstraint other) { other = other.instantiateSelf(self()); assert other.consistent(); CNativeConstraint result = new CNativeConstraint(); if (! consistent) return result; for (XTerm term : other.constraints()) { try { if (! entails(term)) result.addTerm(term); } catch (XFailure z) { // since other is consistent, result must be. result.setInconsistent(); } } return result; } public XVar getThisVar(CConstraint t1, CConstraint t2) throws XFailure { XVar thisVar = t1 == null ? null : t1.thisVar(); if (thisVar == null) return t2==null ? null : t2.thisVar(); if (t2 != null && ! thisVar.equals( t2.thisVar())) throw new XFailure("Inconsistent this vars " + thisVar + " and " + t2.thisVar()); return thisVar; } /** * Return the result of substituting each yi for xi in this. * * The self var of the resulting constraint is guaranteed different from * the self var of this. * * TODO: Use an XGraphVisitor instead of constraints(). * Note: The only vars that need to be changed are in roots! * So doing constraints() and iterating over its terms is really bad. */ public CNativeConstraint substitute(XTerm[] ys, XVar[] xs) throws XFailure { assert (ys != null && xs != null); assert xs.length == ys.length; //for (XVar x : xs) // assert x != self; boolean eq = true; for (int i = 0; i < ys.length; i++) { XTerm y = ys[i]; XVar x = xs[i]; if (! y.equals(x)) eq = false; } if (eq) return this; if (! consistent) return this; // Don't do the quick occurrence check; x might occur in a self constraint. // XPromise last = lookupPartialOk(x); // if (last == null) return this; // x does not occur in this CNativeConstraint result = new CNativeConstraint(); //result.self = self(); // the resulting constraint should share the same self. List<XTerm> terms = constraints(); for (XTerm term : terms) { XTerm t = term; // if term is y==x.f, the subst will produce y==y.f, which is a cycle--bad! // if (term instanceof XEquals_c) { // XEquals_c eq = (XEquals_c) term; // XTerm l = eq.left(); // XTerm r = eq.right(); // if (y.equals(l) || y.equals(r)) // continue; // } for (int i = 0; i < ys.length; i++) { XTerm y = ys[i]; XVar x = xs[i]; t = t.subst(y, x); } t = t.subst(result.self(), self()); try { result.addTerm(t); } catch (XFailure z) { throw z; } } return result; } /** If other is not inconsistent, and this is consistent, * checks that each binding X=t in other also exists in this. * TODO: Improve performance by doing entailment in place * without getting other term's extConstraints. * @param other * @return */ public boolean entails(CConstraint other, ConstraintMaker sigma) { if (!consistent()) return true; if (other == null || other.valid()) return true; CEntailsVisitor ev = new CEntailsVisitor(true, true, this, sigma, other.self()); other.visit(ev); return ev.result(); } @Override protected XNativeTerm bindingForRootField(XVar root, Object field) { XNativeTerm term; if (field instanceof FieldDef) term = (XNativeTerm)ConstraintManager.getConstraintSystem().makeField(root, (FieldDef) field); else if (field instanceof MethodDef) term = (XNativeTerm)ConstraintManager.getConstraintSystem().makeField(root, (MethodDef) field); else { assert false; term = null;} XPromise p = term.nfp(this); if (p == null) return null; return p.term(); } public XTerm bindingForSelfField(FieldDef fd) { return bindingForRootField(self(), fd); } public XTerm bindingForSelfField(MethodDef fd) { return bindingForRootField(self(), fd); } /** * Return the term self.fieldName is bound to in the constraint, and null * if there is no such term. * * @param fieldName -- Name of field * @return * @throws XFailure */ public XTerm bindingForSelfField(Field f) { assert f != null; return bindingForSelfField(f.fieldInstance().def()); } public XTerm bindingForSelfField(FieldInstance f) { assert f != null; return bindingForSelfField(f.def()); } /* Return the least upper bound of this and other. That is, the resulting constraint has precisely * the constraints entailed by both this and other. * @param other * @return */ public CNativeConstraint leastUpperBound(CConstraint c2) { return leastUpperBound1((CNativeConstraint)c2); } /** * Return the constraint obtained by existentially quantifying out the * variable v. * * The self var of the resulting constraint is guaranteed different from * the self var of this. * @param v * @return */ public CNativeConstraint project(XVar v) { if (! consistent) return this; CNativeConstraint result = null; try { XVar eqv = ConstraintManager.getConstraintSystem().makeEQV(); result = substitute(eqv, v); } catch (XFailure c) { // should not happen } return result; } /** * Return exists self.this. Guaranteed that the self var of the * returned constrained does not occur in the constraint. * * The self var of the resulting constraint is guaranteed different from * the self var of this. * @return */ public CNativeConstraint exists() { return instantiateSelf(ConstraintManager.getConstraintSystem().makeEQV()); } /** * Add in the constraint c, and all the constraints associated with the * types of the terms referenced in t. * @param c * @param m * @throws XFailure */ public void addSigma(CConstraint c, Map<XTerm, CConstraint> m) { if (! consistent()) return; if (c != null && ! c.valid()) { addIn(c); addIn(c.constraintProjection(m)); } } public void addSigma(XConstrainedTerm ct, Map<XTerm, CConstraint> m) { if (! consistent()) return; if (ct != null) addSigma(ct.xconstraint(), m); } /** * Return the constraint r generated from this by adding all the constraints * specified by the types of the terms occurring in this. This is done * recursively. That is, for each constraint c added to r, we recursively * add the constraints for the terms that occur in c. * @param m * @param old * @return * @throws XFailure -- if r becomes inconsistent. */ public CNativeConstraint constraintProjection(Map<XTerm,CConstraint> m) { return constraintProjection(m, 0); // CollectionFactory.newHashSet()); } public CNativeConstraint constraintProjection(Map<XTerm,CConstraint> m, int depth /*Set<XTerm> ancestors*/) { CNativeConstraint r = new CNativeConstraint(); for (XTerm t : constraints()) { CNativeConstraint tc = constraintProjection(t, m, depth /*ancestors*/); if (tc != null) r.addIn(tc); } return r; } // ***************************************************************** Implementation protected boolean entails(XTerm term, XVar self) throws XFailure { XTerm subst = term.subst(self(), self); return entails(subst); } private static <T> boolean contains(Set<T> s, Set<T> c) { for (T t: c) if (s.contains(t)) return true; return false; } private static int MAX_DEPTH=15; private static CNativeConstraint constraintProjection(XTerm t, Map<XTerm,CConstraint> m, int depth /*Set<XTerm> ancestors*/) { if (t == null) return null; if (depth > MAX_DEPTH) { //System.err.println("(Warning) Reached threshold when checking constraints. If type-checking fails " // + "\n please insert a dynamic cast." // + "\n\t Term: "+ t); return new CNativeConstraint(); } CNativeConstraint r = (CNativeConstraint)m.get(t); if (r != null) return r; // pre-fill the cache to avoid infinite recursion m.put(t, new CNativeConstraint()); if (t instanceof CLocal) { CLocal v = (CLocal) t; X10LocalDef ld = v.localDef(); if (ld != null) { Type ty = Types.get(ld.type()); ty = PlaceChecker.ReplaceHereByPlaceTerm(ty, ld.placeTerm()); CNativeConstraint ci = (CNativeConstraint)Types.realX(ty); r = new CNativeConstraint(); ci = ci.instantiateSelf(v); r.addIn(ci); if (! r.consistent()) return r; // Recursively perform a constraintProjection on the new constraint ci // only if one of the ancestor terms does not occur in it. // if (! contains(ancestors, ci.terms())) r.addIn(ci.constraintProjection(m, depth+1)); } } else if (t instanceof XLit) { // no new info to contribute } else if (t instanceof CSelf){ // no new info to contribute } else if (t instanceof CThis){ // no new info to contribute } else if (t instanceof XEQV) { // no new info to contribute } else if (t instanceof XUQV) { // no new info to contribute } else if (t instanceof CField){ CField f = (CField) t; XTerm target = f.receiver(); //ancestors.add(target); //ancestors.add(t); CNativeConstraint rt = constraintProjection(target, m, depth+1); // ancestors); Type ty = f.type(); CNativeConstraint ci = null; if (ty == null) r = rt; else { ci = (CNativeConstraint)Types.realX(ty); XVar v = f.thisVar(); r = new CNativeConstraint(); if (v != null) { try { ci = ci.substitute(target, v); // xts.xtypeTranslator().transThisWithoutTypeConstraint()); } catch (XFailure z) { r.setInconsistent(); return r; } } ci = ci.instantiateSelf(f); r.addIn(ci); if (! r.consistent()) return r; // Recursively perform a constraintProjection on the new constraint ci // only if one of the ancestor terms does not occur in it. // if ( ! contains(ancestors, ci.terms())) { CNativeConstraint ciInferred = ci.constraintProjection(m, depth+1); // ancestors); r.addIn(ciInferred); // } if (rt != null) r.addIn(rt); } } else if (t instanceof XFormula<?>) { XFormula<?> f = (XFormula<?>) t; for (XTerm a : f.arguments()) { CNativeConstraint ca = constraintProjection(a, m, depth+1); //ancestors); // if (m.get(a) == null) // m.put(a, new CNativeConstraint()); // ancestors.add(a); if (ca != null) { if (r == null) r = new CNativeConstraint(); r.addIn(ca); } } } else if (t instanceof XField<?>) { } else assert false : "unexpected " + t; if (r != null) m.put(t, r); // update the entry return r; } private CNativeConstraint leastUpperBound1(CNativeConstraint c2) { CNativeConstraint c1 = this; CNativeConstraint result = c1.leastUpperBound0(c2); CNativeConstraint c1a = null, c2a = null; XVar x0 = c1.selfVarBinding(); if (x0 instanceof XVar) c1a = c1.project((XVar) x0); XVar x = c2.selfVarBinding(); if (x instanceof XVar) c2a = c2.project((XVar) x); if (c1a != null) { CNativeConstraint d = c1a.leastUpperBound0(c2); if (d.entails(result)) result = d; if (c2a != null) { d = c1a.leastUpperBound0(c2a); if (d.entails(result)) result = d; } } if (c2a != null) { CNativeConstraint d = c1.leastUpperBound0(c2a); if (d.entails(result)) result = d; } return result; } private CNativeConstraint leastUpperBound0(CNativeConstraint other) { XVar otherSelf = other.self(); CNativeConstraint result = new CNativeConstraint(); XVar resultSelf = result.self(); for (XTerm term : other.constraints()) { try { if (entails(term, otherSelf)) { term = term.subst(resultSelf, otherSelf); result.addTerm(term); } } catch (XFailure z) { result.setInconsistent(); } } return result; } }