/* * 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.Expr_c; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Precedence; import polyglot.ast.Term; import polyglot.ast.TypeNode; import polyglot.types.ErrorRef_c; import polyglot.types.Matcher; import polyglot.types.MethodDef; import polyglot.types.ProcedureDef; import polyglot.types.ProcedureInstance; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.util.CodeWriter; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.util.Pair; import polyglot.util.Position; import polyglot.util.TypedList; import polyglot.visit.CFGBuilder; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import polyglot.visit.PrettyPrinter; import polyglot.visit.TypeBuilder; import x10.errors.Errors; import x10.errors.Warnings; import x10.types.FunctionType; import x10.types.MethodInstance; import polyglot.types.TypeSystem; import x10.types.checker.Checker; import x10.types.checker.Converter; import x10.visit.X10TypeChecker; public class ClosureCall_c extends Expr_c implements ClosureCall { protected Expr target; protected List<Expr> arguments; protected List<TypeNode> typeArguments; protected MethodInstance ci; public ClosureCall_c(Position pos, Expr target, List<TypeNode> typeArguments, List<Expr> arguments) { super(pos); assert target != null; assert typeArguments != null; assert arguments != null; this.target = target; this.typeArguments = TypedList.copyAndCheck(typeArguments, TypeNode.class, true); this.arguments = TypedList.copyAndCheck(arguments, Expr.class, true); } @Override public Precedence precedence() { return Precedence.LITERAL; } public Term firstChild() { if (!(target instanceof Closure)) { return ((Term) target); } return listChild(arguments, null); } @Override public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) { List<Term> args = new ArrayList<Term>(); args.addAll(typeArguments); args.addAll(arguments); if (!(target instanceof Closure)) { // Don't visit a literal closure here Term t = (Term) target; if (args.isEmpty()) { v.visitCFG(t, this, EXIT); } else { v.visitCFG(t, listChild(args, null), ENTRY); } } v.visitCFGList(args, this, EXIT); return succs; } public Expr target() { return target; } public ClosureCall target(Expr target) { assert target != null; ClosureCall_c n= (ClosureCall_c) copy(); n.target= target; return n; } /** Get the method instance of the call. */ public MethodInstance closureInstance() { return this.ci; } /** Set the method instance of the call. */ public ClosureCall closureInstance(MethodInstance ci) { if (ci == this.ci) return this; ClosureCall_c n = (ClosureCall_c) copy(); n.ci= ci; return n; } public ProcedureInstance<?> procedureInstance() { return this.ci; } public ClosureCall procedureInstance(ProcedureInstance<? extends ProcedureDef> pi) { return closureInstance((MethodInstance) pi); } /** Get the actual arguments of the call. */ public List<Expr> arguments() { return this.arguments; } /** Set the actual arguments of the call. */ public ClosureCall arguments(List<Expr> arguments) { assert arguments != null; ClosureCall_c n= (ClosureCall_c) copy(); n.arguments= TypedList.copyAndCheck(arguments, Expr.class, true); return n; } /** Get the type arguments of the call. */ public List<TypeNode> typeArguments() { return this.typeArguments; } /** Set the type arguments of the call. */ public ClosureCall typeArguments(List<TypeNode> typeArguments) { assert typeArguments != null; ClosureCall_c n= (ClosureCall_c) copy(); n.typeArguments= TypedList.copyAndCheck(typeArguments, TypeNode.class, true); return n; } /** Reconstruct the call. */ protected ClosureCall_c reconstruct(Expr target, List<TypeNode> typeArguments, List<Expr> arguments) { if (target != this.target || !CollectionUtil.allEqual(typeArguments, this.typeArguments) || !CollectionUtil.allEqual(arguments, this.arguments)) { ClosureCall_c n= (ClosureCall_c) copy(); n.target= target; n.typeArguments= TypedList.copyAndCheck(typeArguments, TypeNode.class, true); n.arguments= TypedList.copyAndCheck(arguments, Expr.class, true); return n; } return this; } /** Visit the children of the call. */ public Node visitChildren(NodeVisitor v) { Expr target = (Expr) visitChild(this.target, v); List typeArguments = visitList(this.typeArguments, v); List<Expr> arguments = visitList(this.arguments, v); return reconstruct(target, typeArguments, arguments); } public Node buildTypes(TypeBuilder tb) { ClosureCall_c n= (ClosureCall_c) super.buildTypes(tb); TypeSystem ts = (TypeSystem) tb.typeSystem(); MethodInstance mi = (MethodInstance) ts.createMethodInstance(position(), position(), new ErrorRef_c<MethodDef>(ts, position(), "Cannot get MethodDef before type-checking closure call.")); return n.closureInstance(mi); } @Override public Node typeCheck(ContextVisitor tc) { Type targetType = target.type(); List<Type> typeArgs = new ArrayList<Type>(this.typeArguments.size()); for (TypeNode ti : typeArguments) { typeArgs.add(ti.type()); } List<Type> actualTypes = new ArrayList<Type>(this.arguments.size()); for (Expr ei : arguments) { actualTypes.add(ei.type()); } // First try to find the method without implicit conversions. MethodInstance mi = Checker.findAppropriateMethod(tc, targetType, APPLY, typeArgs, actualTypes); List<Expr> args = this.arguments; if (mi.error() != null) { // Now, try to find the method with implicit conversions, making them explicit. try { Pair<MethodInstance,List<Expr>> p = Checker.tryImplicitConversions(this, tc, targetType, APPLY, typeArgs, actualTypes); mi = p.fst(); args = p.snd(); } catch (SemanticException e) { // mi.error() will be reported in checkErrorAndGuard } } Warnings.checkErrorAndGuard(tc,mi,this); // Find the most-specific closure type. // ClosureCall n = this; // n = n.arguments(args); // return n.closureInstance(mi).type(mi.returnType()); if (mi.container() instanceof FunctionType) { ClosureCall_c n = this; n = (ClosureCall_c) n.arguments(args); return n.closureInstance(mi).type(mi.returnType()); } else { // FIXME: deleting this code block breaks the Java code generator. Investigate. // TODO: add ts.Function and uncomment this. // Check that the target actually is of a function type of the appropriate type and doesn't just coincidentally implement an // apply method. // ClosureType ct = ts.Function(mi.typeParameters(), mi.formalTypes(), mi.returnType()); // if (! targetType.isSubtype(ct)) // throw new SemanticException("Invalid closure call; target does not implement " + ct + ".", position()); NodeFactory nf = (NodeFactory) tc.nodeFactory(); X10Call_c n = (X10Call_c) nf.X10Call(position(), target(), nf.Id(Position.compilerGenerated(position()), mi.name().toString()), typeArguments, args); n = (X10Call_c) n.methodInstance(mi).type(mi.returnType()); return n; } } public String toString() { StringBuffer buff= new StringBuffer(); buff.append(target) .append("("); for(Iterator<Expr> iter= arguments.iterator(); iter.hasNext(); ) { Expr arg= (Expr) iter.next(); buff.append(arg); if (iter.hasNext()) buff.append(", "); } buff.append(")"); return buff.toString(); } public void prettyPrint(CodeWriter w, PrettyPrinter tr) { w.begin(0); printSubExpr((Expr) target, w, tr); w.write("("); if (arguments.size() > 0) { w.allowBreak(2, 2, "", 0); // miser mode w.begin(0); for(Iterator<Expr> i = arguments.iterator(); i.hasNext();) { Expr e = (Expr) i.next(); print(e, w, tr); if (i.hasNext()) { w.write(","); w.allowBreak(0, " "); } } w.end(); } w.write(")"); w.end(); } }