/* * 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.Iterator; import java.util.List; import java.util.Collections; import polyglot.ast.ArrayInit; import polyglot.ast.Expr; import polyglot.ast.FlagsNode; import polyglot.ast.Id; import polyglot.ast.LocalDecl_c; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.TypeCheckFragmentGoal; import polyglot.ast.TypeNode; import polyglot.frontend.Globals; import polyglot.types.Context; import polyglot.types.FieldDef; import polyglot.types.Flags; import polyglot.types.LazyRef; import polyglot.types.LocalDef; import polyglot.types.Ref; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.Types; import polyglot.util.CodeWriter; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import polyglot.visit.PrettyPrinter; import polyglot.visit.TypeBuilder; import polyglot.visit.TypeCheckPreparer; import polyglot.visit.TypeChecker; import x10.errors.Errors; import x10.extension.X10Del; import x10.extension.X10Del_c; import x10.extension.X10Ext; import x10.types.X10ClassType; import polyglot.types.Context; import x10.types.X10FieldDef; import x10.types.X10LocalDef; import x10.types.X10ParsedClassType_c; import polyglot.types.TypeSystem; import x10.types.checker.Converter; import x10.types.checker.PlaceChecker; import x10.types.constraints.XConstrainedTerm; import x10.visit.X10PrettyPrinterVisitor; import x10.visit.X10TypeChecker; public class X10LocalDecl_c extends LocalDecl_c implements X10VarDecl { TypeNode hasType; private final List<Id> exploded; public X10LocalDecl_c(NodeFactory nf, Position pos, FlagsNode flags, TypeNode type, Id name, Expr init, List<Id> exploded) { super(pos, flags, type instanceof HasTypeNode_c ? nf.UnknownTypeNode(type.position()) : type, name, init); this.exploded = exploded==null ? Collections.EMPTY_LIST : exploded; if (type instanceof HasTypeNode_c) hasType = ((HasTypeNode_c) type).typeNode(); } /** * This method is called by TypeChecker after the node has been type-checked to give this node a chance * to update the current scope with declarations introduced by this node. * * (Added this delegating method here merely to record how this piece of functionality works. */ @Override public void addDecls(Context c) { if (li!=null) // if we had errors in type checking, li might be null (e.g., "var x = ...") super.addDecls(c); } @Override public Node buildTypes(TypeBuilder tb) { X10LocalDecl_c n = this; if (type instanceof UnknownTypeNode) { if (init == null) Errors.issue(tb.job(), new Errors.CannotInferVariableType(name().id(), position())); // TODO: For now, since there can be more than once assignment to a mutable variable, // do not allow mutable variable types to be inferred. // This can be fixed later for local variables by doing better // type inference. This should never be done for fields. if (! flags.flags().isFinal()) Errors.issue(tb.job(), new Errors.CannotInferTypeofMutalVariable(position())); } // This installs a LocalDef n = (X10LocalDecl_c) super.buildTypes(tb); X10LocalDef fi = (X10LocalDef) n.localDef(); 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 NodeVisitor typeCheckEnter(TypeChecker tc) { return super.typeCheckEnter(tc); } /** Reconstruct the declaration. */ protected LocalDecl_c reconstruct(FlagsNode flags, TypeNode type, TypeNode htn, Id name, Expr init) { if (this.flags != flags || this.type != type || this.hasType != htn || this.name != name || this.init != init) { X10LocalDecl_c n = (X10LocalDecl_c) reconstruct(flags, type, name, init); n.hasType = htn; return n; } return this; } protected X10LocalDecl_c hasType(TypeNode hasType) { if (this.hasType != hasType) { X10LocalDecl_c n = (X10LocalDecl_c) copy(); n.hasType = hasType; return n; } return this; } /** * If the initializer is not null, and the type is Unknown (e.g. because the type was not explicitly * specified by the programmer, and hence must be inferred), then update the type to be the type * of the initializer. */ @Override public Node typeCheckOverride(Node parent, ContextVisitor tc) { NodeVisitor childtc = tc.enter(parent, this); XConstrainedTerm pt = ((Context) tc.context()).currentPlaceTerm(); if (pt != null && pt.term() != null) ((X10LocalDef) localDef()).setPlaceTerm(pt.term()); if (type() instanceof UnknownTypeNode) { Expr init = (Expr) this.visitChild(init(), childtc); if (init != null) { Type t = init.type(); if (t instanceof X10ClassType) { X10ClassType ct = (X10ClassType) t; if (ct.isAnonymous()) { if (ct.interfaces().size() > 0) t = ct.interfaces().get(0); else t = ct.superClass(); } } LazyRef<Type> r = (LazyRef<Type>) type().typeRef(); r.update(t); FlagsNode flags = (FlagsNode) this.visitChild(flags(), childtc); Id name = (Id) this.visitChild(name(), childtc); TypeNode tn = (TypeNode) this.visitChild(type(), childtc); TypeNode htn = null; if (hasType != null) { htn = (TypeNode) visitChild(hasType, childtc); boolean checkSubType = true; try { Types.checkMissingParameters(htn); } catch (SemanticException e) { Errors.issue(tc.job(), e, htn); checkSubType = false; } if (checkSubType && ! tc.typeSystem().isSubtype(type().type(), htn.type(), tc.context())) { Context xc = (Context) enterChildScope(init, tc.context()); Expr newInit = Converter.attemptCoercion(tc.context(xc), init, htn.type()); if (newInit == null) { Errors.issue(tc.job(), new Errors.TypeIsNotASubtypeOfTypeBound(type().type(), htn.type(), position())); } else { init = newInit; r.update(newInit.type()); } } } Node n = tc.leave(parent, this, reconstruct(flags, tn, htn, name, init), childtc); List<AnnotationNode> oldAnnotations = ((X10Ext) ext()).annotations(); if (oldAnnotations == null || oldAnnotations.isEmpty()) { return n; } List<AnnotationNode> newAnnotations = node().visitList(oldAnnotations, childtc); if (! CollectionUtil.allEqual(oldAnnotations, newAnnotations)) { return ((X10Del) n.del()).annotations(newAnnotations); } return n; } } return null; } /** * At this point, the type of the declaration should be known. If the type was not specified * then typeCheckOverride would have set it from the type of the initializer. */ @Override public Node typeCheck(ContextVisitor tc) { final TypeNode typeNode = type(); Type type = typeNode.type(); try { Types.checkMissingParameters(typeNode); } catch (SemanticException e) { Errors.issue(tc.job(), e, typeNode); } // Replace here by PlaceTerm because this local variable may be referenced // later by code that has been place-shifted, and will have a different // interpretation of here. type = PlaceChecker.ReplaceHereByPlaceTerm(type, (Context) tc.context()); Ref<Type> r = (Ref<Type>) typeNode.typeRef(); r.update(type); if (type.isVoid()) { SemanticException e = new Errors.LocalVariableCannotHaveType(this.type().type(), position()); Errors.issue(tc.job(), e); } else { X10Formal_c.checkExplodedVars(exploded.size(), (Ref<Type>)this.type().typeRef(), position(), tc); } TypeSystem ts = tc.typeSystem(); try { ts.checkLocalFlags(flags.flags()); } catch (SemanticException e) { Errors.issue(tc.job(), e, this); } X10LocalDecl_c n = (X10LocalDecl_c) this.type(tc.nodeFactory().CanonicalTypeNode(typeNode.position(), type)); // Need to check that the initializer is a subtype of the (declared or inferred) type of the variable, // or can be implicitly coerced to the type. if (n.init != null) { Expr newInit = Converter.attemptCoercion(tc, n.init, type); if (newInit != null) return n.init(newInit); Errors.issue(tc.job(), Errors.CannotAssign.make(n.init, type, tc, n.init.position()), n); } return n; } public String shortToString() { return "<X10LocalDecl_c #" + hashCode() // todo: using hashCode leads to non-determinism in the output of the compiler // + " flags= |" + flags + "|" +(type() == null ? "" : " <TypeNode #" + type().hashCode()+"type=" + type().type() + ">") + " name=|" + name().id() + "|" + (init() == null ? "" : " <Expr #" + init().hashCode() +">") + (localDef() == null ? "" : " <LocalInstance #" + localDef().hashCode() +">") + ">"; } /** * Add the declaration of the variable as we enter the scope of the * intializer. * In Java you can write this code: * int i= (i=2)+4; * But after XTENLANG-2387 we disallow it in X10: * var i:Int = (i=2)+4; // ERR! */ public Context enterChildScope(Node child, Context c) { Context oldC=c; if (child == init) { c = c.pushBlock(); // addDecls(c); - this will allow writing code like a local: int i= (i=2); } if (child == this.type || child == this.hasType) { if (oldC==c) c=c.pushBlock(); LocalDef li = localDef(); c.addVariable(li.asInstance()); c.setVarWhoseTypeIsBeingElaborated(li); } Context cc = super.enterChildScope(child, c); return cc; } /** Visit the children of the declaration. */ public Node visitChildren(NodeVisitor v) { X10LocalDecl_c n = (X10LocalDecl_c) super.visitChildren(v); TypeNode hasType = (TypeNode) visitChild(n.hasType, v); return n.hasType(hasType); } public void prettyPrint(CodeWriter w, PrettyPrinter tr) { boolean printSemi = tr.appendSemicolon(true); boolean printType = tr.printType(true); Flags f = flags.flags(); Boolean fin = f.isFinal(); f = f.clearFinal(); w.write(f.translate()); for (Iterator<AnnotationNode> i = (((X10Ext) this.ext()).annotations()).iterator(); i.hasNext(); ) { AnnotationNode an = i.next(); an.prettyPrint(w, tr); w.allowBreak(0, " "); } if (fin) w.write("val "); else w.write("var "); tr.print(this, name, w); if (printType) { w.write(":"); print(type, w, tr); } if (init != null) { w.write(" ="); w.allowBreak(2, " "); print(init, w, tr); } if (printSemi) { w.write(";"); } tr.printType(printType); tr.appendSemicolon(printSemi); } @Override public String toString() { Flags flags = this.flags.flags(); return flags.clearFinal().translate() + (flags.isFinal() ? "val" : "var") + " " + name + ":" + type + (init != null ? " = " + init : "") + ";"; } }