package x10cpp.visit; import static x10cpp.visit.Emitter.mangled_field_name; import static x10cpp.visit.SharedVarsMethods.chevrons; import java.util.ArrayList; import java.util.List; import polyglot.ast.Assign_c; import polyglot.ast.Block; import polyglot.ast.BooleanLit_c; import polyglot.ast.Call_c; import polyglot.ast.CharLit_c; import polyglot.ast.Conditional_c; import polyglot.ast.ConstructorCall_c; import polyglot.ast.Expr; import polyglot.ast.FieldDecl_c; import polyglot.ast.FloatLit_c; import polyglot.ast.Formal; import polyglot.ast.IntLit_c; import polyglot.ast.Lit_c; import polyglot.ast.Local; import polyglot.ast.LocalDecl_c; import polyglot.ast.New_c; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.NullLit; import polyglot.ast.NullLit_c; import polyglot.ast.Return_c; import polyglot.frontend.Job; import polyglot.types.Context; import polyglot.types.Flags; import polyglot.types.FunctionDef; import polyglot.types.Name; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.Types; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import x10.ast.AssignPropertyCall_c; import x10.ast.Closure; import x10.ast.ClosureCall_c; import x10.ast.Closure_c; import x10.ast.SettableAssign; import x10.ast.Tuple_c; import x10.types.FunctionType; import x10.types.FunctionType_c; import x10.types.MethodInstance; import x10.types.X10FieldInstance; import x10.types.checker.Converter; import x10.util.AltSynthesizer; import x10.util.Synthesizer; import x10.visit.Desugarer.ClosureCaptureVisitor; /** * A pass to run right before final C++ codegeneration * to insert upcasts that are required by the C++ backend, * but are not explicitly injected by the front-end. * * We assume that the program is statically type correct, * therefore all of these casts can be unchecked. * They fall into two main catagories: * (a) casts injected for actual parameters of a call * so that the static types match the callee method. * These are required to make overloading work. * (b) casts injected on other assignment-like operations * whose purpose is to ensure that representation level * boxing/unboxing operations are performed. * * TODO: There's a static cast for the return type of Call_c in MPGC. * I think that should no longer be needed, since we are injecting casts * thoroughly here. Verify this and remove cast in Call_c if true. */ public class CastInjector extends ContextVisitor { final Synthesizer synth; public CastInjector(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); synth = new Synthesizer(nf, ts); } public Node leaveCall(Node old, Node n, NodeVisitor v) { if (n instanceof Assign_c) { Assign_c assign = (Assign_c)n; Expr rhs = assign.right(); Expr newRhs = boxingUpcastIfNeeded(assign.right(), assign.left().type()); return rhs == newRhs ? assign : assign.right(newRhs); } else if (n instanceof ConstructorCall_c) { ConstructorCall_c call = (ConstructorCall_c)n; List<Expr> args = call.arguments(); List<Type> formals = call.constructorInstance().formalTypes(); List<Expr> newArgs = castActualsToFormals(args, formals); return null == newArgs ? call : call.arguments(newArgs); } else if (n instanceof FieldDecl_c) { FieldDecl_c fd = (FieldDecl_c)n; Expr init = fd.init(); if (init == null) return fd; Expr newInit = boxingUpcastIfNeeded(init, fd.type().type()); return init == newInit ? fd : fd.init(newInit); } else if (n instanceof AssignPropertyCall_c) { AssignPropertyCall_c call = (AssignPropertyCall_c)n; List<Expr> args = call.arguments(); List<X10FieldInstance> props = call.properties(); List<Type> ftypes = new ArrayList<Type>(props.size()); for (X10FieldInstance fi: props) { ftypes.add(fi.type()); } List<Expr> newArgs = castActualsToFormals(args, ftypes); return null == newArgs ? call : call.arguments(newArgs); } else if (n instanceof Return_c) { Return_c ret = (Return_c)n; Expr rhs = ret.expr(); if (rhs == null) return ret; FunctionDef container = (FunctionDef) context.currentCode(); Type rType = container.returnType().get(); Expr newRhs = boxingUpcastIfNeeded(rhs, rType); return rhs == newRhs ? ret : ret.expr(newRhs); } else if (n instanceof LocalDecl_c) { LocalDecl_c ld = (LocalDecl_c)n; Expr init = ld.init(); if (null == init) return ld; Expr newInit = boxingUpcastIfNeeded(init, ld.type().type()); return newInit == init ? ld : ld.init(newInit); } else if (n instanceof Call_c) { Call_c call = (Call_c)n; List<Expr> args = call.arguments(); List<Type> formals = call.methodInstance().formalTypes(); List<Expr> newArgs = castActualsToFormals(args, formals); MethodInstance mi = call.methodInstance(); if (!mi.flags().isStatic() && ts.isParameterType(call.target().type()) && !ts.isHandOptimizedInterface(mi.container())) { call = (Call_c)call.target(makeCast(call.target().position(), (Expr)call.target(), mi.container())); } return null == newArgs ? call : call.arguments(newArgs); } else if (n instanceof New_c) { New_c asNew = (New_c)n; List<Expr> args = asNew.arguments(); List<Type> formals = asNew.constructorInstance().formalTypes(); List<Expr> newArgs = castActualsToFormals(args, formals); return null == newArgs ? asNew : asNew.arguments(newArgs); } else if (n instanceof Conditional_c) { Conditional_c cond = (Conditional_c)n; Expr cons = cond.consequent(); Expr newCons = upcast(cons, cond.type()); Expr alt = cond.alternative(); Expr newAlt = upcast(alt, cond.type()); return cons == newCons && alt == newAlt ? cond : cond.consequent(newCons).alternative(newAlt); } else if (n instanceof ClosureCall_c) { ClosureCall_c call = (ClosureCall_c)n; List<Expr> args = call.arguments(); List<Type> formals = call.closureInstance().formalTypes(); List<Expr> newArgs = castActualsToFormals(args, formals); return null == newArgs ? call : call.arguments(newArgs); } else if (n instanceof Tuple_c) { Tuple_c tuple = (Tuple_c)n; List<Expr> inits = tuple.arguments(); List<Expr> newInits = null; Type T = Types.getParameterType(tuple.type(), 0); for (int i=0; i<inits.size(); i++) { Expr e = inits.get(i); Expr e2 = boxingUpcastIfNeeded(e, T); if (e != e2) { if (newInits == null) { newInits = new ArrayList<Expr>(inits); } newInits.set(i, e2); } } return null == newInits ? tuple : tuple.arguments(newInits); } else if (n instanceof BooleanLit_c || n instanceof IntLit_c || n instanceof FloatLit_c || n instanceof CharLit_c) { Lit_c lit = (Lit_c) n; if (Types.baseType(lit.type()).isAny()) { return makeCast(n.position(), lit.type(lit.constantValue().getLitType(ts)), lit.type()); } } return n; } /** * Inject any needed upcasts to ensure that the actual and formal types * match at a call site. An exact deep base type equality is needed * to ensure that overloading at the C++ level works as expected. */ private List<Expr> castActualsToFormals(List<Expr> args, List<Type> formals) { List<Expr> newArgs = null; for (int i=0; i<args.size(); i++) { Expr e = args.get(i); Type fType = formals.get(i); Expr e2 = null; if (Types.baseType(fType) instanceof FunctionType) { e2 = upcastToFunctionType(e, (FunctionType)Types.baseType(fType), false); } else if (!ts.typeDeepBaseEquals(fType, e.type(), context)) { e2 = makeCast(e.position(), e, fType); } if (e2 != null) { if (newArgs == null) { newArgs = new ArrayList<Expr>(args); } newArgs.set(i, e2); } } return newArgs == null ? args : newArgs; } /** * Like castActualsToFormals, but for a single Expr. * Used in a few additional cases where an exact deep base type equality * is required for C++ level compilation to work as expected. */ private Expr upcast(Expr a, Type fType) { if (Types.baseType(fType) instanceof FunctionType) { return upcastToFunctionType(a, (FunctionType)Types.baseType(fType), false); } else if (!ts.typeDeepBaseEquals(fType, a.type(), context)) { Position pos = a.position(); return makeCast(a.position(), a, fType); } else { return a; } } /** * Check to see if the upcast of the argument expression to the * given type requires an explicit cast operation to perform * a C++-level representation change (eg boxing). * If the cast is needed, insert it. * If the cast is not needed (eg if both types are classes and is is an upcast) * then do nothing, even if the types are not equal. */ private Expr boxingUpcastIfNeeded(Expr a, Type fType) { if (a instanceof NullLit_c) { return makeCast(a.position(), a, fType); } if (Types.baseType(fType) instanceof FunctionType) { return upcastToFunctionType(a, (FunctionType)Types.baseType(fType), true); } if (ts.isObjectType(a.type(), context) && ts.isObjectType(fType, context)) { // implicit C++ level upcast is good enough (C++ and X10 have same class hierarchy structure) return a; } if (!ts.typeDeepBaseEquals(fType, a.type(), context)) { Position pos = a.position(); return makeCast(a.position(), a, fType); } else { return a; } } private Expr upcastToFunctionType(Expr e, FunctionType castFType, boolean allowImplicitCasts) { boolean exactMatch = false; FunctionType exprFType = null; if (e instanceof NullLit) { // can force exactMatch to be true because a NPE will be raised at runtime // if the function is actually applied. Therefore we don't need to worry about // a mismatch between expected and actual argument types. exactMatch = true; } else { List<FunctionType> cands = Types.functionTypes(e.type()); for (FunctionType ft : cands) { if (ft.argumentTypes().size() == castFType.argumentTypes().size()) { exprFType = ft; break; } } if (exprFType == null) { throw new InternalCompilerError("Can't find valid function type on upcast of "+e.type()+"to "+castFType); } exactMatch = ts.typeDeepBaseEquals(exprFType.returnType(), castFType.returnType(), context); if (exactMatch) { for (int i=0; i<exprFType.argumentTypes().size(); i++) { Type ea = exprFType.argumentTypes().get(i); Type ca = castFType.argumentTypes().get(i); if (!ts.typeDeepBaseEquals(ea, ca, context)) { exactMatch = false; break; } } } } if (exactMatch) { if (e instanceof Closure_c) { // C++ code generated for the allocation of a closure literal // already does an upcast to the appropriate function type return e; } if (e.type() instanceof FunctionType) { // Already at the right C++ level interface type. return e; } if (allowImplicitCasts && ts.isObjectOrInterfaceType(e.type(), context)) { // C++ level ref to ref conversion is good enough return e; } Position pos = e.position(); return makeCast(e.position(), e, castFType); } else { Position pos = e.position(); List<Formal> formals = new ArrayList<Formal>(); for (Type ft : castFType.argumentTypes()) { Formal f = synth.createFormal(pos, ft, Name.makeFresh("arg"), Flags.FINAL); formals.add(f); } List<Expr> actuals = new ArrayList<Expr>(); for (int i=0; i<formals.size(); i++) { Formal f = formals.get(i); Type inner = exprFType.argumentTypes().get(i); Local ref = synth.createLocal(pos, f.localDef().asInstance()); if (ts.typeDeepBaseEquals(ref.type(), inner, context)) { actuals.add(ref); } else { actuals.add(makeCast(pos, ref, inner)); } } Expr call = nf.ClosureCall(pos, e, actuals).closureInstance(exprFType.applyMethod()).type(exprFType.returnType()); if (!ts.typeDeepBaseEquals(call.type(), castFType.returnType(), context)) { call = makeCast(pos, call, castFType.returnType()); } Block body = nf.Block(pos, castFType.returnType().isVoid() ? nf.Eval(pos, call) : nf.Return(pos, call)); Closure c = synth.makeClosure(pos, castFType.returnType(), formals, body, context); c.visit(new ClosureCaptureVisitor(this.context(), c.closureDef())); return c; } } private Expr makeCast(Position pos, Expr e, Type t) { return nf.X10Cast(pos, nf.CanonicalTypeNode(pos, t), e, Converter.ConversionType.UNCHECKED).type(t); } }