/* * 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.visit; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Arrays; import polyglot.ast.Assign; import polyglot.ast.Binary; import polyglot.ast.Block; import polyglot.ast.Call; import polyglot.ast.CanonicalTypeNode; import polyglot.ast.ConstructorCall; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.Field; import polyglot.ast.FieldAssign; import polyglot.ast.FloatLit; import polyglot.ast.Formal; import polyglot.ast.Id_c; import polyglot.ast.IntLit; import polyglot.ast.Local; import polyglot.ast.LocalAssign; import polyglot.ast.LocalDecl; import polyglot.ast.Local_c; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Stmt; import polyglot.ast.TypeNode; import polyglot.ast.Unary; import polyglot.ast.New; import polyglot.ast.AmbExpr; import polyglot.ast.If; import polyglot.ast.Receiver; import polyglot.ast.ProcedureCall; import polyglot.ast.Special; import polyglot.ast.BooleanLit; import polyglot.frontend.Job; import polyglot.types.Context; import polyglot.types.LocalDef; import polyglot.types.LocalInstance; import polyglot.types.MemberInstance; import polyglot.types.Name; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.Types; import polyglot.types.VarDef; import polyglot.types.VarInstance; import polyglot.types.QName; import polyglot.types.ProcedureInstance; import polyglot.types.ProcedureDef; import polyglot.types.ClassType; import polyglot.types.ClassDef; import polyglot.types.Ref; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import x10.X10CompilerOptions; import x10.errors.Errors.IllegalConstraint; import x10.errors.Warnings; import x10.ast.Closure; import x10.ast.DepParameterExpr; import x10.ast.ParExpr; import x10.ast.SettableAssign; import x10.ast.X10Binary_c; import x10.ast.X10Call; import x10.ast.X10CanonicalTypeNode; import x10.ast.X10Cast; import x10.ast.X10Field_c; import x10.ast.X10Instanceof; import x10.ast.X10Local_c; import x10.ast.X10Special; import x10.ast.X10Unary_c; import x10.ast.X10ClassDecl_c; import x10.constraint.XFailure; import x10.constraint.XVar; import x10.types.EnvironmentCapture; import x10.types.ThisDef; import x10.types.X10ConstructorInstance; import x10.types.X10MemberDef; import x10.types.MethodInstance; import x10.types.TypeParamSubst; import x10.types.ReinstantiatedMethodInstance; import x10.types.ReinstantiatedConstructorInstance; import x10.types.X10ConstructorInstance_c; import x10.types.ParameterType; import x10.types.checker.Converter; import x10.types.checker.PlaceChecker; import x10.types.constraints.CConstraint; import x10.types.constraints.XConstrainedTerm; import x10.types.matcher.Subst; import x10.util.Synthesizer; /** * Visitor to desugar the AST before code generation. * * NOTE: all the nodes created in the Desugarer must have the appropriate type information. * The NodeFactory methods do not fill in the type information. Use the helper methods available * in the Desugarer to create expressions, or see how the type information is filled in for other * types of nodes elsewhere in the Desugarer. TODO: factor out the helper methods into the * {@link Synthesizer}. */ public class Desugarer extends ContextVisitor { public Desugarer(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); } private static int count; private static Name getTmp() { return Name.make("__desugarer__var__" + (count++) + "__"); } @Override public Node override(Node parent, Node n) { if (n instanceof Eval) { Stmt s = adjustEval((Eval) n); return visitEdgeNoOverride(parent, s); } return null; } @Override public Node leaveCall(Node old, Node n, NodeVisitor v) { if (n instanceof ParExpr) return visitParExpr((ParExpr) n); if (n instanceof Assign) return visitAssign((Assign) n); if (n instanceof Binary) return visitBinary((Binary) n); if (n instanceof Unary) return visitUnary((Unary) n); if (n instanceof X10Cast) return visitCast((X10Cast) n); if (n instanceof X10Instanceof) return visitInstanceof((X10Instanceof) n); if (n instanceof New) return desugarNew((New) n, this); if (n instanceof ConstructorCall) return desugarConstructorCall((ConstructorCall) n, this); if (n instanceof Call) return desugarCall((Call) n, this); // todo: also ctor calls (this&super), operators return n; } /** * Remove parenthesized expressions. */ protected Expr visitParExpr(ParExpr e) { return e.expr(); } // desugar binary operators private Expr visitBinary(Binary n) { return desugarBinary(n, this); } public static Expr desugarBinary(Binary n, ContextVisitor v) { Call c = X10Binary_c.desugarBinaryOp(n, v); if (c != null) { MethodInstance mi = (MethodInstance) c.methodInstance(); if (mi.error() != null) throw new InternalCompilerError("Unexpected exception when desugaring "+n, n.position(), mi.error()); return desugarCall(c, v); } return n; } private Expr getLiteral(Position pos, Type type, long val) { type = Types.baseType(type); Expr lit = null; if (ts.isIntOrLess(type)) { lit = nf.IntLit(pos, IntLit.INT, val); } else if (ts.isLong(type)) { lit = nf.IntLit(pos, IntLit.LONG, val); } else if (ts.isUInt(type)) { lit = nf.IntLit(pos, IntLit.UINT, val); } else if (ts.isULong(type)) { lit = nf.IntLit(pos, IntLit.ULONG, val); } else if (ts.isFloat(type)) { lit = nf.FloatLit(pos, FloatLit.FLOAT, val); } else if (ts.isDouble(type)) { lit = nf.FloatLit(pos, FloatLit.DOUBLE, val); } else if (ts.isChar(type)) { // Don't want to cast return (Expr) nf.IntLit(pos, IntLit.INT, val).typeCheck(this); } else throw new InternalCompilerError(pos, "Unknown literal type: "+type); lit = (Expr) lit.typeCheck(this); if (!ts.isSubtype(lit.type(), type)) { lit = nf.X10Cast(pos, nf.CanonicalTypeNode(pos, type), lit, Converter.ConversionType.PRIMITIVE).type(type); } return lit; } protected Expr getLiteral(Position pos, Type type, boolean val) { type = Types.baseType(type); if (ts.isBoolean(type)) { Type t = ts.Boolean(); t = Types.addSelfBinding(t, val ? ts.TRUE() : ts.FALSE()); return nf.BooleanLit(pos, val).type(t); } else throw new InternalCompilerError(pos, "Unknown literal type: "+type); } // ++x -> x+=1 or --x -> x-=1 private Expr unaryPre(Position pos, Unary.Operator op, Expr e) { Type ret = e.type(); Expr one = getLiteral(pos, ret, 1); Assign.Operator asgn = (op == Unary.PRE_INC) ? Assign.ADD_ASSIGN : Assign.SUB_ASSIGN; Expr a = assign(pos, e, asgn, one); a = visitAssign((Assign) a); return a; } // x++ -> (x+=1)-1 or x-- -> (x-=1)+1 private Expr unaryPost(Position pos, Unary.Operator op, Expr e) { Type ret = e.type(); Expr one = getLiteral(pos, ret, 1); Assign.Operator asgn = (op == Unary.POST_INC) ? Assign.ADD_ASSIGN : Assign.SUB_ASSIGN; Binary.Operator bin = (op == Unary.POST_INC) ? Binary.SUB : Binary.ADD; Expr incr = assign(pos, e, asgn, one); incr = visitAssign((Assign) incr); return visitBinary((Binary) nf.Binary(pos, incr, bin, one).type(ret)); } // desugar unary operators private Expr visitUnary(Unary n) { Unary.Operator op = n.operator(); if (op == Unary.PRE_DEC || op == Unary.PRE_INC) { return unaryPre(n.position(), op, n.expr()); } if (op == Unary.POST_DEC || op == Unary.POST_INC) { return unaryPost(n.position(), op, n.expr()); } return desugarUnary(n, this); } public static Expr desugarUnary(Unary n, ContextVisitor v) { Call c = X10Unary_c.desugarUnaryOp(n, v); if (c != null) { MethodInstance mi = (MethodInstance) c.methodInstance(); if (mi.error() != null) throw new InternalCompilerError("Unexpected exception when desugaring "+n, n.position(), mi.error()); return desugarCall(c, v); } return n; } // This is called from override, so we just need to transform the statement, not desugar // x++; -> ++x; or x--; -> --x; (to avoid creating an extra closure) private Stmt adjustEval(Eval n) { Position pos = n.position(); if (n.expr() instanceof Unary) { Unary e = (Unary) n.expr(); if (e.operator() == Unary.POST_DEC) return n.expr(e.operator(Unary.PRE_DEC)); if (e.operator() == Unary.POST_INC) return n.expr(e.operator(Unary.PRE_INC)); } return n; } private Assign assign(Position pos, Expr e, Assign.Operator asgn, Expr val) { return assign(pos, e, asgn, val, this); } private static Assign assign(Position pos, Expr e, Assign.Operator asgn, Expr val, ContextVisitor v) { try { Synthesizer synth = new Synthesizer(v.nodeFactory(), v.typeSystem()); return synth.makeAssign(pos, e, asgn, val, v.context()); } catch (SemanticException z) { throw new InternalCompilerError("Unexpected exception while creating assignment", pos, z); } } private Closure closure(Position pos, Type retType, List<Formal> parms, Block body) { return closure(pos, retType, parms, body, this); } private static Closure closure(Position pos, Type retType, List<Formal> parms, Block body, ContextVisitor v) { Synthesizer synth = new Synthesizer(v.nodeFactory(), v.typeSystem()); return synth.makeClosure(pos, retType, parms, body, v.context()); } public static class ClosureCaptureVisitor extends NodeVisitor { private final Context context; private final EnvironmentCapture cd; public ClosureCaptureVisitor(Context context, EnvironmentCapture cd) { this.context = context; this.cd = cd; this.cd.setCapturedEnvironment(new ArrayList<VarInstance<?>>()); } @Override public Node leave(Node old, Node n, NodeVisitor v) { if (n instanceof Local) { LocalInstance li = ((Local) n).localInstance(); VarInstance<?> o = context.findVariableSilent(li.name()); if (li == o || (o != null && li.def() == o.def())) { cd.addCapturedVariable(li); } } else if (n instanceof Field) { Field f = (Field) n; if (X10Field_c.isFieldOfThis(f)) { cd.addCapturedVariable(f.fieldInstance()); } } else if (n instanceof X10Special) { X10MemberDef code = (X10MemberDef) context.currentCode(); ThisDef thisDef = code.thisDef(); if (null == thisDef) { throw new InternalCompilerError(n.position(), "ClosureCaptureVisitor.leave: thisDef is null for containing code " +code); } assert (thisDef != null); cd.addCapturedVariable(thisDef.asInstance()); } return n; } } private Expr visitAssign(Assign n) { if (n instanceof SettableAssign) return visitSettableAssign((SettableAssign) n); if (n instanceof LocalAssign) return visitLocalAssign((LocalAssign) n); if (n instanceof FieldAssign) return visitFieldAssign((FieldAssign) n); return n; } public static Expr desugarAssign(Assign n, ContextVisitor v) { if (n instanceof SettableAssign) return desugarSettableAssign((SettableAssign) n, v); if (n instanceof LocalAssign) return desugarLocalAssign((LocalAssign) n, v); if (n instanceof FieldAssign) return desugarFieldAssign((FieldAssign) n, v); return n; } private Expr visitLocalAssign(LocalAssign n) { return desugarLocalAssign(n, this); } // x op=v -> x = x op v public static Expr desugarLocalAssign(LocalAssign n, ContextVisitor v) { Position pos = n.position(); if (n.operator() == Assign.ASSIGN) return n; Binary.Operator op = n.operator().binaryOperator(); Local left = (Local) n.left(); Expr right = n.right(); Type R = left.type(); Expr val = desugarBinary((Binary) v.nodeFactory().Binary(pos, left, op, right).type(R), v); return assign(pos, left, Assign.ASSIGN, val, v); } protected Expr visitFieldAssign(FieldAssign n) { return desugarFieldAssign(n, this); } // def n(a:T, b:S){EXPR(this,a,b)} { ... } // def this(a:T, b:S){EXPR(a,b)} { ... } // if the Call/New has a ProcedureInstance with checkGuardAtRuntime, then we do this transformation: // e.n(e1, e2) -> ((r:C, a:T, b:S)=>{if (!(EXPR(r,a,b))) throw new FailedDynamicCheckException(...); return r.n(a,b); })(e, e1, e2) // (there are two special cases: if e is empty (so it's either "this" or nothing if the "n" is static) // new X(e1, e2) -> ((a:T, b:S)=>{if (!(EXPR(a,b))) throw new FailedDynamicCheckException(...); return new X(a,b); })(e1, e2) private static Expr desugarCall(Expr expr, ContextVisitor v) { if (expr instanceof Call) return desugarCall((Call)expr, v); if (expr instanceof Binary) return desugarCall(expr, null, null, (Binary)expr, v); if (expr instanceof Unary) { Unary unary = (Unary) expr; // TODO: how to get the methodInstance out of an unary? do we even need to worry about it or is an unary always desugared into a Call (which I handle)? } if (expr instanceof SettableAssign) { SettableAssign settableAssign = (SettableAssign) expr; // todo: what about SettableAssign ? is it always desugared into a Call or ClosureCall (because I handle both cases correctly)? } return expr; } private static Expr desugarCall(Call call, ContextVisitor v) { return desugarCall(call, call, null, null, v); } private static Expr desugarNew(final New neu, ContextVisitor v) { return desugarCall(neu, null, neu, null, v); } private static Stmt desugarConstructorCall(ConstructorCall cc, ContextVisitor v) { final ProcedureInstance<? extends ProcedureDef> pi = cc.constructorInstance(); if (!pi.checkConstraintsAtRuntime()) { return cc; } TypeSystem ts = v.typeSystem(); NodeFactory nf = v.nodeFactory(); Job job = v.job(); List<Expr> args = cc.arguments(); List<Expr> newArgs = new ArrayList<Expr>(args.size()); List<Formal> params = new ArrayList<Formal>(args.size()); Position pos = cc.position(); Context context = v.context(); Context closureContext = context.pushBlock(); /* * For a constructor call this(e1,..,en), where this:T,e1:T1,..,en:Tn and T.this(f1:U1,..,fn:Un){g}, * we are going to be creating the following block: * {val x1=e1 as U1;..;val xn=en as Un;if(!g[x1/f1;..;xn/fn])throw new FDCE();this(x1,..,xn);} */ List<Stmt> statements = new ArrayList<Stmt>(1); if (!computeDynamicCheck(pi, args, null, pos, v, params, closureContext, newArgs, statements)) return cc; List<LocalDecl> fvars = new ArrayList<LocalDecl>(params.size()); int i = 0; for (Formal f : params) { fvars.add(nf.LocalDecl(pos, f.flags(), f.type(), f.name(), args.get(i++)).localDef(f.localDef())); } statements.addAll(0, fvars); ConstructorCall newCC = cc.arguments(newArgs); X10TypeBuilder builder = new X10TypeBuilder(job, ts, nf); ContextVisitor checker = new X10TypeChecker(job, ts, nf, job.nodeMemo()).context(closureContext); newCC = (ConstructorCall) newCC.visit(builder).visit(checker); statements.add(newCC); return nf.Block(pos, statements); } /** * * @param booleanGuard * @param constraint * @param selfName * @param baseType -- for use in generating code from any occurrence of self in constraint * @param nf * @param ts * @param pos */ private static void addCheck(ArrayList<Expr> booleanGuard, CConstraint constraint, final Name selfName, final Type baseType, final NodeFactory nf, final TypeSystem ts, final Position pos) { if (constraint==null) return; final List<Expr> guardExpr = new Synthesizer(nf, ts).makeExpr(constraint, baseType, pos); // note: this doesn't typecheck the expression, so we're missing type info. for (Expr e : guardExpr) { e = (Expr) e.visit( new NodeVisitor() { @Override public Node override(Node n) { if (n instanceof Special){ Special special = (Special) n; if (special.kind()== Special.Kind.SELF) { assert selfName!=null; // self cannot appear in a method guard return nf.AmbExpr(pos,nf.Id(pos,selfName)); } } return null; } }); booleanGuard.add(e); } } private static <T> T reinstantiate(TypeParamSubst typeParamSubst, T t) { return typeParamSubst==null ? t : typeParamSubst.reinstantiate(t); } private static Expr desugarCall(final Expr n, final Call call_c, final New new_c, final Binary binary_c, ContextVisitor v) { final NodeFactory nf = v.nodeFactory(); final TypeSystem ts = v.typeSystem(); final Job job = v.job(); assert n!=null && (call_c==n || new_c==n || binary_c==n); ProcedureCall procCall = call_c!=null || new_c!=null ? (ProcedureCall) n : null; final ProcedureInstance<? extends ProcedureDef> procInst = binary_c!=null ? binary_c.methodInstance() : procCall.procedureInstance(); if (procInst==null || // for binary ops (like ==), the methodInstance is null !procInst.checkConstraintsAtRuntime()) return n; final Position pos = n.position(); List<Expr> args = binary_c!=null ? Arrays.asList(binary_c.left(), binary_c.right()) : procCall.arguments(); final Receiver target; if (binary_c!=null) target = null; else target = (call_c==null ? new_c.qualifier() : call_c.target()); Expr oldReceiver = null; if (target!=null && target instanceof Expr) { // making sure that the receiver is not a TypeNode oldReceiver = (Expr) target; args = new ArrayList<Expr>(args); args.add(0, (Expr) oldReceiver); } ArrayList<Expr> newArgs = new ArrayList<Expr>(args.size()); ArrayList<Formal> params = new ArrayList<Formal>(args.size()); final Context context = v.context(); final Context closureContext = context.pushBlock(); /* * For a call r.m(e1,..,en), where r:T,e1:T1,..,en:Tn and U.m(f1:U1,..,fn:Un):R, * we are going to be creating the following closure call: * ((p0:T,p1:T1,..,pn:Tn)=>{val x$0=p0 as U;val f1=p1 as U1;..;val fn=pn as Un;x$0.m(f1,..,fn)})(e1,..,en) */ List<Stmt> statements = new ArrayList<Stmt>(); if (!computeDynamicCheck(procInst, args, oldReceiver, pos, v, params, closureContext, newArgs, statements)) return n; final Expr newReceiver = oldReceiver==null ? null : newArgs.remove(0); final ProcedureCall newProcCall; if (newReceiver==null) newProcCall = procCall; else newProcCall = (call_c!=null ? call_c.target(newReceiver) : new_c.qualifier(newReceiver)); Expr newExpr; if (binary_c!=null) newExpr = binary_c.left(newArgs.get(0)).right(newArgs.get(1)); else newExpr = (Expr) newProcCall.arguments(newArgs); X10TypeBuilder builder = new X10TypeBuilder(job, ts, nf); ContextVisitor checker = new X10TypeChecker(job, ts, nf, job.nodeMemo()).context(closureContext); newExpr = (Expr) newExpr.visit(builder).visit(checker); final Type resType = newExpr.type(); // if resType is void, then we shouldn't use return final boolean isVoid = ts.isVoid(resType); statements.add(isVoid ? nf.Eval(pos,newExpr) : nf.Return(pos, newExpr)); Block body = nf.Block(pos, statements); //body = (Block) body.visit(builder).visit(checker); - there is a problem type-checking the return statement Type closureRet = procInst.returnType(); Closure c = closure(pos, closureRet, params, body, v); MethodInstance ci = c.closureDef().asType().applyMethod(); return nf.ClosureCall(pos, c, args).closureInstance(ci).type(resType); } public static boolean computeDynamicCheck(ProcedureInstance<?> procInst, List<Expr> args, Expr oldReceiver, final Position pos, ContextVisitor v, List<Formal> params, Context closureContext, List<Expr> newArgs, List<Stmt> statements) { // we shouldn't use the def, because sometimes the constraints come from the instance, // e.g., new Box[Int{self!=0}](v) // dynamically checks that v!=0 (but you can't see it in the def! only in the instance). // However, the instance has also the arguments (that exists in the context), // and for some reason formalNames of the instance doesn't return the constraint that self!=0 (and Vijay thinks it shouldn't do it anyway) // so I need to take the paramSubst and do it myself on the def. // E.g., // new Box[Int{self!=0}](i) in the instance returns a formal arg123:Int{self==arg123, arg123==i} but without i!=0 ! // so I take the original formal from the def (x:T) and do the paramSubst on it to get x:Int{self!=0} final TypeSystem ts = v.typeSystem(); final NodeFactory nf = v.nodeFactory(); final Job job = v.job(); final Context context = v.context(); final ProcedureDef procDef = procInst.def(); TypeParamSubst typeParamSubst = procInst instanceof ReinstantiatedMethodInstance ? ((ReinstantiatedMethodInstance)procInst).typeParamSubst() : procInst instanceof ReinstantiatedConstructorInstance ? ((ReinstantiatedConstructorInstance)procInst).typeParamSubst() : null; // this can happen when procInst is X10ConstructorInstance_c (see XTENLANG_2330). But creating an empty TypeParamSubst would also work final List<Type> typeParam = procInst.typeParameters(); // note that X10ConstructorInstance_c.typeParameters returns an empty list! (there is a todo there!) if (typeParam!=null && typeParam.size()>0) { if (typeParamSubst==null) typeParamSubst = new TypeParamSubst(ts,Collections.EMPTY_LIST,Collections.EMPTY_LIST); final ArrayList<Type> newTArgs = typeParamSubst.copyTypeArguments(); newTArgs.addAll(typeParam); final ArrayList<ParameterType> newParams = typeParamSubst.copyTypeParameters(); newParams.addAll(procDef.typeParameters()); typeParamSubst = new TypeParamSubst(ts, newTArgs,newParams); } final List<LocalDef> oldFormals = procDef.formalNames(); List<LocalDecl> locals = new ArrayList<LocalDecl>(args.size()); int i=0; List<VarDef> Ys = new ArrayList<VarDef>(args.size()); List<VarDef> Xs = new ArrayList<VarDef>(args.size()); for (Expr arg : args) { Name pn = Name.make("p$"+i); Type pType = arg.type(); // The argument might be null, e.g., def m(b:Z) {b.x!=null} = 1; ... m(null); final LocalDef oldFormal = arg==oldReceiver ? null : oldFormals.get(oldReceiver==null ? i : i-1); Type type = Types.baseType(oldFormal!=null ? reinstantiate(typeParamSubst, Types.get(oldFormal.type())) : pType); if (type.isNull() && procInst instanceof MemberInstance<?>) { type = reinstantiate(typeParamSubst, ((MemberInstance<?>) procInst).container()); } Type tType; try { tType = Subst.subst(type, Types.toVarArray(Ys), Types.toVarArray(Xs), new Type[0], new ParameterType[0]); } catch (SemanticException z) { throw new InternalCompilerError("Unexpected exception while inserting a dynamic check", z); } if (pType.isNull()) { pType = type; } LocalDef pDef = ts.localDef(pos, ts.Final(), Types.ref(pType), pn); Formal pd = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, pType), nf.Id(pos, pn)).localDef(pDef); params.add(pd); Local p = (Local) nf.Local(pos, nf.Id(pos, pn)).localInstance(pDef.asInstance()).type(pType); Name xn = oldFormal!=null ? Name.make("x$"+oldFormal.name()) : Name.make("x$"+i); // to make sure it doesn't conflict/shadow an existing field LocalDef xDef = ts.localDef(pos, ts.Final(), Types.ref(tType), xn); Expr c = Converter.attemptCoercion(v.context(closureContext), p, tType); c = (Expr) c.visit(v.context(closureContext)); LocalDecl xd = nf.LocalDecl(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, tType), nf.Id(pos, xn), c).localDef(xDef); locals.add(xd); final Local x = (Local) nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(tType); newArgs.add(x); closureContext.addVariable(x.localInstance()); if (oldFormal != null) { Ys.add(xDef); Xs.add(oldFormal); } else { Ys.add(xDef); Xs.add(procDef.thisDef()); } i++; } // we add the guard to the body, then the return stmt. // if (!(GUARDEXPR(a,b))) throw new FailedDynamicCheckException(...); return ... final Ref<CConstraint> guardRefConstraint = procDef.guard(); ArrayList<Expr> booleanGuard = new ArrayList<Expr>(); if (guardRefConstraint!=null) { final CConstraint guard = reinstantiate(typeParamSubst, guardRefConstraint.get()); // self cannot occur in the constraint, hence null can be passed as the type. addCheck(booleanGuard,guard, null, null, nf, ts, pos); } // add the constraints of the formals for (LocalDef localDef : procDef.formalNames()) { Type type = reinstantiate(typeParamSubst, Types.get(localDef.type())); CConstraint constraint = Types.xclause(type); LocalInstance li = localDef.asInstance(); Receiver r = new X10Local_c(pos, new Id_c(pos, localDef.name())).localInstance(li); r = ((X10Local_c) r).type(li.type()); XVar selfVar=null; try { selfVar = (XVar) localDef.typeSystem().xtypeTranslator().translate(constraint, r, context); } catch (IllegalConstraint z) { /// what do we do? } if (selfVar != null && constraint != null) constraint = constraint.instantiateSelf(selfVar); addCheck(booleanGuard,constraint, localDef.name(), type, nf, ts, pos); } final Expr newReceiver = oldReceiver==null ? null : newArgs.get(0); int offset = oldReceiver==null ? 0 : 1; // replace old formals in depExpr with the new locals final Map<Name,Expr> old2new = CollectionFactory.newHashMap(oldFormals.size()); for (int k=0; k<oldFormals.size(); k++) { Expr newE = newArgs.get(k+offset); old2new.put(oldFormals.get(k).name(), newE); } // replace all AmbExpr with the new locals final X10TypeBuilder builder = new X10TypeBuilder(job, ts, nf); final ContextVisitor checker = new X10TypeChecker(job, ts, nf, job.nodeMemo()).context(closureContext); NodeVisitor replace = new NodeVisitor() { @Override public Node override(Node n) { if (n instanceof Special){ // if it's an outer instance, then we need to access the outer field Special special = (Special) n; TypeNode qualifer = special.qualifier(); if (qualifer==null) return newReceiver; // qualifer doesn't have type info because it was created in Synthesizer.makeExpr qualifer = (TypeNode) qualifer.visit(builder).visit(checker); ClassType ct = qualifer.type().toClass(); ClassType receiverType = Types.getClassType(newReceiver.type(), ts, context); if (receiverType==null) return newReceiver; final ClassDef newReceiverDef = receiverType.def(); final ClassDef qualifierDef = ct.def(); if (newReceiverDef==qualifierDef) return newReceiver; return nf.Call(pos,newReceiver, nf.Id(pos,X10ClassDecl_c.getThisMethod(newReceiverDef.fullName(),ct.fullName()))); } if (n instanceof AmbExpr) { AmbExpr amb = (AmbExpr) n; Name name = amb.name().id(); Expr newE = old2new.get(name); if (newE==null) throw new OuterLocalUsed(); return newE; } return null; } }; ArrayList<Expr> newCheck = new ArrayList<Expr>(booleanGuard.size()); for (Expr e : booleanGuard) { try { newCheck.add( (Expr)e.visit(replace) ); } catch (OuterLocalUsed e1) { // ignore expressions that have outer locals (constraint system bugs like XTENLANG_2638) // [DC] since the above jira is fixed, making this a hard error throw new InternalCompilerError("Dynamic check of constraint "+e+" was malformed"); } } if (newCheck.size()==0) return false; // nothing to check... Warnings.dynamicCall(v.job(), Warnings.GeneratedDynamicCheck(pos)); Expr newDep = newCheck.get(0); for (int k=1; k<newCheck.size(); k++) { Expr e = newCheck.get(k); newDep = nf.Binary(pos, newDep, Binary.Operator.COND_AND, e).type(ts.Boolean()); } // if (!newDep) throw new FailedDynamicCheckException(); newDep = nf.Unary(pos, Unary.Operator.NOT, newDep).type(ts.Boolean()); If anIf = nf.If(pos, newDep, nf.Throw(pos, nf.New(pos, nf.CanonicalTypeNode(pos, ts.FailedDynamicCheckException()), CollectionUtil.<Expr>list(nf.StringLit(pos, newDep.toString()))).type(ts.FailedDynamicCheckException()))); anIf = (If) anIf.visit(builder).visit(checker).visit(v); statements.addAll(locals); statements.add(anIf); return true; } private static class OuterLocalUsed extends RuntimeException {} // T.f op=v -> T.f = T.f op v or e.f op=v -> ((x:E,y:T)=>x.f=x.f op y)(e,v) public static Expr desugarFieldAssign(FieldAssign n, ContextVisitor v) { NodeFactory nf = v.nodeFactory(); TypeSystem ts = v.typeSystem(); Position pos = n.position(); if (n.operator() == Assign.ASSIGN) return n; Binary.Operator op = n.operator().binaryOperator(); Field left = (Field) n.left(); Expr right = n.right(); Type R = left.type(); if (left.flags().isStatic()) { Expr val = desugarBinary((Binary) nf.Binary(pos, left, op, right).type(R), v); return assign(pos, left, Assign.ASSIGN, val, v); } Expr e = (Expr) left.target(); Type E = e.type(); List<Formal> parms = new ArrayList<Formal>(); Name xn = Name.make("x"); LocalDef xDef = ts.localDef(pos, ts.Final(), Types.ref(E), xn); Formal x = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, E), nf.Id(pos, xn)).localDef(xDef); parms.add(x); Name yn = Name.make("y"); Type T = right.type(); LocalDef yDef = ts.localDef(pos, ts.Final(), Types.ref(T), yn); Formal y = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, T), nf.Id(pos, yn)).localDef(yDef); parms.add(y); Expr lhs = nf.Field(pos, nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(E), nf.Id(pos, left.name().id())).fieldInstance(left.fieldInstance()).type(R); Expr val = desugarBinary((Binary) nf.Binary(pos, lhs, op, nf.Local(pos, nf.Id(pos, yn)).localInstance(yDef.asInstance()).type(T)).type(R), v); Expr res = assign(pos, lhs, Assign.ASSIGN, val, v); Block body = nf.Block(pos, nf.Return(pos, res)); Closure c = closure(pos, R, parms, body, v); MethodInstance ci = c.closureDef().asType().applyMethod(); List<Expr> args = new ArrayList<Expr>(); args.add(0, e); args.add(right); return nf.ClosureCall(pos, c, args).closureInstance(ci).type(R); } protected Expr visitSettableAssign(SettableAssign n) { return desugarSettableAssign(n, this); } // a(i)=v -> a.operator()=(i,v) or a(i)op=v -> ((x:A,y:I,z:T)=>x.operator()=(y,x.operator()(y) op z))(a,i,v) public static Expr desugarSettableAssign(SettableAssign n, ContextVisitor v) { NodeFactory nf = v.nodeFactory(); TypeSystem ts = v.typeSystem(); Position pos = n.position(); MethodInstance mi = n.methodInstance(); List<Expr> args = new ArrayList<Expr>(n.index()); Expr a = n.array(); if (n.operator() == Assign.ASSIGN) { args.add(n.right()); return desugarCall(nf.Call(pos, a, nf.Id(pos, mi.name()), args).methodInstance(mi).type(mi.returnType()), v); } Binary.Operator op = n.operator().binaryOperator(); X10Call left = (X10Call) n.left(); MethodInstance ami = left.methodInstance(); List<Formal> parms = new ArrayList<Formal>(); Name xn = Name.make("x"); Type aType = a.type(); assert (ts.isSubtype(aType, mi.container(), v.context())); LocalDef xDef = ts.localDef(pos, ts.Final(), Types.ref(aType), xn); Formal x = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, aType), nf.Id(pos, xn)).localDef(xDef); parms.add(x); List<Expr> idx1 = new ArrayList<Expr>(); int i = 0; assert (ami.formalTypes().size()==n.index().size()); for (Expr e : n.index()) { Type t = e.type(); Name yn = Name.make("y"+i); LocalDef yDef = ts.localDef(pos, ts.Final(), Types.ref(t), yn); Formal y = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, t), nf.Id(pos, yn)).localDef(yDef); parms.add(y); idx1.add(nf.Local(pos, nf.Id(pos, yn)).localInstance(yDef.asInstance()).type(t)); i++; } Name zn = Name.make("z"); Type T = mi.formalTypes().get(mi.formalTypes().size()-1); Type vType = n.right().type(); assert (ts.isSubtype(ami.returnType(), T, v.context())); assert (ts.isSubtype(vType, T, v.context())); LocalDef zDef = ts.localDef(pos, ts.Final(), Types.ref(vType), zn); Formal z = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, vType), nf.Id(pos, zn)).localDef(zDef); parms.add(z); Expr val = desugarBinary((Binary) nf.Binary(pos, desugarCall(nf.Call(pos, nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(aType), nf.Id(pos, ami.name()), idx1).methodInstance(ami).type(ami.returnType()), v), op, nf.Local(pos, nf.Id(pos, zn)).localInstance(zDef.asInstance()).type(vType)).type(T), v); Type rType = val.type(); Name rn = Name.make("r"); LocalDef rDef = ts.localDef(pos, ts.Final(), Types.ref(rType), rn); LocalDecl r = nf.LocalDecl(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, rType), nf.Id(pos, rn), val).localDef(rDef); List<Expr> args1 = new ArrayList<Expr>(idx1); args1.add(nf.Local(pos, nf.Id(pos, rn)).localInstance(rDef.asInstance()).type(rType)); Expr res = desugarCall(nf.Call(pos, nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(aType), nf.Id(pos, mi.name()), args1).methodInstance(mi).type(mi.returnType()), v); Block block = nf.Block(pos, r, nf.Eval(pos, res), nf.Return(pos, nf.Local(pos, nf.Id(pos, rn)).localInstance(rDef.asInstance()).type(rType))); Closure c = closure(pos, rType, parms, block, v); MethodInstance ci = c.closureDef().asType().applyMethod(); args.add(0, a); args.add(n.right()); return desugarCall(nf.ClosureCall(pos, c, args).closureInstance(ci).type(rType), v); } /** * Concatenates the given list of clauses with &&, creating a conjunction. * Any occurrence of "self" in the list of clauses is replaced by self. */ private Expr conjunction(Position pos, List<Expr> clauses, Expr self) { if (clauses.isEmpty()) { // FIXME: HACK: need to ensure that source expressions are preserved return getLiteral(pos, ts.Boolean(), true); } assert clauses.size() > 0; Substitution<Expr> subst = new Substitution<Expr>(Expr.class, Collections.singletonList(self)) { protected Expr subst(Expr n) { if (n instanceof X10Special && ((X10Special) n).kind() == X10Special.SELF) return by.get(0); return n; } }; Expr left = null; for (Expr clause : clauses) { Expr right = (Expr) clause.visit(subst); right = (Expr) right.visit(this); if (left == null) left = right; else { left = nf.Binary(pos, left, Binary.COND_AND, right).type(ts.Boolean()); left = visitBinary((Binary) left); } } return left; } private DepParameterExpr getClause(TypeNode tn) { Type t = tn.type(); if (tn instanceof X10CanonicalTypeNode) { CConstraint c = Types.xclause(t); if (c == null || c.valid()) return null; XConstrainedTerm here = context().currentPlaceTerm(); if (here != null && here.term() instanceof XVar) { try { c = c.substitute(PlaceChecker.here(), (XVar) here.term()); } catch (XFailure e) { } } DepParameterExpr res = nf.DepParameterExpr(tn.position(), new Synthesizer(nf, ts).makeExpr(c, t, tn.position())); res = (DepParameterExpr) res.visit(new X10TypeBuilder(job, ts, nf)).visit(new X10TypeChecker(job, ts, nf, job.nodeMemo()).context(context().pushDepType(tn.typeRef()))); return res; } throw new InternalCompilerError("Unknown type node type: "+tn.getClass(), tn.position()); } private TypeNode stripClause(TypeNode tn) { Type t = tn.type(); if (tn instanceof X10CanonicalTypeNode) { X10CanonicalTypeNode ctn = (X10CanonicalTypeNode) tn; Type baseType = Types.baseType(t); if (baseType != t) { return ctn.typeRef(Types.ref(baseType)); } return ctn; } throw new InternalCompilerError("Unknown type node type: "+tn.getClass(), tn.position()); } // e as T{c} -> ((x:T):T{c}=>{if (x!=null&&!c[self/x]) throwCCE(); return x;})(e as T) private Expr visitCast(X10Cast n) { // We give the DYNAMIC_CALLS warning here (and not in type-checking), because we create a lot of temp cast nodes in the process that are discarded later. if (n.conversionType()==Converter.ConversionType.DESUGAR_LATER) { Warnings.dynamicCall(job(), Warnings.CastingExprToType(n.expr(),n.type(),n.position())); n = n.conversionType(Converter.ConversionType.CHECKED); } Position pos = n.position(); Expr e = n.expr(); TypeNode tn = n.castType(); Type ot = tn.type(); DepParameterExpr depClause = getClause(tn); tn = stripClause(tn); X10CompilerOptions opts = (X10CompilerOptions) job.extensionInfo().getOptions(); if (depClause == null || opts.x10_config.NO_CHECKS) return n.castType(tn); Name xn = getTmp(); Type t = tn.type(); // the base type of the cast LocalDef xDef = ts.localDef(pos, ts.Final(), Types.ref(t), xn); Formal x = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, xDef.type()), nf.Id(pos, xn)).localDef(xDef); Expr xl = nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(t); List<Expr> condition = depClause.condition(); Expr cond = visitUnary((Unary) nf.Unary(pos, conjunction(depClause.position(), condition, xl), Unary.NOT).type(ts.Boolean())); Type ccet = ts.ClassCastException(); CanonicalTypeNode CCE = nf.CanonicalTypeNode(pos, ccet); Expr msg = nf.StringLit(pos, ot.toString()).type(ts.String()); X10ConstructorInstance ni; try { ni = ts.findConstructor(ccet, ts.ConstructorMatcher(ccet, Collections.singletonList(ts.String()), context())); } catch (SemanticException z) { throw new InternalCompilerError("Unexpected exception while desugaring "+n, pos, z); } Expr newCCE = nf.New(pos, CCE, Collections.singletonList(msg)).constructorInstance(ni).type(ccet); Stmt throwCCE = nf.Throw(pos, newCCE); Stmt check = nf.If(pos, cond, throwCCE); Block body = nf.Block(pos, check, nf.Return(pos, xl)); Closure c = closure(pos, ot, Collections.singletonList(x), body); c.visit(new ClosureCaptureVisitor(this.context(), c.closureDef())); //if (!c.closureDef().capturedEnvironment().isEmpty()) // System.out.println(c+" at "+c.position()+" captures "+c.closureDef().capturedEnvironment()); Expr cast = nf.X10Cast(pos, tn, e, Converter.ConversionType.CHECKED).type(t); MethodInstance ci = c.closureDef().asType().applyMethod(); return nf.ClosureCall(pos, c, Collections.singletonList(cast)).closureInstance(ci).type(ot); } // e instanceof T{c} -> ((x:F)=>x instanceof T && c[self/x as T])(e) private Expr visitInstanceof(X10Instanceof n) { Position pos = n.position(); Expr e = n.expr(); TypeNode tn = n.compareType(); DepParameterExpr depClause = getClause(tn); tn = stripClause(tn); if (depClause == null) return n; Name xn = getTmp(); Type et = e.type(); LocalDef xDef = ts.localDef(pos, ts.Final(), Types.ref(et), xn); Formal x = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, xDef.type()), nf.Id(pos, xn)).localDef(xDef); Expr xl = nf.Local(pos, nf.Id(pos, xn)).localInstance(xDef.asInstance()).type(et); Expr iof = nf.Instanceof(pos, xl, tn).type(ts.Boolean()); Expr cast = nf.X10Cast(pos, tn, xl, Converter.ConversionType.CHECKED).type(tn.type()); List<Expr> condition = depClause.condition(); Expr cond = conjunction(depClause.position(), condition, cast); Expr rval = visitBinary((Binary) nf.Binary(pos, iof, Binary.COND_AND, cond).type(ts.Boolean())); Block body = nf.Block(pos, nf.Return(pos, rval)); Closure c = closure(pos, ts.Boolean(), Collections.singletonList(x), body); c.visit(new ClosureCaptureVisitor(this.context(), c.closureDef())); //if (!c.closureDef().capturedEnvironment().isEmpty()) // System.out.println(c+" at "+c.position()+" captures "+c.closureDef().capturedEnvironment()); MethodInstance ci = c.closureDef().asType().applyMethod(); return nf.ClosureCall(pos, c, Collections.singletonList(e)).closureInstance(ci).type(ts.Boolean()); } public static class Substitution<T extends Node> extends NodeVisitor { protected final List<T> by; private final Class<T> cz; public Substitution(Class<T> cz, List<T> by) { this.cz = cz; this.by = by; } @SuppressWarnings("unchecked") // Casting to a generic type parameter @Override public Node leave(Node old, Node n, NodeVisitor v) { if (cz.isInstance(n)) return subst((T)n); return n; } protected T subst(T n) { return n; } } }