/* * 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 * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.types; import java.util.HashMap; import java.util.List; import java.util.Map; import polyglot.util.Copy; import polyglot.util.CollectionUtil; import polyglot.util.Position; import x10.util.CollectionFactory; import x10.constraint.XFailure; import x10.constraint.XVar; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.X10CodeDef; import x10.types.X10FieldInstance; import x10.types.X10LocalInstance; import x10.types.MethodInstance; import x10.types.constraints.CConstraint; import x10.types.constraints.TypeConstraint; import x10.types.constraints.XConstrainedTerm; import java.util.*; import polyglot.main.Reporter; import polyglot.util.*; import x10.constraint.XTerm; import x10.constraint.XVar; import x10.errors.Errors; import x10.types.AsyncDef; import x10.types.AtDef; import x10.types.ConstrainedType; import x10.types.EnvironmentCapture; import x10.types.MacroType; import x10.types.MethodInstance; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.X10CodeDef; import x10.types.X10ConstructorDef; import x10.types.X10FieldInstance; import x10.types.X10LocalInstance; import x10.types.X10MemberDef; import x10.types.X10MethodDef; import x10.types.X10ProcedureDef; import x10.types.XTypeTranslator; import x10.types.checker.PlaceChecker; import x10.types.constraints.CConstraint; import x10.types.constraints.CQualifiedVar; import x10.types.constraints.ConstraintManager; import x10.types.constraints.SubtypeConstraint; import x10.types.constraints.TypeConstraint; import x10.types.constraints.XConstrainedTerm; import x10.util.CollectionFactory; /** * This class maintains a context for looking up named variables, types, * and methods. * It's implemented as a stack of Context objects. Each Context * points to an outer context. To enter a new scope, call one of the * pushXXX methods. To leave a scope, just follow the outer() pointer. * NodeVisitors handle leaving scope automatically. * Each context object contains maps from names to variable, type, and * method objects declared in that scope. * This class extends the jl notion of Context to keep track of * the current deptype, if any, and its set of properties. These * properties can be referenced inside a deptype, i.e. in * the depClause in [[Foo(: depClause)]]. * * We implement as follows. Since we want to reuse the mechanism for pushing * popping scopes as we enter a depClause, we shall implement pushDepType * as a pushing of a context, rather than as adding extra structure to * the current context. * * To push a deptype we push a class. However * we delegate certain methods, such as currentClass() to outer, since * pushing a deptype does not change the meaning of "this", only introduces * a meaning for "self". Thus jl code should continue to work -- it does not "see" * the deptype pushed onto the context. * * While processing depClause the only variables of the surrounding scope * that are visible are the final variables. Inside depClause no new scopes * can be entered, e.g. inner classes, or method declarations or even depTypes. * This is a property of the X10 language. * * Certain methods should not be called if depType is set, e.g. methods to add names, * push scopes etc. These throw an assertion error. * * Certain methods can be called within a deptype, but the result should be as if they * are called on the outer context. So this is easily dealt with using the pattern * depType == null ? super.Foo(..) : pop.Foo(...) * That is, if this context is not deptype context, run the usual code. Otherwise * delegate to the outer context. * * @author nystrom * @see Context */ public class Context implements Resolver, Cloneable { /** * The prefix for compiler generated variables. No user-specified * type or package or parameter name or local variable should begin * with this prefix. */ public static String MAGIC_VAR_PREFIX = "x10$__var"; // Use addVariable to add a PropertyInstance to the context. /** Context name table */ public static String MAGIC_NAME_PREFIX = "X10$"; static Map<String,Name> contextNameTable = CollectionFactory.newHashMap(); public static enum Kind { BLOCK("block"), CLASS("class"), CODE("code"), ASYNC("async"), AT("at"), OUTER("outer"), SOURCE("source"); public final String name; private Kind(String name) { this.name = name; } @Override public String toString() { return name; } } public enum X10Kind { None, Async, At, Finish; } public X10Kind x10Kind = X10Kind.None; static protected int varCount = 0; static protected int nameCount = 0; public static Name getNewVarName() { return Name.make(MAGIC_VAR_PREFIX + (varCount++)); } public static Name makeFreshName(String name) { synchronized (contextNameTable) { Name n = contextNameTable.get(name); if (n == null) { String fresh = MAGIC_NAME_PREFIX + name + (nameCount++); n = Name.make(fresh); contextNameTable.put(name,n); } return n; } } public static final Kind BLOCK = Kind.BLOCK; public static final Kind CLASS = Kind.CLASS; public static final Kind CODE = Kind.CODE; public static final Kind ASYNC = Kind.ASYNC; public static final Kind AT = Kind.AT; public static final Kind OUTER = Kind.OUTER; public static final Kind SOURCE = Kind.SOURCE; private static final Collection<String> TOPICS = CollectionUtil.list(Reporter.types, Reporter.context); protected Context outer; protected TypeSystem ts; protected Reporter reporter; protected String name; protected boolean inLoopHeader; protected boolean inAnnotation; protected boolean inAnonObjectScope; protected boolean inAssignment; protected boolean inClockedFinishScope=false; protected boolean isClocked=false; protected Ref<? extends Type> depType = null; protected X10ClassDef inSuperOf = null; // in supertype of this type? protected XConstrainedTerm currentFinishPlaceTerm = null; protected ImportTable it; // import table for file protected Kind kind; protected X10ClassType type; protected X10ClassDef scope; protected X10CodeDef code; protected Map<Name,Type> types; protected Map<Name,VarInstance<?>> vars; protected boolean inCode; protected VarDef varWhoseTypeIsBeingElaborated = null; protected boolean staticContext; // is the context static? protected XConstrainedTerm currentPlaceTerm = null; protected CConstraint currentConstraint=null; protected Ref<TypeConstraint> currentTypeConstraint=null; protected XConstrainedTerm thisPlace = null; protected boolean cStackUsedUp=false; protected Type currentCollectingFinishType=null; protected Type switchType=null; protected Stack<Ref<CConstraint>> cStack=null; protected boolean specialAsQualifiedVar=false; public Context(TypeSystem ts) { this.ts = ts; this.outer = null; this.kind = OUTER; this.reporter = ts.extensionInfo().getOptions().reporter; } // -------- Section: Query methods -------------------------- public boolean isBlock() { return kind == BLOCK; } public boolean isClass() { return kind == CLASS; } public boolean isCode() { return kind == CODE; } public boolean isAsync() { return kind == ASYNC; } public boolean isAt() { return kind == AT; } public boolean isOuter() { return kind == OUTER; } public boolean isSource() { return kind == SOURCE; } public boolean isClocked() { if (isClocked) return true; X10CodeDef cd = currentCode(); if (cd instanceof X10MethodDef) { X10MethodDef md = (X10MethodDef) cd; return md.flags().isClocked(); } return false; } public boolean inDepType() { return depType != null; } public boolean inLoopHeader() { return inLoopHeader; } public boolean inAnnotation() { return inAnnotation; } public boolean inAnonObjectScope() { return inAnonObjectScope;} public boolean inClockedFinishScope() { if (inClockedFinishScope) return true; if (outer != null) return ((Context) outer).inClockedFinishScope(); return false; } public boolean inSuperTypeDeclaration() { return inSuperOf != null; } public X10ClassDef supertypeDeclarationType() { return inSuperOf; } public boolean isFormalParameter(LocalDef ld) { CodeDef thisCode = currentCode(); if (thisCode instanceof X10ProcedureDef) { for (LocalDef fd : ((X10ProcedureDef) thisCode).formalNames()) if (ld == fd) return true; } return false; } public TypeSystem typeSystem() { return ts; } public Type currentDepType() { return Types.get(depType); } public Ref<? extends Type> depTypeRef() { return depType; } public VarDef varWhoseTypeIsBeingElaborated() { return varWhoseTypeIsBeingElaborated; } /** Get import table currently in scope. */ public ImportTable importTable() { return it; } /** The current package, or null if not in a package. */ public Package package_() { return Types.get(importTable().package_()); } Context skipDepType() { Context me = this; while (me.depType !=null) me = me.pop(); return me; // could be null? } public Type currentSwitchType() { return switchType; } /** Return the innermost method or constructor in scope. */ public X10CodeDef currentCode() { return skipDepType().code; } /** * Return true if in a method's scope and not in a local class within the * innermost method. ** Alternatively? Return whether innermost non-block scope is a code scope. */ public boolean inCode() { return skipDepType().inCode; } public boolean inAssignment() { return inAssignment; } /** * Returns whether the current context is a static context. * A statement of expression occurs in a static context if and only if the * inner-most method, constructor, instance initializer, static initializer, * field initializer, or explicit constructor statement enclosing the * statement or expressions is a static method, static initializer, the * variable initializer of a static variable, or an explicit constructor * invocation statement. (Java Language Spec, 2nd Edition, 8.1.2) */ public boolean inStaticContext() { return skipDepType().staticContext; } public boolean specialAsQualifiedVar() { return specialAsQualifiedVar || inDepType(); } /** * Returns whether the particular symbol is defined locally. If it isn't * in this scope, we ask the parent scope, but don't traverse to enclosing * classes. */ public boolean isLocal(Name name) { Context me = this; while (me != null && me.depType != null) { me = me.pop(); } if (me != null) { if (isClass()) { return false; } if ((isBlock() || isCode() || isAsync() || isAt()) && (findVariableInThisScope(name) != null || findInThisScope(ts.TypeMatcher(name)) != null)) { return true; } if (isCode()) { return false; } if (outer == null) { return false; } return outer.isLocal(name); } return false; } public boolean localHasAt(Name name) { // is there an "at" until the definition of the local? final VarInstance<?> var = findVariableInThisScope(name); return var!=null && var instanceof LocalInstance ? false : // no "at" found until the definition of the local x10Kind== X10Kind.At ? true : outer==null || (isCode() && !isDummyCode(currentCode())) ? false : outer.localHasAt(name); } // Same as isLocal(), except async and at are considered code scopes public boolean isLocalExcludingAsyncAt(Name name) { if (isClass()) { return false; } if ((isBlock() || isCode() || isAsync() || isAt()) && (findVariableInThisScope(name) != null || findInThisScope(ts.TypeMatcher(name)) != null)) { return true; } if (isCode() || isAsync() || isAt()) { return false; } if (outer == null) { return false; } return outer.isLocalExcludingAsyncAt(name); } public boolean isValInScopeInClass(Name name) { if (isClass()) { return false; } if ((isBlock() || isCode() || isAsync() || isAt()) && (findVariableInThisScope(name) != null)) { return true; } if (outer instanceof Context) { return outer.isValInScopeInClass(name); } return false; } public Type collectingFinishType() { if (currentCollectingFinishType != null) return currentCollectingFinishType; // check if you are in code. Context cxt = this; CodeDef cc = cxt.currentCode(); if (cc instanceof X10ProcedureDef) { X10ProcedureDef md = (X10ProcedureDef) cc; while (md instanceof AtDef || md instanceof AsyncDef) { cxt = cxt.pop(); if (cxt == null) break; cc = cxt.currentCode(); if (cc instanceof X10MethodDef) md = (X10MethodDef) cc; } if (md != null) return Types.get(md.offerType()); } return null; } public static boolean isDummyCode(CodeDef ci) { return (ci instanceof AtDef || ci instanceof AsyncDef); } public boolean inAsyncScope() { return x10Kind== X10Kind.Async ? true : outer==null || (isCode() && !isDummyCode(currentCode())) ? false : outer.inAsyncScope(); } public boolean isSequentialAccess(boolean isSeqential, Name name) { // there is no async without an enclosing finish if (findVariableInThisScope(name)!=null) return isSeqential; if (outer!=null) { return outer.isSequentialAccess( x10Kind==X10Kind.Finish ? true : x10Kind==X10Kind.Async ? false : isSeqential,name); } return true; // not in this scope (like a field), so access is ok } protected String mapsToString() { return (types == null ? "" : "types=|" + types + "|") + (vars == null ? "" : "vars=|" + vars + "|"); } /** Return the innermost class in scope. */ public X10ClassType currentClass() { return skipDepType().type; } /** * Gets current class scope */ CConstraint outerThisEquivalences() { CConstraint result = ConstraintManager.getConstraintSystem().makeCConstraint(); Type curr = currentClass(); List<X10ClassDef> outers = Types.outerTypes(curr); for (int i=0; i < outers.size(); i++) { XVar base = outers.get(i).thisVar(); for (int j=i+1; j < outers.size(); j++ ) { X10ClassDef y = outers.get(j); result.addBinding(y.thisVar(), ConstraintManager.getConstraintSystem().makeQualifiedVar(y.asType(), base)); } } return result; } public X10ClassDef currentClassDef() { return skipDepType().scope; } public XConstrainedTerm currentFinishPlaceTerm() { return currentFinishPlaceTerm;} public XConstrainedTerm currentPlaceTerm() { return currentPlaceTerm; } public CConstraint currentConstraint() { CConstraint result = currentConstraint; if (result == null) { result = ConstraintManager.getConstraintSystem().makeCConstraint(); if (! inStaticContext()) { result.setThisVar(thisVar()); CConstraint d = outerThisEquivalences(); result.addIn(d); } } if (! cStackUsedUp) { // this means that cStack needs to be evaluated. cStackUsedUp=true; if (cStack!= null) { for (Object r : cStack.toArray()) { CConstraint top = Types.get((Ref<CConstraint>) r); result.addIn(top); } } // also get the constraint from the outer context. if (outer !=null) { // call this recursively so the outer also caches its constraint. // System.out.print("^" +outer.hashCode()); CConstraint c = outer.currentConstraint(); if (c != null && ! c.valid()) result.addIn(c); // do not store it in currentConstraint. } // cache it. currentConstraint = result; } // System.out.println("==>" + result); return result; } public TypeConstraint currentTypeConstraint() { // System.out.println("tc: #" + hashCode() + "."); TypeConstraint result = null; if (currentTypeConstraint == null) result=new TypeConstraint(); else result=currentTypeConstraint.get(); // System.out.println("tc: #" + hashCode() + "==> " + result); return result; } public Type findInThisScope(Matcher<Type> matcher) { Name name = matcher.name(); Type t = null; if (types != null) { t = types.get(name); if (t != null) { try { t = matcher.instantiate(t); } catch (SemanticException e) { // todo: we might have this exception: // SemanticException: Call invalid; calling environment does not entail the method guard. // therefore we should report the error or store it. // Because we ignore it, the error message for TypedefConstraint3c_MustFailCompile is horrible! t = null; } } } if (t == null && isClass()) { if (this.type != null && ! this.type.isAnonymous()) { try { t = matcher.instantiate(this.type); } catch (SemanticException e) { } } } if (t == null && isClass()) { try { List<Type> res = ts.classContextResolver(this.currentClass(), this).find(matcher); t = SystemResolver.first(res); } catch (SemanticException e) { } } return t; } public void addNamedToThisScope(Type type) { if (types == null) types = CollectionFactory.<Name, Type>newHashMap(); types.put(type.name(), type); } public XVar thisVar() { if (this.inSuperTypeDeclaration()) { X10ClassDef t = this.supertypeDeclarationType(); return t.thisVar(); } CodeDef cd = this.currentCode(); if (cd instanceof X10MemberDef) { return ((X10MemberDef) cd).thisVar(); } X10ClassDef t = (X10ClassDef) this.currentClassDef(); if (t != null) { return t.thisVar(); } return null; } public CConstraint constraintProjection(CConstraint... cs) { Map<XTerm, CConstraint> m = CollectionFactory.newHashMap(); // add in the real clause of the type of any var mentioned in the constraint list cs CConstraint r = null; for (CConstraint ci : cs) { if (ci == null) continue; CConstraint ri = ci.constraintProjection(m); if (r == null) r = ri; else r.addIn(ri); if (! r.consistent()) // r is inconsistent, no point going further. return r; } if (r == null) r = ConstraintManager.getConstraintSystem().makeCConstraint(); // fold in the current constraint r.addSigma(currentConstraint(), m); r.addSigma(currentPlaceTerm, m); PlaceChecker.AddHereEqualsPlaceTerm(r, this); r.addSigma(thisPlace, m); // fold in the real clause of the base type Type selfType = this.currentDepType(); if (selfType != null) { CConstraint selfConstraint = Types.realX(selfType); if (selfConstraint != null) { r.addIn(selfConstraint.instantiateSelf(r.self())); } } return r; } // to check if we can call property(...) or assign to final fields // FIXME: [IP] I think this is subtly wrong -- what if there is an intervening block? public X10ConstructorDef getCtorIgnoringAsync() { return inCode() && currentCode() instanceof X10ConstructorDef? (X10ConstructorDef) currentCode() : x10Kind==X10Kind.Async ? outer.getCtorIgnoringAsync() : null; } public List<LocalDef> locals() { if (vars != null) { List<LocalDef> lis = allLocals(); if (lis.isEmpty()) return lis; Context c = pop(); if (c != null) lis.removeAll(c.allLocals()); return lis; } return Collections.<LocalDef>emptyList(); } public List<LocalDef> allLocals() { if (vars != null) { List<LocalDef> lis = new ArrayList<LocalDef>(vars.values().size()); for (VarInstance<?> vi : vars.values()) { if (vi instanceof LocalInstance) lis.add(((LocalInstance) vi).def()); } Context c = pop(); if (c != null) lis.addAll(c.allLocals()); return lis; } else { Context c = pop(); if (c != null) return c.allLocals(); } return Collections.<LocalDef>emptyList(); } /** * Looks up a method with name "name" and arguments compatible with * "argTypes". */ public MethodInstance findMethod(TypeSystem_c.MethodMatcher matcher) throws SemanticException { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-method " + matcher.signature() + " in " + this); Context me = this; while (me != null && me.depType != null) { me = me.pop(); } if (me != null) { // Check for any method with the appropriate name. // If found, stop the search since it shadows any enclosing // classes method of the same name. if (this.currentClass() != null && ts.hasMethodNamed(this.currentClass(), matcher.name())) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-method " + matcher.signature() + " -> " + this.currentClass()); // Found a class that has a method of the right name. // Now need to check if the method is of the correct type. return ts.findMethod(this.currentClass(), matcher.container(this.currentClass())); } if (outer != null) { return outer.findMethod(matcher); } } throw new SemanticException("Method " + matcher.signature() + " not found."); } /** * Gets a local variable of a particular name. */ public X10LocalInstance findLocal(Name name) throws SemanticException { Context me = this; while (me != null && me.depType != null) me = me.pop(); if (me != null) { VarInstance<?> vi = findVariableSilent(name); if (vi instanceof X10LocalInstance) { return (X10LocalInstance) vi; } } throw new SemanticException("Local " + name + " not found."); } /** * Gets a field of a particular name. The lookupContext is the context in * which the field is being looked up, this is typically a lower context * than the class context in which the field is defined. This lower context * may have additional constraints, such as the type bounds for the * type parameters of the class, and the class invariant pushed in. This * information may be necessary to ensure that this field access is * consistent. */ public X10FieldInstance findField(Name name, Context lookupContext) throws SemanticException { VarInstance<?> vi = findVariableSilent(name, lookupContext); if (vi instanceof FieldInstance) { X10FieldInstance fi = (X10FieldInstance) vi; if (! ts.isAccessible(fi, this)) { throw new SemanticException("Field " + name + " not accessible."); } if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-field " + name + " -> " + fi); return fi; } throw new NoMemberException(NoMemberException.FIELD, "Field " + name + " not found."); } /** Looks up a local variable or field in the current scope. */ public VarInstance<?> findVariableSilent(Name name) { return findVariableSilent(name, this); } /** * See the comment for findField(Name, Context) for a discussion of the * importance of lookupContext. * @param name * @param lookupContext * @return */ public VarInstance<?> findVariableSilent(Name name, Context lookupContext) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-var " + name + " in " + this); VarInstance<?> vi = findVariableInThisScope(name, lookupContext); if (vi != null) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-var " + name + " -> " + vi); return vi; } if (outer != null) { return outer.findVariableSilent(name, lookupContext); } return null; } public X10ClassType findMethodContainerInThisScope(Name name) { if (isClass() && ts.hasMethodNamed(this.currentClass(), name)) { return this.type; } return null; } /** * Finds the class which added a method to the scope. Do not * search the current scope if depType !=null, since that does not contribute methods. * In fact, it should be an error for this method to be called when * deptype is true. */ public X10ClassType findMethodScope(Name name) throws SemanticException { X10ClassType result=null; if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-method-scope " + name + " in " + this); if (this.currentClass() != null && ts.hasMethodNamed(this.currentClass(), name)) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-method-scope " + name + " -> " + this.currentClass()); result= this.currentClass(); if (result == null) { // hack. This is null when this context is in a deptype, and the deptype // is not a classtype, and the field belongs to the outer type, e.g. // class Foo { int(:v=0) v; } ClassType r = type; result = pop().type; } return result; } if (outer != null) { result= outer.findMethodScope(name); if (result == null) { // hack. This is null when this context is in a deptype, and the deptype // is not a classtype, and the field belongs to the outer type, e.g. // class Foo { int(:v=0) v; } ClassType r = type; result = pop().type; } return result; } throw new SemanticException("Method " + name + " not found."); } /** * Finds the class which added a field to the scope. */ public X10ClassType findFieldScope(Name name) throws SemanticException { return findFieldScope(name, this); } public X10ClassType findFieldScope(Name name, Context lookupContext) throws SemanticException { VarInstance<?> vi = findVariableInThisScope(name, lookupContext); if (vi instanceof FieldInstance) { X10ClassType result = type; if (result != null) return result; if (inDepType()) result = pop().type; if (result != null) return result; if (supertypeDeclarationType() != null) result = supertypeDeclarationType().asType(); if (result != null) return result; } if (vi == null && pop() != null) { return pop().findFieldScope(name, lookupContext); } throw new SemanticException("Field " + name + " not found."); } public Type findInThisScope(Name name) { if (types != null) { Type t = types.get(name); if (t != null) return t; } if (isClass()) { if (! this.type.isAnonymous() && this.type.name().equals(name)) { return this.type; } else { ClassType container = this.currentClass(); Type t = findMemberTypeInThisScope(name, container); if (t != null) return t; } } if (inDepType()) { Type container = currentDepType(); Type t = findMemberTypeInThisScope(name, container); if (t != null) return t; } return null; } public Context findEnclosingCapturingScope() { Context c = popToCode(); while (c != null && !(c.currentCode() instanceof EnvironmentCapture)) { c = c.pop().popToCode(); } assert (c == null || c.isCode() || c.isAsync() || c.isAt()); if (c != null && c.currentCode() instanceof EnvironmentCapture) return c; return null; } private Type findMemberTypeInThisScope(Name name, Type container) { TypeSystem ts = typeSystem(); ClassDef currentClassDef = this.currentClassDef(); if (container instanceof MacroType) { MacroType mt = (MacroType) container; return findMemberTypeInThisScope(name, mt.definedType()); } if (container instanceof ConstrainedType) { ConstrainedType mt = (ConstrainedType) container; return findMemberTypeInThisScope(name, mt.baseType().get()); } try { Type t = ts.findMemberType(container, name, this); return t; } catch (SemanticException e) { } try { return ts.findTypeDef(container, name, Collections.<Type>emptyList(), Collections.<Type>emptyList(), this); } catch (SemanticException e) { } return null; } public VarInstance<?> findVariableInThisScope(Name name) { return findVariableInThisScope(name, this); } /** * See the comment for findField(Name, Context) for a discussion of the * importance of lookupContext. * @param name * @param lookupContext * @return */ public VarInstance<?> findVariableInThisScope(Name name, Context lookupContext) { //if (name.startsWith("val")) Report.report(1, "X10Context: searching for |" + name + " in " + this); if (depType == null) return superFindVariableInThisScope(name, lookupContext); VarInstance<?> vi = pop().findVariableInThisScope(name, lookupContext); if (vi instanceof LocalInstance) return vi; // otherwise it is a FieldInstance (might be a PropertyInstance, which is a FieldInstance) // See if the currentDepType has a field of this name. If so, that gets priority // and should be returned. The receiver must treat it as the reference // self.name. try { if (depType instanceof X10ClassType) { X10ClassType dep = (X10ClassType) this.depType; TypeSystem ts = typeSystem(); X10FieldInstance myVi = ts.findField(dep, dep, name, this); if (myVi != null) { return myVi; } } } catch (SemanticException e) { } return vi; } VarInstance<?> superFindVariableInThisScope(Name name, Context lookupContext) { VarInstance<?> vi = null; if (vars != null) { vi = (VarInstance<?>) vars.get(name); } if (vi == null && isClass()) { try { return ts.findField(this.currentClass(), this.currentClass(), name, lookupContext); } catch (SemanticException e) { return null;// todo: we loose the error message! e.g., "Field XXX is ambiguous; it is defined in both ..." } } return vi; } public X10FieldInstance findProperty(Name name) throws SemanticException { X10FieldInstance pi = null; FieldInstance fi = findField(name, this); if (fi instanceof X10FieldInstance) { pi = (X10FieldInstance) pi; } return pi; } public X10ClassType findPropertyScope(Name name) throws SemanticException { return findFieldScope(name, this); } /** * Finds the definition of a particular type. */ public List<Type> find(Matcher<Type> matcher) throws SemanticException { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find-type " + matcher.signature() + " in " + this); if (isOuter()) return ts.systemResolver().find(QName.make(null, matcher.name())); // NOTE: looking up a short name if (isSource()) return it.find(matcher); Type type = findInThisScope(matcher); if (type != null) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "find " + matcher.signature() + " -> " + type); return CollectionUtil.<Type>list(type); } if (outer != null) { return outer.find(matcher); } throw new SemanticException("Type " + matcher.signature() + " not found."); } // -------- Section: Pop and Push methods -------------------------- /** Pop the context. */ public Context pop() { return outer; } protected Context push() { Context v = this.shallowCopy(); v.outer = this; v.types = null; v.vars = null; v.depType = null; v.name = null; v.currentConstraint=null; v.cStack= null; v.cStackUsedUp=false; // v.varWhoseTypeIsBeingElaborated = null; // Do not set the inXXXCode attributes to false, inherit them from parent. return v; } public Context pushClockedContext() { Context v = pushBlock(); v.isClocked = true; return v; } /** Enter the scope of a source file. */ public Context pushSource(ImportTable it) { assert (depType == null); if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push source"); Context v = push(); v.kind = SOURCE; v.it = it; v.inCode = false; v.staticContext = false; return v; } /** Enter a static scope. In general, this is only used for * explicit constructor calls; static methods, initializers of static * fields and static initializers are generally handled by pushCode(). */ public Context pushStatic() { assert (depType == null); if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push static"); Context v; if (isCode() || isAsync() || isAt()) { v = pushBlock(); } else { v = push(); } v.staticContext = true; return v; } /** Enter the scope of a method, constructor, field initializer, or closure. */ public Context pushCode(X10CodeDef ci) { assert (depType == null); if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push code " + ci.position()); Context v = push(); v.kind = CODE; v.code = ci; v.inCode = true; v.staticContext = ci.staticContext(); return v; } /** Enter the scope of an async. */ public Context pushAsync(X10CodeDef ci) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push async " + ci.position()); Context v = push(); v.kind = ASYNC; v.code = ci; v.inCode = true; v.staticContext = ci.staticContext(); return v; } /** Enter the scope of an at. */ public Context pushAt(X10CodeDef ci) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push at " + ci.position()); Context v = push(); v.kind = AT; v.code = ci; v.inCode = true; v.staticContext = ci.staticContext(); return v; } public Context pushAtomicBlock() { assert (depType == null); if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push atomic block"); Context v = pushBlock(); return v; } public Context pushAssignment() { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push assignment"); assert (depType == null); Context v = pushBlock(); v.setInAssignment(); return v; } public Context pushSwitchType(Type st) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push switch type: "+st); assert (depType == null); Context v = pushBlock(); v.setSwitchType(st); return v; } /** Enter the scope of a block. */ public Context pushBlock() { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push block"); Context v = push(); v.kind = BLOCK; return v; } /** * In a context marked as SpecialAsQualifiedVar a qualified this, * A.this is returned as a * QualifiedVar instead of the this var in the appropriate outer * context. */ public Context pushSpecialAsQualifiedVar() { Context c = pushBlock(); c.specialAsQualifiedVar=true; return c; } public Context pushFinishScope(boolean isClocked) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push finish scope"); Context v = pushBlock(); v.x10Kind = X10Kind.Finish; v.inClockedFinishScope = isClocked; v.currentFinishPlaceTerm = v.currentPlaceTerm; return v; } /** * Pushes on a deptype. Treat this as pushing a class. */ public Context pushDepType(polyglot.types.Ref<? extends polyglot.types.Type> ref) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push dep type"); Context v = pushBlock(); v.depType = ref; v.inCode = false; return v; } /** * Pushes on a class scoping. * @param classScope The class whose scope is being entered. This is * the object associated with the class declaration and is returned by * currentClassScope. This is a mutable class type since for some * passes (e.g., addMembers), the object returned by currentClassScope * is modified. * @param type The type to be returned by currentClass(). For JL, this * type is the same as classScope. For other languages, it may differ * since currentClassScope might not represent a type. * @return A new context with a new scope and which maps the short name * of type to type. */ public Context pushClass(X10ClassDef classScope, X10ClassType type) { assert (depType == null); Context result = this; // Pushing a nested (non-inner) class should be done in a static context if (classScope.isMember() && classScope.flags().isStatic()) { result = pushStatic(); } if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push class " + classScope + " " + classScope.position()); Context v = result.push(); v.kind = CLASS; v.scope = classScope; v.type = type; v.inCode = false; v.staticContext = false; if (! type.isAnonymous()) { v.addNamed(type); } return v; } public Context pushSuperTypeDeclaration(X10ClassDef type) { if (reporter.should_report(TOPICS, 4)) reporter.report(4, "push super type"); Context v = pushBlock(); v.inSuperOf = type; v.inCode = false; return v; } public Context pushAdditionalConstraint(CConstraint env, Position pos) throws SemanticException { // Now push the newly computed Gamma Context xc = pushBlock(); CConstraint c = xc.currentConstraint(); if (c == null) { c = env; } else { c = c.copy(); c.addIn(env); // c.addIn(xc.constraintProjection(c)); if (! c.consistent()) throw new Errors.InconsistentContext(env, pos); } xc.setCurrentConstraint(c); // xc.setCurrentTypeConstraint(tenv); return xc; } public Context popToCode() { Context c = this; while (c != null && !c.isCode() && !c.isAsync() && !c.isAt()) { c = c.pop(); } return c; } // -------------Section: Setter methods, updating current context-------- public void setName(String n) { name = n; } public void restoreAnonObjectScope(boolean s) { inAnonObjectScope=s;} public void setInAssignment() { inAssignment = true;} public void setLoopHeader() { inLoopHeader = true; } public void setAnnotation() { inAnnotation = true; } public void setAnonObjectScope() { inAnonObjectScope = true;} public void clearAnnotation() { inAnnotation = false; } public void setCurrentTypeConstraint(Ref<TypeConstraint> c) { currentTypeConstraint = c; } public void setTypeConstraintWithContextTerms(TypeConstraint c) { TypeConstraint equals = new TypeConstraint(); //if (currentTypeConstraint!=null) equals.addIn(currentTypeConstraint.get()); equals.addIn(c); if (currentTypeConstraint != null) { equals.addIn(currentTypeConstraint.get()); } setCurrentTypeConstraint(Types.ref(equals)); } public void setTypeConstraintWithContextTerms(final Ref<TypeConstraint> c) { // System.out.println("Context (#" + hashCode() + ") setting with" + c); if (currentTypeConstraint == null) { setCurrentTypeConstraint(c); return; } // Else add the lazy conjunction of c and currentTypeConstraint. final Ref<TypeConstraint> old = currentTypeConstraint; final LazyRef<TypeConstraint> ref = new LazyRef_c<TypeConstraint>(new TypeConstraint()); Runnable runnable = new Runnable() { public void run() { TypeConstraint result = new TypeConstraint(); result.addIn(c.get()); result.addIn(old.get()); ref.update(result); } public String toString() { return "and(" + old +"," + c + ")"; } }; ref.setResolver(runnable); setCurrentTypeConstraint(ref); /*TypeConstraint equals = new TypeConstraint(); //if (currentTypeConstraint!=null) equals.addIn(currentTypeConstraint.get()); equals.addIn(c); if (currentTypeConstraint != null) { equals.addIn(currentTypeConstraint.get()); } setCurrentTypeConstraint(Types.ref(equals));*/ } public void setTypeConstraint(TypeConstraint c) { setTypeConstraint(c.terms()); } public void setTypeConstraint(Collection<SubtypeConstraint> d) { TypeConstraint equals = new TypeConstraint(); //if (currentTypeConstraint!=null) equals.addIn(currentTypeConstraint.get()); equals.addTerms(d); // if (currentTypeConstraint != null) { // equals.addIn(currentTypeConstraint.get()); // } setCurrentTypeConstraint(Types.ref(equals)); } public void setPlace(XConstrainedTerm t) { //assert t!= null; //X10Context cxt = (X10Context) SUPER_pushBlock(); currentPlaceTerm = t; } public void setSwitchType(Type st) { switchType = st; } public void setCollectingFinishScope(Type t) { assert t!=null; //X10Context cxt = (X10Context) SUPER_pushBlock(); currentCollectingFinishType =t; //return cxt; } public X10CodeDef definingCodeDef(Name name) { if ((isBlock() || isCode() || isAsync() || isAt()) && (findVariableInThisScope(name) != null || findInThisScope(name) != null)) { return currentCode(); } return pop().definingCodeDef(name); } /** * Adds a symbol to the current scoping level. */ public void addVariable(VarInstance<?> vi) { if (reporter.should_report(TOPICS, 3)) reporter.report(3, "Adding " + vi.name() + " to context."); addVariableToThisScope(vi); } /** * Adds a named type object to the current scoping level. */ public void addNamed(Type t) { assert (depType == null); if (reporter.should_report(TOPICS, 3)) reporter.report(3, "Adding type " + t.name() + " to context."); addNamedToThisScope(t); } public void addVariableToThisScope(VarInstance<?> var) { if (vars == null) vars = CollectionFactory.newHashMap(); vars.put(var.name(), var); } public void recordCapturedVariable(VarInstance<? extends VarDef> vi) { Context c = findEnclosingCapturingScope(); if (c == null) return; VarInstance<?> o = c.pop().findVariableSilent(vi.name()); if (vi == o || (o != null && vi.def() == o.def())) ((EnvironmentCapture) c.currentCode()).addCapturedVariable(vi); } public void setVarWhoseTypeIsBeingElaborated(VarDef var) { varWhoseTypeIsBeingElaborated = var; } public XConstrainedTerm currentThisPlace() { return thisPlace; } public void addConstraint(Ref<CConstraint> c) { if (cStack==null) cStack = new Stack<Ref<CConstraint>>(); cStack.push(c); } public void setCurrentConstraint(CConstraint c) { currentConstraint = c; } /** * Add the real clause for the current class/struct to the current constraint. * If force is true, then force the computation of the real clause if it is not * yet known. * @param force */ /* public void addInClassInvariantIfNeeded(boolean force) { CodeDef cd = currentCode(); if (cd !=null && cd instanceof MethodDef) { MethodDef md = (MethodDef) cd; if (!md.flags().isStatic()) { // this call occurs in the body of an instance method for T. // Pick up the real clause for T -- that information is known // statically about "this" Ref<? extends ContainerType> container = md.container(); if (container.known()) { X10ClassType type = (X10ClassType) Types.get(container); Ref<CConstraint> rc = type.x10Def().realClause(); addConstraint(rc); Ref<TypeConstraint> tc = type.x10Def().typeBounds(); if (tc != null) { setCurrentTypeConstraint(tc); } if (rc != null && (force || rc.known())) { // do not trigger prematurely TypeSystem ts = typeSystem(); if (! ts.isUnknown(type)) { CConstraint env = rc.get(); if (env !=null) { XVar containerThis = thisVar(); if (containerThis !=null) env=env.instantiateSelf(containerThis); if (! env.valid()) { if (currentConstraint == null) currentConstraint=env; else currentConstraint.addIn(env); } } } } } } } } */ // todo: we have both freeze and shallowCopy (that used to be called copy). why? get rid of shallowCopy. public Context shallowCopy() { try { Context res = (Context) super.clone(); res.x10Kind = X10Kind.None; return res; // it's a shallow copy cause we do not copy types or vars or deep copy the outer. that's what we do in freeze. } catch (CloneNotSupportedException e) { throw new InternalCompilerError("Java clone() weirdness."); } } /** Deep copy the context so that it can be saved away. */ public Context freeze() { if (true) return this; // todo: is freezing actually needed anymore? (the guard in closures might be a problem...) Context c = this.shallowCopy(); c.types = types != null ? CollectionFactory.newHashMap(types) : null; c.vars = vars != null ? CollectionFactory.newHashMap(vars) : null; c.outer = outer != null ? outer.freeze() : null; return c; } static <T> String stackToString(Stack<T> stack) { String str = "|"; for (T t : stack) { str += t.toString() + " "; } return str+"|"; } private String indentedString(String prefix) { return "(#" + hashCode() + " " + kind + (name ==null ? "" : " " + name) + " "+ mapsToString() + (depType == null ? "" : "\n " + prefix + " depType=|" + depType + "|") + (cStack == null || cStack.isEmpty() ? "" : ("\n" + prefix + " cStack " + (cStackUsedUp? "(used)" : "") + "=" + stackToString(cStack))) + (currentConstraint ==null ? "" : "\n " + prefix + " constraint=|" + currentConstraint+"|") + (currentTypeConstraint ==null ? "" : "\n " + prefix + " type constraint=|" + currentTypeConstraint+"|") + (currentPlaceTerm == null ? "" : "\n " + prefix + " place=" + currentPlaceTerm) + (thisPlace == null? "" : "\n " + prefix + " home(this)=" + thisPlace.toString()) + (outer == null? "" : " \n " + prefix + " outer=" + outer.indentedString(prefix + " ") + ")") +")"; } public String toString() { return indentedString(""); } }