@Override public Node leave(Node parent, Node old, Node n, NodeVisitor v) { // do context bookkeeping first if (old instanceof X10MethodDecl) { assert ((X10MethodDecl) old).returnType().type() == returnType.peek(); returnType.pop(); } // NB: for calls and closure calls, the rules for boxing are complicated, // so we insert a simple Cast AST node, so that codegen makes a decision // whether to do boxing/unboxing. The goal is to ensure, that // the only places, where codegen needs to consider boxing/unboxing // are: // * casts // * method or closure call arguments // Generic methods (return type T) can return boxed values (e.g., x10.core.UInt) // but in the type instantiated [UInt] subclass it is treated as unboxed int. // We need to insert explicit cast to // an unboxed type, so that proper conversion method call is inserted later. if (n instanceof X10Call && !(parent instanceof Eval)) { X10Call call = (X10Call) n; Receiver target = call.target(); MethodInstance mi = call.methodInstance(); Type expectedReturnType = call.type(); // do not insert cast the expected type is already boxed or is void if (X10PrettyPrinterVisitor.isBoxedType(expectedReturnType) || expectedReturnType.isVoid() // (void) never needs boxing // or if the method def type is void -- it is a synthetic method without a proper // definition || mi.def().returnType().get().isVoid()) return n; if (X10PrettyPrinterVisitor.isBoxedType(mi.def().returnType().get())) { // only insert cast if the actual returned type is boxed return cast(call, expectedReturnType); } } // Closures may be implemented by methods returning boxed or unboxed values, // depending on some involved condition that is checked in codegen. // So add the dummy type cast to give the codegen // chance to handle unboxing in a single place if (n instanceof ClosureCall && !(parent instanceof Eval) && !(parent instanceof Cast)) { ClosureCall call = (ClosureCall) n; // if the return type is not primitive, then unboxing will not be needed if (X10PrettyPrinterVisitor.isBoxedType(call.type()) || call.type().isVoid()) return n; return cast(call, call.type()); } if (n instanceof Expr) { // boxing may be needed only for expressions Expr expr = (Expr) n; // parent node still has "old" as its child if (isBoxed(expr) && expectsUnboxed(parent, old)) { return unbox(expr); } else if (isUnboxed(expr) && expectsBoxed(parent, old)) { return box(expr); } } return n; }
private boolean isBoxed(ClosureCall call) { // FIXME: these conditions are copied over from Emitter.printApplyMethodName() and // X10PrettyPrinterVisitor.visit(ClosureCall) Expr target = call.target(); if (target instanceof ParExpr) { target = ((ParExpr) target).expr(); } boolean newClosure = target instanceof Closure_c; MethodInstance mi = call.closureInstance(); if ((!newClosure && !mi.returnType().isVoid() && mi.formalTypes().size() == 0) || !(mi.returnType().isVoid() || (newClosure && !mi.returnType().isParameterType()))) { // in this case generic apply$G is used return true; } Type type = call.closureInstance().def().returnType().get(); if (isBoxedType(type)) return true; return false; }