/* * 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.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import polyglot.ast.Binary; import polyglot.ast.Call; import polyglot.ast.Expr; import polyglot.ast.IntLit; import polyglot.ast.NamedVariable; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.TypeNode; import polyglot.ast.Unary; import polyglot.ast.Unary_c; import polyglot.ast.Variable; import polyglot.types.ClassDef; import polyglot.types.ClassType; import polyglot.types.Flags; import polyglot.types.MethodDef; import polyglot.types.Name; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.Types; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.visit.ContextVisitor; import x10.errors.Errors; import x10.errors.Warnings; import x10.types.MethodInstance; import x10.types.X10Use; import polyglot.types.TypeSystem; import x10.types.checker.Checker; import x10.types.checker.Converter; import x10.types.checker.PlaceChecker; import x10.types.constants.ConstantValue; /** * An immutable representation of a unary operation op Expr. * Overridden from Java to allow unary negation of points. * * @author igor Feb 15, 2006 */ public class X10Unary_c extends Unary_c { /** * @param pos * @param op * @param expr */ public X10Unary_c(Position pos, Operator op, Expr expr) { super(pos, op, expr); } public ConstantValue constantValue() { return super.constantValue(); } public static Binary.Operator getBinaryOp(Operator op) { if (op == PRE_INC || op == POST_INC) { return Binary.ADD; } else if (op == PRE_DEC || op == POST_DEC) { return Binary.SUB; } return null; } /** * Type check a unary expression. Must take care of various cases because * of operators on regions, distributions, points, places and arrays. * An alternative implementation strategy is to resolve each into a method * call. */ public Node typeCheck(ContextVisitor tc) { TypeSystem ts = (TypeSystem) tc.typeSystem(); NodeFactory nf = (NodeFactory) tc.nodeFactory(); Operator op = this.operator(); if (op == NEG && expr instanceof IntLit) { final IntLit intLit = (IntLit) expr; IntLit.Kind kind = intLit.kind(); IntLit lit = nf.IntLit(position(), kind, -intLit.longValue()); try { return Converter.check(lit, tc); } catch (SemanticException e) { throw new InternalCompilerError("Unexpected exception when typechecking "+lit, lit.position(), e); } } Type t = expr.type(); if (op == POST_INC || op == POST_DEC || op == PRE_INC || op == PRE_DEC) { if (!ts.isNumeric(t)) { Errors.issue(tc.job(), new SemanticException("Operator ++ and -- can only be used on built in numerical types.",position())); return this.type(t); } // Compute the type and the expected type Type et = t; if (expr instanceof Variable) { Variable v = (Variable) expr; if (v.flags().isFinal()) { Errors.issue(tc.job(), new Errors.CannotApplyToFinalVariable(op, position())); } if (v instanceof NamedVariable) { et = ((NamedVariable) v).varInstance().type(); } } else { Expr target = null; List<TypeNode> typeArgs = null; List<Expr> args = null; // Handle a(i)++ and a.apply(i)++ if (expr instanceof ClosureCall) { ClosureCall e = (ClosureCall) expr; target = e.target(); typeArgs = e.typeArguments(); args = e.arguments(); } else if (expr instanceof X10Call) { X10Call e = (X10Call) expr; if (!(e.target() instanceof Expr) || e.name().id() != ClosureCall.APPLY) { Errors.issue(tc.job(), new Errors.CannotApplyToArbitraryMethodCall(op, position())); t = ts.unknownType(position()); et = null; } else { target = (Expr) e.target(); typeArgs = e.typeArguments(); args = e.arguments(); } } else { Errors.issue(tc.job(), new Errors.CannotApplyToArbitraryExpression(op, position())); t = ts.unknownType(position()); et = null; } if (target != null) { List<Type> tArgs = new ArrayList<Type>(); for (TypeNode tn : typeArgs) { tArgs.add(tn.type()); } List<Type> actualTypes = new ArrayList<Type>(); for (Expr a : args) { actualTypes.add(a.type()); } // value goes after args actualTypes.add(t); MethodInstance mi = Checker.findAppropriateMethod(tc, target.type(), SettableAssign.SET, tArgs, actualTypes); Warnings.checkErrorAndGuard(tc, mi, this); // Make sure we don't coerce here. List<Type> fTypes = mi.formalTypes(); for (int i = 0; i < actualTypes.size(); i++) { if (!ts.isSubtype(actualTypes.get(i), fTypes.get(i), tc.context())) Errors.issue(tc.job(), new Errors.NoMethodFoundInType(SettableAssign.SET, target.type(), position())); } t = mi.returnType(); et = fTypes.get(fTypes.size()-1); } } if (et != null) { // Check that there's a binary operator with the right return type IntLit lit = nf.IntLit(position(), IntLit.INT, 1); try { lit = Converter.check(lit, tc); } catch (SemanticException e) { throw new InternalCompilerError("Unexpected exception when typechecking "+lit, lit.position(), e); } Binary.Operator binaryOp = getBinaryOp(op); Call c = X10Binary_c.desugarBinaryOp(nf.Binary(position(), expr, binaryOp, lit), tc); if (c == null) { Errors.issue(tc.job(), new Errors.NoBinaryOperatorFoundInType(binaryOp, t, expr.position())); } else { MethodInstance mi = (MethodInstance) c.methodInstance(); Warnings.checkErrorAndGuard(tc, mi, this); Type resultType = mi.returnType(); if (!ts.isSubtype(resultType, et, tc.context())) { Errors.issue(tc.job(), new Errors.IncompatibleReturnTypeOfBinaryOperator(binaryOp, resultType, et, expr.position())); } } } return this.type(t); } Call c = desugarUnaryOp(this, tc); if (c != null) { MethodInstance mi = (MethodInstance) c.methodInstance(); Warnings.checkErrorAndGuard(tc, mi, this); // rebuild the unary using the call's arguments. We'll actually use the call node after desugaring. Type resultType = c.type(); resultType = ts.performUnaryOperation(resultType, t, op); if (mi.flags().isStatic()) { return this.expr(c.arguments().get(0)).type(resultType); } else { return this.expr((Expr) c.target()).type(resultType); } } if (!ts.hasUnknown(t)) { Errors.issue(tc.job(), new Errors.NoOperationFoundForOperand(op, t, position())); } return this.type(ts.unknownType(position())); } public static X10Call_c desugarUnaryOp(Unary n, ContextVisitor tc) { Expr left = n.expr(); Operator op = n.operator(); Position pos = n.position(); Type l = left.type(); NodeFactory nf = (NodeFactory) tc.nodeFactory(); Name methodName = unaryMethodName(op); if (methodName == null) return null; // TODO: byte+byte should convert both bytes to int and search int // For now, we have to define byte+byte in byte.x10. X10Call_c virtual_left = null; X10Call_c static_left = null; if (methodName != null) { // Check if there is a method with the appropriate name and type with the operand as receiver. X10Call_c n2 = (X10Call_c) nf.X10Call(pos, left, nf.Id(pos, methodName), Collections.<TypeNode>emptyList(), Collections.<Expr>emptyList()); n2 = X10Binary_c.typeCheckCall(tc, n2); MethodInstance mi2 = (MethodInstance) n2.methodInstance(); if (mi2.error() == null && !mi2.def().flags().isStatic()) virtual_left = n2; } if (methodName != null) { // Check if there is a static method of the left type with the appropriate name and type. X10Call_c n4 = (X10Call_c) nf.X10Call(pos, nf.CanonicalTypeNode(pos, Types.ref(l)), nf.Id(pos, methodName), Collections.<TypeNode>emptyList(), Collections.singletonList(left)); n4 = X10Binary_c.typeCheckCall(tc, n4); MethodInstance mi4 = (MethodInstance) n4.methodInstance(); if (mi4.error() == null && mi4.def().flags().isStatic()) static_left = n4; } List<X10Call_c> defs = new ArrayList<X10Call_c>(); if (virtual_left != null) defs.add(virtual_left); if (static_left != null) defs.add(static_left); if (defs.size() == 0) return null; TypeSystem xts = tc.typeSystem(); List<X10Call_c> best = new ArrayList<X10Call_c>(); X10Binary_c.Conversion bestConversion = X10Binary_c.Conversion.UNKNOWN; for (int i = 0; i < defs.size(); i++) { X10Call_c n1 = defs.get(i); // Check if n needs a conversion Expr[] actuals = new Expr[] { n1.arguments().size() != 1 ? (Expr) n1.target() : n1.arguments().get(0) }; Expr[] original = new Expr[] { left }; X10Binary_c.Conversion conversion = X10Binary_c.conversionNeeded(actuals, original); if (bestConversion.harder(conversion)) { best.clear(); best.add(n1); bestConversion = conversion; } else if (conversion.harder(bestConversion)) { // best is still the best } else { // all other things being equal MethodDef md = n1.methodInstance().def(); Type td = Types.get(md.container()); ClassDef cd = X10Binary_c.def(td); boolean isBetter = false; for (Iterator<X10Call_c> ci = best.iterator(); ci.hasNext();) { X10Call_c c = ci.next(); MethodDef bestmd = c.methodInstance().def(); assert (bestmd != md) : pos.toString(); if (bestmd == md) break; // same method by a different path (shouldn't happen for unary) Type besttd = Types.get(bestmd.container()); if (xts.isUnknown(besttd) || xts.isUnknown(td)) { // going to create a fake one anyway; might as well get more data isBetter = true; continue; } ClassDef bestcd = X10Binary_c.def(besttd); assert (bestcd != null && cd != null); if (xts.descendsFrom(cd, bestcd)) { // we found the method of a subclass; remove the superclass one ci.remove(); isBetter = true; assert (bestConversion == conversion); bestConversion = conversion; } else if (xts.descendsFrom(bestcd, cd)) { // best is still the best isBetter = false; break; } else { isBetter = true; } } if (isBetter) best.add(n1); } } assert (best.size() != 0); X10Call_c result = best.get(0); if (best.size() > 1) { List<MethodInstance> bestmis = new ArrayList<MethodInstance>(); Type rt = null; boolean rtset = false; ClassType ct = null; boolean ctset = false; // See if all matches have the same container and return type, and save that to avoid losing information. for (X10Call_c c : best) { MethodInstance xmi = c.methodInstance(); bestmis.add(xmi); if (!rtset) { rt = xmi.returnType(); rtset = true; } else if (rt != null && !xts.typeEquals(rt, xmi.returnType(), tc.context())) { if (xts.typeBaseEquals(rt, xmi.returnType(), tc.context())) { rt = Types.baseType(rt); } else { rt = null; } } if (!ctset) { ct = xmi.container().toClass(); ctset = true; } else if (ct != null && !xts.typeEquals(ct, xmi.container(), tc.context())) { if (xts.typeBaseEquals(ct, xmi.container(), tc.context())) { ct = Types.baseType(ct).toClass(); } else { ct = null; } } } if (ct == null) ct = l.toClass(); SemanticException error = new Errors.AmbiguousOperator(op, bestmis, pos); MethodInstance mi = xts.createFakeMethod(ct, Flags.PUBLIC.Static(), methodName, Collections.<Type>emptyList(), Collections.singletonList(l), error); if (rt != null) mi = mi.returnType(rt); result = (X10Call_c) nf.X10Call(pos, nf.CanonicalTypeNode(pos, Types.ref(ct)), nf.Id(pos, methodName), Collections.<TypeNode>emptyList(), Collections.singletonList(left)).methodInstance(mi).type(mi.returnType()); } try { result = (X10Call_c) PlaceChecker.makeReceiverLocalIfNecessary(result, tc); } catch (SemanticException e) { MethodInstance mi = (MethodInstance) result.methodInstance(); if (mi.error() == null) result = (X10Call_c) result.methodInstance(mi.error(e)); } if (n.isConstant()) result = result.constantValue(n.constantValue()); return result; } private static final Map<Operator,Name> OPERATOR_NAMES = CollectionFactory.newHashMap(); static { Map<Operator,Name> methodNameMap = OPERATOR_NAMES; methodNameMap.put(NEG, OperatorNames.MINUS); methodNameMap.put(POS, OperatorNames.PLUS); methodNameMap.put(NOT, OperatorNames.BANG); methodNameMap.put(BIT_NOT, OperatorNames.TILDE); methodNameMap.put(CARET, OperatorNames.CARET); methodNameMap.put(BAR, OperatorNames.BAR); methodNameMap.put(AMPERSAND, OperatorNames.AMPERSAND); methodNameMap.put(STAR, OperatorNames.STAR); methodNameMap.put(SLASH, OperatorNames.SLASH); methodNameMap.put(PERCENT, OperatorNames.PERCENT); } public static Name unaryMethodName(Operator op) { return OPERATOR_NAMES.get(op); } }