/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * (C) Copyright IBM Corporation 2006-2010. */ package x10.ast; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import polyglot.ast.Expr; import polyglot.ast.FlagsNode; import polyglot.ast.Formal; import polyglot.ast.Formal_c; import polyglot.ast.Id; import polyglot.ast.Id_c; import polyglot.ast.IntLit; import polyglot.ast.Local; import polyglot.ast.LocalDecl; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Stmt; import polyglot.ast.TypeNode; import polyglot.types.ClassDef; import polyglot.types.Context; import polyglot.types.Flags; import polyglot.types.LazyRef; import polyglot.types.LocalDef; import polyglot.types.LocalInstance; import polyglot.types.Ref; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.UnknownType; import polyglot.util.CodeWriter; import polyglot.util.CollectionUtil; import polyglot.util.Position; import polyglot.util.TypedList; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import polyglot.visit.PrettyPrinter; import polyglot.visit.Translator; import polyglot.visit.TypeBuilder; import polyglot.visit.TypeCheckPreparer; import x10.errors.Errors; import x10.extension.X10Del; import x10.types.X10LocalDef; import polyglot.types.Types; import polyglot.types.TypeSystem_c; import x10.types.ConstrainedType; import x10.types.constraints.XConstrainedTerm; import x10.visit.X10PrettyPrinterVisitor; import x10.constraint.XTerm; import x10.constraint.XLit; /** * An immutable representation of an X10Formal, which is of the form * Flag Type VarDeclaratorId * Recall that a VarDeclaratorId may have additional variable bindings. * @author vj Jan 23, 2005 * @author igor Jan 13, 2006 */ public class X10Formal_c extends Formal_c implements X10Formal { /* Invariant: vars != null */ protected List<Formal> vars; // e.g., when exploding a point: def foo(p[i,j]:Point) boolean unnamed; public X10Formal_c(Position pos, FlagsNode flags, TypeNode type, Id name, List<Formal> vars, boolean unnamed) { super(pos, flags, type, name == null ? new Id_c(pos, X10PrettyPrinterVisitor.getId()) : name); if (vars == null) vars = Collections.<Formal>emptyList(); this.vars = TypedList.copyAndCheck(vars, Formal.class, true); this.unnamed = unnamed; assert vars != null; } public Node visitChildren(NodeVisitor v) { X10Formal_c n = (X10Formal_c) super.visitChildren(v); List<Formal> l = visitList(vars, v); if (!CollectionUtil.allEqual(l, this.vars)) { if (n == this) n = (X10Formal_c) copy(); n.vars = TypedList.copyAndCheck(l, Formal.class, true); } return n; } public List<Formal> vars() { return vars; } public X10Formal vars(List<Formal> vars) { X10Formal_c n = (X10Formal_c) super.copy(); n.vars = vars; return n; } public boolean isUnnamed() { return unnamed; } /** Get the local instances of the bound variables. */ public LocalDef[] localInstances() { LocalDef[] lis = new LocalDef[vars.size()]; for (int i = 0; i < vars.size(); i++) { lis[i] = vars.get(i).localDef(); } return lis; } public X10Formal flags(FlagsNode fn) { return (X10Formal) super.flags(fn); } public X10Formal type(TypeNode type) { return (X10Formal) super.type(type); } public X10Formal name(Id name) { return (X10Formal) super.name(name); } public X10Formal localDef(LocalDef li) { return (X10Formal) super.localDef(li); } public X10LocalDef localDef() { return (X10LocalDef) super.localDef(); } /* (non-Javadoc) * @see polyglot.ext.jl.ast.Formal#addDecls() */ public void addDecls(Context c) { c.addVariable(li.asInstance()); for (Iterator<Formal> j = this.vars().iterator(); j.hasNext(); ) { Formal fj = (Formal) j.next(); fj.addDecls(c); } } private String translateVars() { StringBuffer sb = new StringBuffer(); if (! vars.isEmpty()) { sb.append("["); for (int i = 0; i < vars.size(); i++) sb.append(i > 0 ? "," : "").append(vars.get(i).name().id()); sb.append("]"); } return sb.toString(); } @Override public Node buildTypes(TypeBuilder tb) { X10Formal_c n = (X10Formal_c) super.buildTypes(tb); X10LocalDef fi = (X10LocalDef) n.localDef(); if (isUnnamed()) { fi.setUnnamed(); } List<AnnotationNode> as = ((X10Del) n.del()).annotations(); if (as != null) { List<Ref<? extends Type>> ats = new ArrayList<Ref<? extends Type>>(as.size()); for (AnnotationNode an : as) { ats.add(an.annotationType().typeRef()); } fi.setDefAnnotations(ats); } return n; } public Node setResolverOverride(final Node parent, TypeCheckPreparer v) { final TypeSystem ts = (TypeSystem) v.typeSystem(); final Context context = (Context) v.context(); final ClassDef currClassDef = context.currentClassDef(); Formal f = (Formal) this; X10LocalDef li = (X10LocalDef) f.localDef(); if (f.type() instanceof UnknownTypeNode && parent instanceof Formal) { // We infer the types of exploded formals final UnknownTypeNode tn = (UnknownTypeNode) f.type(); final LazyRef<Type> r = (LazyRef<Type>) tn.typeRef(); r.setResolver(new Runnable() { public void run() { Formal ff = (Formal) parent; Type containerType = ff.type().type(); final Type indexType; if (ts.isArray(containerType)) { indexType = TypeSystem_c.getArrayComponentType(containerType); } else { // must be a Point or we had complained when typeChecking the parent. indexType = ts.Int(); } r.update(indexType); // It used to be: ts.unknownType(tn.position()), however now I complain in checkExplodedVars if the type of the parent is not Point nor Array, so no need to complain we cannot infer the type of the components. } }); } return null; } @Override public Type declType() { return type.type(); } @Override public Node typeCheckOverride(Node parent, ContextVisitor tc) { NodeVisitor childtc = tc.enter(parent, this); XConstrainedTerm pt = ((Context) ((ContextVisitor) childtc).context()).currentPlaceTerm(); if (pt != null) ((X10LocalDef) localDef()).setPlaceTerm(pt.term()); return null; } @Override public Node typeCheck(ContextVisitor tc) { // Check if the variable is multiply defined. Context c = tc.context(); LocalInstance outerLocal = null; try { outerLocal = c.findLocal(li.name()); } catch (SemanticException e) { // not found, so not multiply defined } if (outerLocal != null && ! li.equals(outerLocal.def()) && c.isLocal(li.name())) { // todo: give me a test case that shows this error? Errors.issue(tc.job(), new Errors.LocalVariableMultiplyDefined(name.id(), outerLocal.position(), position())); } TypeSystem ts = tc.typeSystem(); TypeNode typeNode = this.type(); final Type myType = typeNode.type(); try { Types.checkMissingParameters(typeNode); } catch (SemanticException e) { Errors.issue(tc.job(), e, this); } try { ts.checkLocalFlags(flags().flags()); } catch (SemanticException e) { Errors.issue(tc.job(), e, this); } if (typeNode instanceof UnknownTypeNode || myType instanceof UnknownType) { Errors.issue(tc.job(), new Errors.CannotInferTypeForFormalParameter(this.name(), position())); } else if (myType.isVoid()) Errors.issue(tc.job(), new Errors.FormalParameterCannotHaveType(myType, position())); else { checkExplodedVars(vars.size(), (Ref<Type>)typeNode.typeRef(), position(), tc); } return this; } public static void checkExplodedVars(int num, Ref<Type> ref, Position pos, ContextVisitor tc) { Context c = tc.context(); TypeSystem ts = tc.typeSystem(); final Type myType = ref.get(); if (num>0) { // check the type is a subtype of Point/Array, and that it's rank is vars.size() final boolean isArray = ts.isArray(myType); if (!ts.isSubtype(myType, ts.Point(), c) && !isArray) Errors.issue(tc.job(), new Errors.OnlyTypePointOrArrayCanBeExploded(myType, pos)); else { // make sure there is an init expr ConstrainedType cType = Types.toConstrainedType(myType); boolean okOrError = false; if (isArray) { XTerm rank = cType.rank(c); XTerm size = cType.size(c); if (rank instanceof XLit && size instanceof XLit) { okOrError = true; int r = (Integer) ((XLit) rank).val(); int s = (Integer) ((XLit) size).val(); if (r!=1 || s!=num) { if (r!=1) Errors.issue(tc.job(), new SemanticException("The rank of the exploded Array is "+r+" but it should be 1", pos)); else Errors.issue(tc.job(), new SemanticException("The size of the exploded Array is "+s+" but it should be "+num, pos)); } } } else { XTerm rank = cType.rank(c); if (rank instanceof XLit) { okOrError = true; int r = (Integer) ((XLit) rank).val(); if (r!=num) { Errors.issue(tc.job(), new SemanticException("The rank of the exploded Point is "+r+" but it should be "+num, pos)); } } } if (!okOrError) { if (isArray) { if (false) { // Just adding cType = cType.addRank(1).addSize(num); // is not enough, because it gives the type: //x10.array.Array[x10.lang.Int]{self.x10.array.Array#rank==1, self.x10.array.Array#size==2} // and we should get the type: //x10.array.Array[x10.lang.Int]{self.x10.array.Array#region.x10.array.Region#rank==1, self.x10.array.Array#region.x10.array.Region#rect==true, self.x10.array.Array#region.x10.array.Region#zeroBased==true, self.x10.array.Array#region.x10.array.Region#rail==true, self.x10.array.Array#rank==1, self.x10.array.Array#rect==true, self.x10.array.Array#zeroBased==true, self.x10.array.Array#rail==true, self.x10.array.Array#size==2, self!=null} // you can test it with this code: //{ val p[i,j]: Array[Int] = new Array[Int](2); } // ShouldNotBeERR: Message: Semantic Error: Method operator()(i0: x10.lang.Int){x10.array.Array#this.x10.array.Array#rank==1}[] in x10.array.Array[x10.lang.Int]{self.x10.array.Array#rank==1, self==p, p.x10.array.Array#size==2} cannot be called with arguments (x10.lang.Int{self==1}); Call invalid; calling environment does not entail the method guard. } else Errors.issue(tc.job(), new Errors.ArrayExplosionError(num, pos)); } else { cType = cType.addRank(num); } if (cType.constraint().get().consistent()) { ref.update(cType); } else { Errors.issue(tc.job(), new SemanticException("The type after adding the rank is inconsistent. The type before is "+myType+" and after adding {rank=="+(isArray ? "1,size=":"")+num+"} is "+cType, pos)); } } } } } public String toString() { StringBuffer sb = new StringBuffer(); sb.append(flags.flags().clearFinal().translate()); boolean noheader = unnamed && vars.isEmpty(); if (!noheader && !flags.flags().isFinal()) { sb.append("var "); } if (!unnamed) { sb.append(name); } if (! vars.isEmpty()) { sb.append("["); for (int i = 0; i < vars.size(); i++) sb.append(i > 0 ? "," : "").append(vars.get(i)); sb.append("]"); } if (!noheader) { sb.append(": "); } sb.append(type); return sb.toString(); } /* (non-Javadoc) * @see x10.ast.X10Formal#hasExplodedVars() */ public boolean hasExplodedVars() { return ! vars.isEmpty(); } /** * Create a local variable declaration for an exploded var, * at the given type, name and with the given initializer. * The exploded variable is implicitly final. * * @param nf * @param pos * @param type * @param name * @param li * @param init * @return */ protected static LocalDecl makeLocalDecl(NodeFactory nf, Position pos, FlagsNode flags, TypeNode type, Id name, LocalDef li, Expr init) { /* boolean allCapitals = name.equals(name.id().toUpperCase()); // vj: disable until we have more support for declarative programming in X10. Flags f = (false || allCapitals ? flags.set(Flags.FINAL) : flags); */ return nf.LocalDecl(pos, flags.flags(flags.flags().set(Flags.FINAL)), type, name, init) .localDef(li); } /** * Return the initialization statements for the exploding variables. * * @param nf * @param ts * @return * @throws SemanticException */ public List<Stmt> explode(ContextVisitor tc) throws SemanticException { return explode(tc, name(), position(), flags(), vars, localDef()); } /* (non-Javadoc) * @see x10.ast.X10Formal#explode(polyglot.ast.NodeFactory, polyglot.types.TypeSystem) */ public List<Stmt> explode(ContextVisitor tc, Stmt s) throws SemanticException { List<Stmt> init = this.explode(tc); if (s != null) init.add(s); return init; } public List<Stmt> explode(ContextVisitor tc, List<Stmt> s, boolean prepend) throws SemanticException { List<Stmt> init = this.explode(tc); if (s != null) { if (prepend) init.addAll(s); else init.addAll(0, s); } return init; } /** * Return the initialization statements for the exploding variables. * * @param nf * @param ts * @param name * @param pos * @param flags * @param vars * @param lis * @return */ private static List<Stmt> explode(ContextVisitor tc, Id name, Position pos, FlagsNode flags, List<Formal> vars, LocalDef bli) throws SemanticException { TypeSystem ts = tc.typeSystem(); NodeFactory nf = tc.nodeFactory(); if (vars == null || vars.isEmpty()) return null; NodeFactory x10nf = (NodeFactory) nf; List<Stmt> stmts = new TypedList<Stmt>(new ArrayList<Stmt>(vars.size()), Stmt.class, false); Local arrayBase =nf.Local(pos, name); if (bli != null) arrayBase = (Local) arrayBase.localInstance(bli.asInstance()).type(bli.asInstance().type()); for (int i = 0; i < vars.size(); i++) { // int arglist(i) = name[i]; Formal var = vars.get(i); Expr index = x10nf.IntLit(var.position(), IntLit.INT, i).type(ts.Int()); Expr init = x10nf.ClosureCall(var.position(), arrayBase, Collections.singletonList(index)); if (bli != null) { init = (Expr) init.disambiguate(tc).typeCheck(tc).checkConstants(tc); } Stmt d = makeLocalDecl(nf, var.position(), flags, var.type(), var.name(), var.localDef(), init); stmts.add(d); } return stmts; } // public X10Formal_c pickUpTypeFromTypeNode(TypeChecker tc) { // X10LocalInstance xli = (X10LocalInstance) li; // X10Type newType = (X10Type) type.type(); // xli.setType(newType); // xli.setSelfClauseIfFinal(); // return (X10Formal_c) type(type().type(xli.type())); // } public Context enterChildScope(Node child, Context c) { Context cxt = (Context) c; if (child == this.type) { TypeSystem ts = c.typeSystem(); LocalDef li = localDef(); cxt = (Context) cxt.shallowCopy(); cxt.addVariable(li.asInstance()); cxt.setVarWhoseTypeIsBeingElaborated(li); } Context cc = super.enterChildScope(child, cxt); return cc; } /** * Return the initialization statements for the exploding variables * early. * * @param nf * @param ts * @param name * @param pos * @param flags * @param vars * @return * @throws SemanticException */ public static List<Stmt> explode(ContextVisitor tc, Id name, Position pos, FlagsNode flags, List<Formal> vars) throws SemanticException { return explode(tc, name, pos, flags, vars, null); } /** Write the formal to an output file. */ public void prettyPrint(CodeWriter w, PrettyPrinter tr) { Boolean f = flags.flags().isFinal(); Flags fs = flags.flags().clearFinal(); if (!f) w.write("var "); w.write(fs.translate()); tr.print(this, name, w); // todo: should we use translateVars() here? w.write(":"); print(type, w, tr); } @Override public void translate(CodeWriter w, Translator tr) { super.prettyPrint(w, tr); // todo: why is it calling super and not the newly defined prettyPrint? } }