/*
* 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.AmbExpr;
import polyglot.ast.Binary;
import polyglot.ast.Binary_c;
import polyglot.ast.BooleanLit;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.IntLit;
import polyglot.ast.IntLit.Kind;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Precedence;
import polyglot.ast.Prefix;
import polyglot.ast.Receiver;
import polyglot.ast.StringLit;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
import polyglot.util.InternalCompilerError;
import polyglot.util.Pair;
import polyglot.util.Position;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.FlowGraph;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import x10.constraint.XFailure;
import x10.constraint.XLit;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.errors.Errors;
import x10.errors.Warnings;
import x10.types.MethodInstance;
import x10.types.X10ClassType;
import x10.types.X10Use;
import x10.types.checker.Checker;
import x10.types.checker.Converter;
import x10.types.checker.PlaceChecker;
import x10.types.constants.*;
import x10.types.constraints.BuiltInTypeRules;
/**
* An immutable representation of a binary operation Expr op Expr.
* Overridden from Java to allow distributions, regions, points and places to
* participate in binary operations.
*
* @author vj Jan 21, 2005
*/
public class X10Binary_c extends Binary_c {
protected Expr left;
protected Operator op;
protected Expr right;
protected Precedence precedence;
/**
* @param pos
* @param left
* @param op
* @param right
*/
public X10Binary_c(Position pos, Expr left, Operator op, Expr right) {
super(pos, left, op, right);
assert(left != null && op != null && right != null);
this.left = left;
this.op = op;
this.right = right;
this.precedence = op.precedence();
if (op == ADD && (left instanceof StringLit || right instanceof StringLit)) {
this.precedence = Precedence.STRING_ADD;
}
invert = false;
}
private boolean invert;
public boolean invert() {
return invert;
}
public X10Binary_c invert(boolean invert) {
if (invert == this.invert) return this;
X10Binary_c n = (X10Binary_c) copy();
n.invert = invert;
return n;
}
private MethodInstance mi;
public MethodInstance methodInstance() {
return mi;
}
public X10Binary_c methodInstance(MethodInstance mi) {
if (mi == this.mi) return this;
X10Binary_c n = (X10Binary_c) copy();
n.mi = mi;
return n;
}
// FIXME: need to figure out if the implementation is pure (can't assume that for user-overloadable operators)
protected static boolean isPureOperation(Type left, Operator op, Type right) {
switch (op) {
case ADD:
case SUB:
case MUL:
return true;
case DIV:
case MOD:
return false; // division/modulus can throw an exception, thus is not pure
case EQ:
case NE:
case GT:
case LT:
case GE:
case LE:
return true;
case SHL:
case SHR:
case USHR:
return true;
case BIT_AND:
case BIT_OR:
case BIT_XOR:
return true;
case COND_AND:
case COND_OR:
return true;
case ARROW:
case DOT_DOT:
return false;
}
return false;
}
public boolean isConstant() {
boolean lconst = left.isConstant();
Type lt = left.type();
if (lconst && lt.isBoolean()) {
if ((op == COND_AND || op == BIT_AND) && ((BooleanValue) left.constantValue()).value() == false) {
// false && e == false
return true;
}
if ((op == COND_OR || op == BIT_OR) && ((BooleanValue) left.constantValue()).value() == true) {
// true || e == true
return true;
}
}
boolean rconst = right.isConstant();
Type rt = right.type();
if (lconst && rconst && isPureOperation(lt, op, rt)) {
if (op == EQ || op == NE) {
// Additional checks for type equality because conversions not applied for ==
TypeSystem xts = (TypeSystem) lt.typeSystem();
if (lt == null || rt == null)
return false;
if (lt.isClass() && rt.isClass()) {
X10ClassType ltc = (X10ClassType)lt.toClass();
X10ClassType rtc = (X10ClassType)rt.toClass();
if (ltc.isX10Struct() || rtc.isX10Struct()) {
return xts.typeBaseEquals(ltc, rtc, xts.emptyContext());
}
}
}
return true;
}
return false;
}
public ConstantValue constantValue() {
ConstantValue result = superConstantValue();
if (result != null)
return result;
if (!isConstant())
return null;
Type lt = left.type();
Type rt = right.type();
TypeSystem xts = (TypeSystem) lt.typeSystem();
Context context = (Context) xts.emptyContext();
// [IP] An optimization: a struct and null can never be equal
if (op == EQ) {
if (xts.isStructType(lt) && rt.isNull())
return ConstantValue.makeBoolean(false);
if (xts.isStructType(rt) && lt.isNull())
return ConstantValue.makeBoolean(false);
}
if (op == NE) {
if (xts.isStructType(lt) && rt.isNull())
return ConstantValue.makeBoolean(true);
if (xts.isStructType(rt) && lt.isNull())
return ConstantValue.makeBoolean(true);
return null;
}
return null;
}
private ConstantValue superConstantValue() {
if (!isConstant()) {
return null;
}
ConstantValue lv = left.constantValue();
ConstantValue rv = right.constantValue();
if (lv instanceof BooleanValue) {
boolean l = ((BooleanValue) lv).value();
// false && e == false
if (op == BIT_AND && l == false) return ConstantValue.makeBoolean(l);
if (op == COND_AND && l == false) return ConstantValue.makeBoolean(l);
// true || e == true
if (op == BIT_OR && l == true) return ConstantValue.makeBoolean(l);
if (op == COND_OR && l == true) return ConstantValue.makeBoolean(l);
}
if (lv == null || rv == null) {
return null;
}
if (op == ADD && (lv instanceof StringValue || rv instanceof StringValue)) {
// toString() on a ConstantValue gives the same String as toString on the value itself.
return ConstantValue.makeString(lv.toString() + rv.toString());
}
if (op == EQ && (lv instanceof StringValue && rv instanceof StringValue)) {
return ConstantValue.makeBoolean(((StringValue) lv).value().equals(((StringValue)rv).value()));
}
if (op == NE && (lv instanceof StringValue && rv instanceof StringValue)) {
return ConstantValue.makeBoolean(!((StringValue) lv).value().equals(((StringValue)rv).value()));
}
if (lv instanceof BooleanValue && rv instanceof BooleanValue) {
boolean l = ((BooleanValue) lv).value();
boolean r = ((BooleanValue)rv).value();
if (op == EQ) return ConstantValue.makeBoolean(l == r);
if (op == NE) return ConstantValue.makeBoolean(l != r);
if (op == BIT_AND) return ConstantValue.makeBoolean(l & r);
if (op == BIT_OR) return ConstantValue.makeBoolean(l | r);
if (op == BIT_XOR) return ConstantValue.makeBoolean(l ^ r);
if (op == COND_AND) return ConstantValue.makeBoolean(l && r);
if (op == COND_OR) return ConstantValue.makeBoolean(l || r);
}
try {
if (lv instanceof DoubleValue || rv instanceof DoubleValue) {
// args promoted to double if needed and result will be a double
double l = lv.doubleValue();
double r = rv.doubleValue();
if (op == ADD) return ConstantValue.makeDouble(l + r);
if (op == SUB) return ConstantValue.makeDouble(l - r);
if (op == MUL) return ConstantValue.makeDouble(l * r);
if (op == DIV) return ConstantValue.makeDouble(l / r);
if (op == MOD) return ConstantValue.makeDouble(l % r);
if (op == EQ) return ConstantValue.makeBoolean(l == r);
if (op == NE) return ConstantValue.makeBoolean(l != r);
if (op == LT) return ConstantValue.makeBoolean(l < r);
if (op == LE) return ConstantValue.makeBoolean(l <= r);
if (op == GE) return ConstantValue.makeBoolean(l >= r);
if (op == GT) return ConstantValue.makeBoolean(l > r);
}
if (lv instanceof FloatValue || rv instanceof FloatValue) {
// args promoted to float if needed and result will be a float
float l = lv.floatValue();
float r = rv.floatValue();
if (op == ADD) return ConstantValue.makeFloat(l + r);
if (op == SUB) return ConstantValue.makeFloat(l - r);
if (op == MUL) return ConstantValue.makeFloat(l * r);
if (op == DIV) return ConstantValue.makeFloat(l / r);
if (op == MOD) return ConstantValue.makeFloat(l % r);
if (op == EQ) return ConstantValue.makeBoolean(l == r);
if (op == NE) return ConstantValue.makeBoolean(l != r);
if (op == LT) return ConstantValue.makeBoolean(l < r);
if (op == LE) return ConstantValue.makeBoolean(l <= r);
if (op == GE) return ConstantValue.makeBoolean(l >= r);
if (op == GT) return ConstantValue.makeBoolean(l > r);
}
if (lv instanceof CharValue || rv instanceof CharValue) {
long l = lv.integralValue();
long r = rv.integralValue();
if (op == ADD) return ConstantValue.makeChar((char)(l + r));
if (op == SUB) return ConstantValue.makeChar((char)(l -r));
if (op == EQ) return ConstantValue.makeBoolean(l == r);
if (op == NE) return ConstantValue.makeBoolean(l != r);
if (op == LT) return ConstantValue.makeBoolean(l < r);
if (op == LE) return ConstantValue.makeBoolean(l <= r);
if (op == GE) return ConstantValue.makeBoolean(l >= r);
if (op == GT) return ConstantValue.makeBoolean(l > r);
}
if (lv instanceof IntegralValue || rv instanceof IntegralValue) {
long l = lv.integralValue();
long r = rv.integralValue();
IntLit.Kind lk = null;
IntLit.Kind rk = null;
if (lv instanceof IntegralValue) {
lk = ((IntegralValue)lv).kind();
}
if (rv instanceof IntegralValue) {
rk = ((IntegralValue)rv).kind();
}
if (op == ADD) return ConstantValue.makeIntegral(l + r, meet(lk, rk));
if (op == SUB) return ConstantValue.makeIntegral(l -r, meet(lk, rk));
if (op == MUL) return ConstantValue.makeIntegral(l * r, meet(lk, rk));
if (op == DIV) return ConstantValue.makeIntegral(l / r, meet(lk, rk));;
if (op == MOD) return ConstantValue.makeIntegral(l % r, meet(lk, rk));;
if (op == EQ) return ConstantValue.makeBoolean(lk == rk && l == r);
if (op == NE) return ConstantValue.makeBoolean(lk != rk || l != r);
if (op == BIT_AND) return ConstantValue.makeIntegral(l & r, meet(lk, rk));;
if (op == BIT_OR) return ConstantValue.makeIntegral(l | r, meet(lk, rk));
if (op == BIT_XOR) return ConstantValue.makeIntegral(l ^ r, meet(lk, rk));
if (op == SHL) return ConstantValue.makeIntegral(l << r, lk);
if (op == SHR) {
if (lk.isSigned()) {
return ConstantValue.makeIntegral(l >> r, lk);
} else {
return ConstantValue.makeIntegral(l >>> r, lk);
}
}
if (op == USHR) return ConstantValue.makeIntegral(l >>> r, lk);
if (meet(lk, rk).isSigned()) {
if (op == LT) return ConstantValue.makeBoolean(l < r);
if (op == LE) return ConstantValue.makeBoolean(l <= r);
if (op == GE) return ConstantValue.makeBoolean(l >= r);
if (op == GT) return ConstantValue.makeBoolean(l > r);
} else {
long lAdj = l + Long.MIN_VALUE;
long rAdj = r + Long.MIN_VALUE;
if (op == LT) return ConstantValue.makeBoolean(lAdj < rAdj);
if (op == LE) return ConstantValue.makeBoolean(lAdj <= rAdj);
if (op == GE) return ConstantValue.makeBoolean(lAdj >= rAdj);
if (op == GT) return ConstantValue.makeBoolean(lAdj > rAdj);
}
}
} catch (ArithmeticException e) {
// Actually unreachable because MOD/DIV are not pure.
// Therefore isConstant will return false.
// However, leave in place for future in case we remove the isPure check in isConstant
return null;
}
return null;
}
private Kind meet(Kind lk, Kind rk) {
if (lk == rk) return lk;
if (lk == IntLit.Kind.ULONG || rk == IntLit.Kind.ULONG) return IntLit.Kind.ULONG;
if (lk == IntLit.Kind.LONG || rk == IntLit.Kind.LONG) return IntLit.Kind.LONG;
if (lk == IntLit.Kind.UINT || rk == IntLit.Kind.UINT) return IntLit.Kind.UINT;
if (lk == IntLit.Kind.INT || rk == IntLit.Kind.INT) return IntLit.Kind.INT;
if (lk == IntLit.Kind.USHORT || rk == IntLit.Kind.USHORT) return IntLit.Kind.USHORT;
if (lk == IntLit.Kind.SHORT || rk == IntLit.Kind.SHORT) return IntLit.Kind.SHORT;
if (lk == IntLit.Kind.UBYTE || rk == IntLit.Kind.UBYTE) return IntLit.Kind.UBYTE;
if (lk == IntLit.Kind.BYTE || rk == IntLit.Kind.BYTE) return IntLit.Kind.BYTE;
throw new InternalCompilerError("Unknown meet of kinds "+lk+" "+rk);
}
/** If the expression was parsed as an ambiguous expression, return a Receiver that would have parsed the same way. Otherwise, return null. */
private static Receiver toReceiver(NodeFactory nf, Expr e) {
if (e instanceof AmbExpr) {
AmbExpr e1 = (AmbExpr) e;
return nf.AmbReceiver(e.position(), null, e1.name());
}
if (e instanceof Field) {
Field f = (Field) e;
if (f.target() instanceof Expr) {
Prefix p = toPrefix(nf, (Expr) f.target());
if (p == null)
return null;
return nf.AmbReceiver(e.position(), p, f.name());
}
else {
return nf.AmbReceiver(e.position(), f.target(), f.name());
}
}
return null;
}
/** If the expression was parsed as an ambiguous expression, return a Prefix that would have parsed the same way. Otherwise, return null. */
private static Prefix toPrefix(NodeFactory nf, Expr e) {
if (e instanceof AmbExpr) {
AmbExpr e1 = (AmbExpr) e;
return nf.AmbPrefix(e.position(), null, e1.name());
}
if (e instanceof Field) {
Field f = (Field) e;
if (f.target() instanceof Expr) {
Prefix p = toPrefix(nf, (Expr) f.target());
if (p == null)
return null;
return nf.AmbPrefix(e.position(), p, f.name());
}
else {
return nf.AmbPrefix(e.position(), f.target(), f.name());
}
}
return null;
}
// HACK: T1==T2 can sometimes be parsed as e1==e2. Correct that.
@Override
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
if (op == EQ) {
NodeFactory nf = (NodeFactory) tc.nodeFactory();
Receiver t1 = toReceiver(nf, left);
Receiver t2 = toReceiver(nf, right);
if (t1 != null && t2 != null) {
Node n1 = this.visitChild(t1, tc);
Node n2 = this.visitChild(t2, tc);
if (n1 instanceof TypeNode && n2 instanceof TypeNode) {
SubtypeTest n = nf.SubtypeTest(position(), (TypeNode) n1, (TypeNode) n2, true);
n = (SubtypeTest) n.typeCheck(tc);
return n;
}
}
}
return null;
}
private static final Map<Operator,Name> OPERATOR_NAMES = CollectionFactory.newHashMap();
static {
Map<Operator,Name> methodNameMap = OPERATOR_NAMES;
methodNameMap.put(ADD, OperatorNames.PLUS);
methodNameMap.put(SUB, OperatorNames.MINUS);
methodNameMap.put(MUL, OperatorNames.STAR);
methodNameMap.put(DIV, OperatorNames.SLASH);
methodNameMap.put(MOD, OperatorNames.PERCENT);
methodNameMap.put(BIT_AND, OperatorNames.AMPERSAND);
methodNameMap.put(BIT_OR, OperatorNames.BAR);
methodNameMap.put(BIT_XOR, OperatorNames.CARET);
methodNameMap.put(COND_OR, OperatorNames.OR);
methodNameMap.put(COND_AND, OperatorNames.AND);
methodNameMap.put(SHL, OperatorNames.LEFT);
methodNameMap.put(SHR, OperatorNames.RIGHT);
methodNameMap.put(USHR, OperatorNames.RRIGHT);
methodNameMap.put(LT, OperatorNames.LT);
methodNameMap.put(GT, OperatorNames.GT);
methodNameMap.put(LE, OperatorNames.LE);
methodNameMap.put(GE, OperatorNames.GE);
methodNameMap.put(DOT_DOT, OperatorNames.RANGE);
methodNameMap.put(ARROW, OperatorNames.ARROW);
methodNameMap.put(LARROW, OperatorNames.LARROW);
methodNameMap.put(FUNNEL, OperatorNames.FUNNEL);
methodNameMap.put(LFUNNEL, OperatorNames.LFUNNEL);
methodNameMap.put(DIAMOND, OperatorNames.DIAMOND);
methodNameMap.put(BOWTIE, OperatorNames.BOWTIE);
methodNameMap.put(STARSTAR, OperatorNames.STARSTAR);
methodNameMap.put(TWIDDLE, OperatorNames.TILDE);
methodNameMap.put(NTWIDDLE, OperatorNames.NTILDE);
methodNameMap.put(BANG, OperatorNames.BANG);
}
public static Name binaryMethodName(Operator op) {
return OPERATOR_NAMES.get(op);
}
public static Name invBinaryMethodName(Operator op) {
return OperatorNames.inverse(binaryMethodName(op));
}
public static boolean isInv(Name name) {
return OperatorNames.is_inverse(name);
}
private static Type promote(TypeSystem ts, Type t1, Type t2) {
if (ts.isByte(t1)) {
return t2;
}
if (ts.isShort(t1)) {
if (ts.isByte(t2))
return t1;
return t2;
}
if (ts.isInt(t1)) {
if (ts.isByte(t2) || ts.isShort(t2))
return t1;
return t2;
}
if (ts.isLong(t1)) {
if (ts.isByte(t2) || ts.isShort(t2) || ts.isInt(t2))
return t1;
return t2;
}
if (ts.isUByte(t1)) {
return t2;
}
if (ts.isUShort(t1)) {
if (ts.isUByte(t2))
return t1;
return t2;
}
if (ts.isUInt(t1)) {
if (ts.isUByte(t2) || ts.isUShort(t2))
return t1;
return t2;
}
if (ts.isULong(t1)) {
if (ts.isUByte(t2) || ts.isUShort(t2) || ts.isUInt(t2))
return t1;
return t2;
}
if (ts.isFloat(t1)) {
if (ts.isByte(t2) || ts.isShort(t2) || ts.isInt(t2) || ts.isLong(t2))
return t1;
if (ts.isUByte(t2) || ts.isUShort(t2) || ts.isUInt(t2) || ts.isULong(t2))
return t1;
return t2;
}
if (ts.isDouble(t1)) {
return t1;
}
return null;
}
/**
* Type check a binary 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 xts = (TypeSystem) tc.typeSystem();
Context context = (Context) tc.context();
Type lbase = Types.baseType(left.type());
Type rbase = Types.baseType(right.type());
if (xts.hasUnknown(lbase) || xts.hasUnknown(rbase))
return this.type(xts.unknownType(position()));
if (op == EQ || op == NE || op == LT || op == GT || op == LE || op == GE) {
// XTENLANG-2156
//Object lv = left.isConstant() ? left.constantValue() : null;
//Object rv = right.isConstant() ? right.constantValue() : null;
//
//// If comparing signed vs. unsigned, check if one operand is a constant convertible to the other (base) type.
//// If so, insert the conversion and check again.
//
//if ((xts.isSignedNumeric(lbase) && xts.isUnsignedNumeric(rbase)) || (xts.isUnsignedNumeric(lbase) && xts.isSignedNumeric(rbase))) {
// try {
// if (lv != null && xts.numericConversionValid(rbase, lbase, lv, context)) {
// Expr e = Converter.attemptCoercion(tc, left, rbase);
// if (e == left)
// return this.type(xts.Boolean());
// if (e != null)
// return Converter.check(this.left(e), tc);
// }
// if (rv != null && xts.numericConversionValid(lbase, rbase, rv, context)) {
// Expr e = Converter.attemptCoercion(tc, right, lbase);
// if (e == right)
// return this.type(xts.Boolean());
// if (e != null)
// return Converter.check(this.right(e), tc);
// }
// } catch (SemanticException e) { } // FIXME
//}
if (xts.isUnsignedNumeric(lbase) && xts.isSignedNumeric(rbase))
Errors.issue(tc.job(),
new Errors.CannotCompareUnsignedVersusSignedValues(position()));
if (xts.isSignedNumeric(lbase) && xts.isUnsignedNumeric(rbase))
Errors.issue(tc.job(),
new Errors.CannotCompareSignedVersusUnsignedValues(position()));
}
if (op == EQ || op == NE) {
// == and != are allowed if the *unconstrained* types can be cast to each other without coercions.
// Coercions are handled above for numerics. No other coercions are allowed.
// Constraints are ignored so that things like x==k will not cause compile-time errors
// when x is a final variable initialized to a constant != k.
// Yoav: I remove the constraints from inside generic arguments as well (see XTENLANG-2022)
Type lbbase = Types.stripConstraints(lbase);
Type rbbase = Types.stripConstraints(rbase);
if (xts.isCastValid(lbbase, rbbase, context) || xts.isCastValid(rbbase, lbbase, context)) {
return type(xts.Boolean());
}
//
// if (xts.isImplicitCastValid(lbase, rbase, context) || xts.isImplicitCastValid(rbase, lbase, context)) {
// assert false : "isCastValid but isImplicitCastValid not for " + lbase + " and " + rbase;
// return type(xts.Boolean());
// }
Errors.issue(tc.job(),
new Errors.OperatorMustHaveOperandsOfComparabletype(lbase, rbase, position()));
return this.type(xts.Boolean());
}
X10Call c = desugarBinaryOp(this, tc);
if (c != null) {
MethodInstance mi = (MethodInstance) c.methodInstance();
Warnings.checkErrorAndGuard(tc,mi, this);
X10Binary_c result = (X10Binary_c) this.methodInstance(mi).type(c.type());
// rebuild the binary using the call's arguments. We'll actually use the call node after desugaring.
if (mi.flags().isStatic()) {
return result.left(c.arguments().get(0)).right(c.arguments().get(1));
}
else if (!c.name().id().equals(invBinaryMethodName(this.operator()))) {
assert (c.name().id().equals(binaryMethodName(this.operator())));
return result.left((Expr) c.target()).right(c.arguments().get(0));
}
else {
return result.invert(true).left(c.arguments().get(0)).right((Expr) c.target());
}
}
Type l = left.type();
Type r = right.type();
if (!xts.hasUnknown(l) && !xts.hasUnknown(r)) {
if (op == COND_OR || op == COND_AND) {
Type result = xts.Boolean();
if (op == COND_OR)
return this.type(xts.Boolean());
if (l.isBoolean() && r.isBoolean()) {
return this.type(BuiltInTypeRules.adjustReturnTypeForConjunction(l,r, context));
}
}
Errors.issue(tc.job(),
new Errors.NoOperationFoundForOperands(op, l, r, position()));
}
return this.type(xts.unknownType(position()));
}
public static X10Call_c typeCheckCall(ContextVisitor tc, X10Call_c call) {
List<Type> typeArgs = new ArrayList<Type>(call.typeArguments().size());
for (TypeNode tn : call.typeArguments()) {
typeArgs.add(tn.type());
}
List<Type> argTypes = new ArrayList<Type>(call.arguments().size());
for (Expr e : call.arguments()) {
Type et = e.type();
argTypes.add(et);
}
Type targetType = call.target().type();
MethodInstance mi = null;
List<Expr> args = null;
// First try to find the method without implicit conversions.
Pair<MethodInstance, List<Expr>> p = Checker.findMethod(tc, call, targetType, call.name().id(), typeArgs, argTypes);
mi = p.fst();
args = p.snd();
if (mi.error() != null) {
try {
// Now, try to find the method with implicit conversions, making them explicit.
p = Checker.tryImplicitConversions(call, tc, targetType, call.name().id(), typeArgs, argTypes);
mi = p.fst();
args = p.snd();
} catch (SemanticException e) {
// FIXME: [IP] The exception may indicate that there's an ambiguity, which is better than reporting that a method is not found.
int i = 3;
}
}
Type rt = Checker.rightType(mi.rightType(), mi.x10Def(), call.target(), tc.context());
call = (X10Call_c) call.methodInstance(mi).type(rt);
call = (X10Call_c) call.arguments(args);
return call;
}
public static X10Call_c searchInstance1(Name methodName, Position pos, ContextVisitor tc, Expr first, Expr second) {
NodeFactory nf = tc.nodeFactory();
// Check if there is a method with the appropriate name and type with the left operand as receiver.
X10Call_c n2 = (X10Call_c) nf.X10Call(pos, first, nf.Id(pos, methodName), Collections.<TypeNode>emptyList(), Collections.singletonList(second));
n2 = typeCheckCall(tc, n2);
MethodInstance mi2 = (MethodInstance) n2.methodInstance();
if (mi2.error() == null && !mi2.def().flags().isStatic())
return n2;
return null;
}
public static X10Call_c searchInstance(Name methodName, Position pos, ContextVisitor tc, Expr first, Expr second) {
if (methodName != null) {
X10Call_c res = searchInstance1(methodName,pos,tc,first,second);
if (res!=null) return res;
// maybe the left operand can be cast to the right operand (e.g., Byte+Int should use Int.operator+(Int) and not Byte.operator+(Byte))
Expr newFirst = Converter.attemptCoercion(
tc, first,
Types.baseType(second.type())); // I use baseType because the constraints are irrelevant for resolution (and it can cause an error if the constraint contain "place23423423")
if (newFirst!=first && newFirst!=null) {
return searchInstance1(methodName,pos,tc,newFirst,second);
}
}
return null;
}
public static X10Call_c desugarBinaryOp(Binary n, ContextVisitor tc) {
Expr left = n.left();
Expr right = n.right();
Operator op = n.operator();
Position pos = n.position();
TypeSystem xts = tc.typeSystem();
Type l = left.type();
Type r = right.type();
// Equality operators are special
if (op == EQ || op == NE)
return null;
// Conditional operators on Booleans are special
if ((op == COND_OR || op == COND_AND) && l.isBoolean() && r.isBoolean())
return null;
NodeFactory nf = tc.nodeFactory();
Name methodName = X10Binary_c.binaryMethodName(op);
Name invMethodName = X10Binary_c.invBinaryMethodName(op);
// 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;
X10Call_c virtual_right;
X10Call_c static_left = null;
X10Call_c static_right = null;
virtual_right = searchInstance(invMethodName,pos,tc,right,left);
virtual_left = searchInstance(methodName,pos,tc,left,right);
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(),
CollectionUtil.list(left, right));
n4 = typeCheckCall(tc, n4);
MethodInstance mi4 = (MethodInstance) n4.methodInstance();
if (mi4.error() == null && mi4.def().flags().isStatic())
static_left = n4;
}
if (methodName != null && !xts.hasSameClassDef(l, r)) {
// Check if there is a static method of the right type with the appropriate name and type.
X10Call_c n3 = (X10Call_c) nf.X10Call(pos, nf.CanonicalTypeNode(pos, Types.ref(r)),
nf.Id(pos, methodName), Collections.<TypeNode>emptyList(),
CollectionUtil.list(left, right));
n3 = typeCheckCall(tc, n3);
MethodInstance mi3 = (MethodInstance) n3.methodInstance();
if (mi3.error() == null && mi3.def().flags().isStatic())
static_right = n3;
}
List<X10Call_c> defs = new ArrayList<X10Call_c>();
if (virtual_left != null) defs.add(virtual_left);
if (virtual_right != null) defs.add(virtual_right);
if (static_left != null) defs.add(static_left);
if (static_right != null) defs.add(static_right);
if (defs.size() == 0) {
if (methodName == null)
return null;
// Create a fake static method in the left type with the appropriate name and type.
X10Call_c fake = (X10Call_c) nf.Call(pos, nf.CanonicalTypeNode(pos, left.type()), nf.Id(pos, methodName), left, right);
fake = typeCheckCall(tc, fake);
return fake;
}
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() != 2 ? (Expr) n1.target() : n1.arguments().get(0),
n1.arguments().size() != 2 ? n1.arguments().get(0) : n1.arguments().get(1),
};
boolean inverse = isInv(n1.name().id());
Expr[] original = new Expr[] {
inverse ? right : left,
inverse ? left : right,
};
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 = def(td);
boolean isBetter = false;
for (Iterator<X10Call_c> ci = best.iterator(); ci.hasNext(); ) {
X10Call_c c = ci.next();
MethodDef bestmd = c.methodInstance().def();
if (bestmd == md) break; // same method by a different path; already in best
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 = def(besttd);
assert (bestcd != null && cd != null);
if (cd != bestcd && 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 (cd != bestcd && 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(); // arbitrarily pick the left operand
SemanticException error = new Errors.AmbiguousOperator(op, bestmis, pos);
MethodInstance mi = xts.createFakeMethod(ct, Flags.PUBLIC.Static(), methodName,
Collections.<Type>emptyList(), CollectionUtil.list(l, r), 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(),
CollectionUtil.list(left, right)).methodInstance(mi).type(mi.returnType());
}
{
MethodInstance mi = result.methodInstance();
boolean inverse = isInv(mi.name());
left = result.arguments().size() != 2 ? (Expr) result.target() : result.arguments().get(0);
right = result.arguments().size() != 2 ? result.arguments().get(0) : result.arguments().get(1);
Type lbase = Types.baseType(left.type());
Type rbase = Types.baseType(right.type());
Context context = (Context) tc.context();
// Add support for patching up the return type of Region's operator*().
// The rank of the result is a+b, if the rank of the left arg is a and of the right arg is b,
// and a and b are literals. Further the result is rect if both args are rect, and zeroBased
// if both args are zeroBased.
if (op == Binary.MUL && xts.typeEquals(xts.Region(), lbase, context)
&& xts.typeEquals(xts.Region(), rbase, context)) {
Type type = result.type();
type = BuiltInTypeRules.adjustReturnTypeForRegionMult(left, right, type, context);
mi = mi.returnType(type);
result = (X10Call_c) result.methodInstance(mi).type(type);
}
// Add support for patching up the return type of IntRegion's operator*().
// The rank of the result is 2. Further the result is zeroBased if both args are zeroBased.
if (op == Binary.MUL && xts.typeEquals(xts.IntRange(), lbase, context)
&& xts.typeEquals(xts.IntRange(), rbase, context)) {
Type type = result.type();
type = BuiltInTypeRules.adjustReturnTypeForRangeRangeMult(left, right, type, context);
mi = mi.returnType(type);
result = (X10Call_c) result.methodInstance(mi).type(type);
}
// Add support for patching up the return type of Int's operator..(),
// The result is zeroBased if the left arg is 0.
if (op == Binary.DOT_DOT && xts.typeEquals(xts.Int(), lbase, context)
&& xts.typeEquals(xts.Int(), rbase, context)) {
Type type = result.type();
type = BuiltInTypeRules.adjustReturnTypeForIntRange(left, right, type, context);
mi = mi.returnType(type);
result = (X10Call_c) result.methodInstance(mi).type(type);
}
}
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;
}
/**
* Get the class that defines type t. Only returns null if
* t is a parameter type or an unknown type.
*/
public static ClassDef def(Type t) {
Type base = Types.baseType(t);
if (base instanceof ClassType)
return ((ClassType) base).def();
return null;
}
public static enum Conversion {
NONE { boolean harder(Conversion a) { return false; } },
IMPLICIT { boolean harder(Conversion a) { return a == NONE; } },
EXPLICIT { boolean harder(Conversion a) { return a == NONE || a == IMPLICIT; } },
UNKNOWN { boolean harder(Conversion a) { return a != UNKNOWN; } };
abstract boolean harder(Conversion b);
}
public static Conversion conversionNeeded(Expr[] actuals, Expr[] original) {
Conversion result = Conversion.NONE;
for (int k = 0; k < actuals.length; k++) {
if (actuals[k] != original[k]) {
if (result == Conversion.NONE)
result = Conversion.IMPLICIT;
if (actuals[k] instanceof X10Call) {
X10Call c = (X10Call) actuals[k];
if (c.methodInstance().name() == Converter.operator_as) {
result = Conversion.EXPLICIT;
}
}
}
}
return result;
}
public static Expr coerceToString(ContextVisitor tc, Expr e) throws SemanticException {
TypeSystem ts = tc.typeSystem();
if (!e.type().isSubtype(ts.String(), tc.context())) {
NodeFactory nf = tc.nodeFactory();
e = nf.X10Call(e.position(), nf.CanonicalTypeNode(e.position(), ts.String()),
nf.Id(e.position(), Name.make("valueOf")),
Collections.<TypeNode>emptyList(), Collections.singletonList(e));
return (Expr) e.del().disambiguate(tc).typeCheck(tc).checkConstants(tc);
}
return e;
}
/** Get the left operand of the expression. */
public Expr left() {
return this.left;
}
/** Set the left operand of the expression. */
public Binary left(Expr left) {
X10Binary_c n = (X10Binary_c) copy();
n.left = left;
return n;
}
/** Get the operator of the expression. */
public Operator operator() {
return this.op;
}
/** Set the operator of the expression. */
public Binary operator(Operator op) {
X10Binary_c n = (X10Binary_c) copy();
n.op = op;
return n;
}
/** Get the right operand of the expression. */
public Expr right() {
return this.right;
}
/** Set the right operand of the expression. */
public Binary right(Expr right) {
X10Binary_c n = (X10Binary_c) copy();
n.right = right;
return n;
}
/** Get the precedence of the expression. */
public Precedence precedence() {
return this.precedence;
}
public Binary precedence(Precedence precedence) {
X10Binary_c n = (X10Binary_c) copy();
n.precedence = precedence;
return n;
}
/** Reconstruct the expression. */
protected Binary_c reconstruct(Expr left, Expr right) {
if (left != this.left || right != this.right) {
X10Binary_c n = (X10Binary_c) copy();
n.left = left;
n.right = right;
return n;
}
return this;
}
/** Visit the children of the expression. */
public Node visitChildren(NodeVisitor v) {
Expr left = (Expr) visitChild(this.left, v);
Expr right = (Expr) visitChild(this.right, v);
return reconstruct(left, right);
}
/** Get the throwsArithmeticException of the expression. */
public boolean throwsArithmeticException() {
// conservatively assume that any division or mod may throw
// ArithmeticException this is NOT true-- floats and doubles don't
// throw any exceptions ever...
return op == DIV || op == MOD;
}
public String toString() {
return left + " " + op + " " + right;
}
/** Write the expression to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
printSubExpr(left, true, w, tr);
w.write(" ");
w.write(op.toString());
w.allowBreak(type() == null || type().isJavaPrimitive() ? 2 : 0, " ");
printSubExpr(right, false, w, tr);
}
public void dump(CodeWriter w) {
super.dump(w);
if (type != null) {
w.allowBreak(4, " ");
w.begin(0);
w.write("(type " + type + ")");
w.end();
}
w.allowBreak(4, " ");
w.begin(0);
w.write("(operator " + op + ")");
w.end();
}
public Term firstChild() {
return left;
}
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
if (op == COND_AND || op == COND_OR) {
// short-circuit
if (left instanceof BooleanLit) {
BooleanLit b = (BooleanLit) left;
if ((b.value() && op == COND_OR) || (! b.value() && op == COND_AND)) {
v.visitCFG(left, this, EXIT);
}
else {
v.visitCFG(left, right, ENTRY);
v.visitCFG(right, this, EXIT);
}
}
else {
if (op == COND_AND) {
// AND operator
// short circuit means that left is false
v.visitCFG(left, FlowGraph.EDGE_KEY_TRUE, right,
ENTRY, FlowGraph.EDGE_KEY_FALSE, this, EXIT);
}
else {
// OR operator
// short circuit means that left is true
v.visitCFG(left, FlowGraph.EDGE_KEY_FALSE, right,
ENTRY, FlowGraph.EDGE_KEY_TRUE, this, EXIT);
}
v.visitCFG(right, FlowGraph.EDGE_KEY_TRUE, this,
EXIT, FlowGraph.EDGE_KEY_FALSE, this, EXIT);
}
}
else {
if (left.type().isBoolean() && right.type().isBoolean()) {
v.visitCFG(left, FlowGraph.EDGE_KEY_TRUE, right,
ENTRY, FlowGraph.EDGE_KEY_FALSE, right, ENTRY);
v.visitCFG(right, FlowGraph.EDGE_KEY_TRUE, this,
EXIT, FlowGraph.EDGE_KEY_FALSE, this, EXIT);
}
else {
v.visitCFG(left, right, ENTRY);
v.visitCFG(right, this, EXIT);
}
}
return succs;
}
public List<Type> throwTypes(TypeSystem ts) {
if (throwsArithmeticException()) {
return Collections.<Type>singletonList(ts.ArithmeticException());
}
return Collections.<Type>emptyList();
}
}