/* * 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.types.checker; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import polyglot.ast.Assign; import polyglot.ast.Assign_c; import polyglot.ast.Binary; import polyglot.ast.Call; import polyglot.ast.Expr; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Assign.Operator; import polyglot.ast.Receiver; import polyglot.types.ClassType; import polyglot.types.CodeDef; import polyglot.types.Context; import polyglot.types.Flags; import polyglot.types.LocalDef; import polyglot.types.Matcher; import polyglot.types.MethodDef; import polyglot.types.Name; import polyglot.types.ProcedureDef; import polyglot.types.QName; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.TypeSystem_c; import polyglot.types.TypeSystem_c.MethodMatcher; import polyglot.types.Types; import polyglot.util.InternalCompilerError; import polyglot.util.Pair; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import x10.ast.X10Binary_c; import x10.ast.X10New_c; import x10.ast.X10New_c.MatcherMaker; import x10.ast.X10ProcedureCall; import x10.constraint.XFailure; import x10.constraint.XLocal; import x10.constraint.XTerm; import x10.types.constraints.ConstraintManager; import x10.constraint.XVar; import x10.errors.Errors; import x10.errors.Errors.IllegalConstraint; import x10.errors.Warnings; import x10.errors.Errors.CannotAssign; import x10.errors.Errors.MethodOrStaticConstructorNotFound; import x10.types.ConstrainedType; import x10.types.MacroType; import x10.types.ParameterType; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.X10LocalDef; import polyglot.types.Context; import x10.types.X10MemberDef; import x10.types.MethodInstance; import x10.types.X10ProcedureInstance; import polyglot.types.TypeSystem; import polyglot.types.NoMemberException; import x10.types.XTypeTranslator; import x10.types.constraints.CConstraint; import x10.types.constraints.CLocal; import x10.types.matcher.Subst; import x10.visit.X10TypeChecker; import static polyglot.ast.Assign.*; /** * A set of static methods used by AST nodes to check types. * @author vj * */ public class Checker { public static void checkOfferType(Position pos, X10ProcedureInstance<? extends ProcedureDef> pi,ContextVisitor tc) throws SemanticException { Context cxt = (Context) tc.context(); Type offerType = (Type) Types.get(pi.offerType()); Type type = cxt.collectingFinishType(); if (offerType != null) { if (type == null) throw new Errors.CannotCallCodeThatOffers(pi, pos); if (! tc.typeSystem().isSubtype(offerType, type, cxt)) throw new Errors.OfferTypeMismatch(offerType, type, pos); } else { } } public static Node typeCheckAssign(Assign_c a, ContextVisitor tc) { Assign n = a; n = (Assign) a.typeCheckLeft(tc); TypeSystem ts = tc.typeSystem(); Type t = n.leftType(); if (t == null) t = ts.unknownType(n.position()); Expr right = n.right(); Assign.Operator op = n.operator(); Type s = right.type(); if (op == ASSIGN) { Expr e = Converter.attemptCoercion(tc, right, t); if (e != null) { n = n.right(e); } else { // Don't try to extract the LHS expression, this is called by X10FieldAssign_c as well. Errors.issue(tc.job(), Errors.CannotAssign.make(right, t, tc, n.position())); } } if (op == ADD_ASSIGN || op == Assign.SUB_ASSIGN || op == Assign.MUL_ASSIGN || op == DIV_ASSIGN || op == MOD_ASSIGN || op == BIT_AND_ASSIGN || op == BIT_OR_ASSIGN || op == BIT_XOR_ASSIGN || op == SHL_ASSIGN || op == SHR_ASSIGN || op == USHR_ASSIGN) { Binary.Operator bop = op.binaryOperator(); NodeFactory nf = tc.nodeFactory(); Binary bin = (Binary) nf.Binary(n.position(), n.left(), bop, right); Call c = X10Binary_c.desugarBinaryOp(bin, tc); if (c != null) { MethodInstance mi = (MethodInstance) c.methodInstance(); Warnings.checkErrorAndGuard(tc,mi,n); t = c.type(); } else { Errors.issue(tc.job(), Errors.CannotAssign.make(right, t, tc, n.position())); } } return n.type(t); } /*public static void checkVariancesOfType(Position pos, Type t, ParameterType.Variance requiredVariance, String desc, Map<Name,ParameterType.Variance> vars, ContextVisitor tc) throws SemanticException { if (t instanceof ParameterType) { ParameterType pt = (ParameterType) t; X10ClassDef cd = (X10ClassDef) tc.context().currentClassDef(); if (pt.def() != cd) return; ParameterType.Variance actualVariance = vars.get(pt.name()); if (actualVariance == null) return; switch (actualVariance) { case INVARIANT: break; case COVARIANT: switch (requiredVariance) { case INVARIANT: throw new SemanticException("Cannot use covariant parameter " + pt + " " + desc + "; must be invariant.", pos); case COVARIANT: break; case CONTRAVARIANT: throw new SemanticException("Cannot use covariant parameter " + pt + " " + desc + "; must be contravariant or invariant.", pos); } break; case CONTRAVARIANT: switch (requiredVariance) { case INVARIANT: throw new SemanticException("Cannot use contravariant parameter " + pt + " " + desc + "; must be invariant.", pos); case COVARIANT: throw new SemanticException("Cannot use contravariant parameter " + pt + " " + desc + "; must be covariant or invariant.", pos); case CONTRAVARIANT: break; } break; } } if (t instanceof MacroType) { MacroType mt = (MacroType) t; checkVariancesOfType(pos, mt.definedType(), requiredVariance, desc, vars, tc); } if (t instanceof X10ClassType) { X10ClassType ct = (X10ClassType) t; X10ClassDef def = ct.x10Def(); if (ct.typeArguments() == null) return; for (int i = 0; i < ct.typeArguments().size(); i++) { Type at = ct.typeArguments().get(i); ParameterType pt = def.typeParameters().get(i); ParameterType.Variance v = def.variances().get(i); ParameterType.Variance newVariance; switch (v) { case INVARIANT: checkVariancesOfType(pos, at, requiredVariance, desc, vars, tc); break; case COVARIANT: checkVariancesOfType(pos, at, requiredVariance, desc, vars, tc); break; case CONTRAVARIANT: switch (requiredVariance) { case INVARIANT: checkVariancesOfType(pos, at, requiredVariance, desc, vars, tc); break; case COVARIANT: checkVariancesOfType(pos, at, ParameterType.Variance.CONTRAVARIANT, desc, vars, tc); break; case CONTRAVARIANT: checkVariancesOfType(pos, at, ParameterType.Variance.COVARIANT, desc, vars, tc); break; } break; } } } if (t instanceof ConstrainedType) { ConstrainedType ct = (ConstrainedType) t; Type at = Types.get(ct.baseType()); checkVariancesOfType(pos, at, requiredVariance, desc, vars, tc); } } */ /** * Substitute the XTerm obtained by translating target (and an EQV if there is * no such term) for fi.thisVar() in t. */ public static Type rightType(Type t, X10MemberDef fi, Receiver target, Context c) { //CConstraint x = Types.xclause(t); // Do this even if x.valid() if (fi.thisVar()==null || (! (target instanceof Expr))) return t; XVar receiver = null; TypeSystem ts = (TypeSystem) t.typeSystem(); try { XTerm r=ts.xtypeTranslator().translate(ConstraintManager.getConstraintSystem().makeCConstraint(), target, c); if (r instanceof XVar) { receiver = (XVar) r; } } catch (IllegalConstraint z) { } if (receiver == null) receiver = ConstraintManager.getConstraintSystem().makeEQV(); try { t = Subst.subst(t, (new XVar[] { receiver }), (new XVar[] { fi.thisVar() }), new Type[] { }, new ParameterType[] { }); } catch (SemanticException e) { throw new InternalCompilerError("Unexpected error while computing field type", e); } return t; } /** * Called from within XField_c and X10Call_c to return the adjusted * type for a call which is known to be a property call. The type is adjusted * with the contents of the property call. * @param type -- the current return type of the call * @param t -- the call * @param c -- the current context * @return type, adjusted with constraints clauses based on the body of the call * @throws IllegalConstraint */ public static Type expandCall(Type type, Call t, Context c) throws IllegalConstraint { CConstraint cs = ConstraintManager.getConstraintSystem().makeCConstraint(); XTypeTranslator xt = ((TypeSystem) type.typeSystem()).xtypeTranslator(); Receiver target = t.target(); XTerm body = xt.translate(cs, t, c); CConstraint x = Types.xclause(type); X10MemberDef fi = (X10MemberDef) t.methodInstance().def(); if (x == null || fi.thisVar() == null || !(target instanceof Expr)) return type; x = x.copy(); try { XVar receiver = Types.selfVarBinding(target.type()); XVar root = null; if (receiver == null) { receiver = root = ConstraintManager.getConstraintSystem().makeUQV(); } // Need to add the target's constraints in here because the target may not // be a variable. hence the type information wont be in the context. CConstraint ttc = Types.xclause(target.type()); ttc = ttc == null ? ConstraintManager.getConstraintSystem().makeCConstraint() : ttc.copy(); ttc = ttc.instantiateSelf(receiver); if (! Types.contextKnowsType(target)) x.addIn(ttc); if (body != null) x.addSelfBinding(body); x=x.substitute(receiver, fi.thisVar()); if (root != null) { x = x.project(root); } type = Types.addConstraint(Types.baseType(type), x); } catch (XFailure z) { Types.setInconsistent(type); } return type; } public static Type fieldRightType(Type type, X10MemberDef fi, Receiver target, Context c) { CConstraint x = Types.xclause(type); if (x == null || fi.thisVar() == null || !(target instanceof Expr)) return type; // Need to add the target's constraints in here because the target may not // be a variable. hence the type information wont be in the context. CConstraint xc = Types.xclause(target.type()); if (xc == null || xc.valid()) return type; //xc = xc.copy(); x = x.copy(); try { XVar receiver = Types.selfVarBinding(target.type()); XVar root = null; if (receiver == null) { receiver = root = ConstraintManager.getConstraintSystem().makeUQV(); } xc = xc.instantiateSelf(receiver); if (! Types.contextKnowsType(target)) x.addIn(xc); x=x.substitute(receiver, fi.thisVar()); if (root != null) { x = x.project(root); } type = Types.addConstraint(Types.baseType(type), x); } catch (XFailure z) { Types.setInconsistent(type); } return type; } /** * Find a method on the given targetType with the given name and list of args. If you cannot find * one, create and return a fake method instance, recording the reason you could not find one. * To facilitate further processing, if all the methods that you found have the same return type, * record that return type for the fake method instance you are creating. * @param tc * @param targetType * @param name * @param typeArgs * @param actualTypes * @return */ public static MethodInstance findAppropriateMethod(ContextVisitor tc, Type targetType, Name name, List<Type> typeArgs, List<Type> actualTypes) { MethodInstance mi; TypeSystem xts = tc.typeSystem(); Context context = tc.context(); boolean haveUnknown = xts.hasUnknown(targetType); for (Type t : actualTypes) { if (xts.hasUnknown(t)) haveUnknown = true; } SemanticException error = null; if (!haveUnknown) { try { return xts.findMethod(targetType, xts.MethodMatcher(targetType, name, typeArgs, actualTypes, context)); } catch (SemanticException e) { error = e; } } // If not returned yet, fake the method instance. Collection<MethodInstance> mis = null; try { mis = xts.findMethods(targetType, xts.MethodMatcher(targetType, name, typeArgs, actualTypes, context)); } catch (SemanticException e) { if (error == null) error = e; } // See if all matches have the same return type, and save that to avoid losing information. Type rt = null; if (mis != null) { for (MethodInstance xmi : mis) { if (rt == null) { rt = xmi.returnType(); } else if (!xts.typeEquals(rt, xmi.returnType(), context)) { if (xts.typeBaseEquals(rt, xmi.returnType(), context)) { rt = Types.baseType(rt); } else { rt = null; break; } } } } if (haveUnknown) error = new SemanticException(); // null message if (!targetType.isClass()) { QName tName = targetType.fullName(); if (tName == null) { tName = QName.make(null, targetType.toString()); } targetType = xts.createFakeClass(tName, new SemanticException("Target type is not a class: "+targetType)); } mi = xts.createFakeMethod(targetType.toClass(), Flags.PUBLIC, name, typeArgs, actualTypes, error); if (rt == null) rt = mi.returnType(); rt = PlaceChecker.AddIsHereClause(rt, context); mi = mi.returnType(rt); return mi; } /** * Find a match while trying implicit conversions. * @param n * @param tc * @param targetType * @param name * @param typeArgs * @param argTypes * @return * @throws SemanticException */ public static Pair<MethodInstance,List<Expr>> tryImplicitConversions(X10ProcedureCall n, ContextVisitor tc, Type targetType, final Name name, List<Type> typeArgs, List<Type> argTypes) throws SemanticException { final TypeSystem ts = (TypeSystem) tc.typeSystem(); final Context context = tc.context(); List<MethodInstance> methods = ts.findAcceptableMethods(targetType, ts.MethodMatcher(targetType, name, typeArgs, argTypes, context,true)); Pair<MethodInstance,List<Expr>> p = Converter.<MethodDef,MethodInstance>tryImplicitConversions(n, tc, targetType, methods, new X10New_c.MatcherMaker<MethodInstance>() { public Matcher<MethodInstance> matcher(Type ct, List<Type> typeArgs, List<Type> argTypes) { return ts.MethodMatcher(ct, name, typeArgs, argTypes, context); } }); return p; } /** * Looks up a method with given name and argument types. */ public static Pair<MethodInstance,List<Expr>> findMethod(ContextVisitor tc, X10ProcedureCall n, Type targetType, Name name, List<Type> typeArgs, List<Type> actualTypes) { MethodInstance mi; TypeSystem xts = tc.typeSystem(); Context context = (Context) tc.context(); boolean haveUnknown = xts.hasUnknown(targetType); for (Type t : actualTypes) { if (xts.hasUnknown(t)) haveUnknown = true; } SemanticException error = null; try { return findMethod(tc, context, n, targetType, name, typeArgs, actualTypes, context.inStaticContext()); } catch (SemanticException e) { error = e; } // If not returned yet, fake the method instance. Collection<MethodInstance> mis = null; try { mis = Checker.findMethods(tc, targetType, name, typeArgs, actualTypes); } catch (SemanticException e) { if (error == null) error = e; } // See if all matches have the same return type, and save that to avoid losing information. Type rt = null; if (mis != null) { for (MethodInstance xmi : mis) { if (rt == null) { rt = xmi.returnType(); } else if (!xts.typeEquals(rt, xmi.returnType(), context)) { if (xts.typeBaseEquals(rt, xmi.returnType(), context)) { rt = Types.baseType(rt); } else { rt = null; break; } } } } if (targetType == null) targetType = context.currentClass(); if (haveUnknown) error = new SemanticException(); // null message if (!targetType.isClass()) { QName tName = targetType.fullName(); if (tName == null) { tName = QName.make(null, targetType.toString()); } targetType = xts.createFakeClass(tName, new SemanticException("Target type is not a class: "+targetType)); } mi = xts.createFakeMethod(targetType.toClass(), Flags.PUBLIC.Static(), name, typeArgs, actualTypes, error); if (rt != null) mi = mi.returnType(rt); return new Pair<MethodInstance, List<Expr>>(mi, n.arguments()); } private static Pair<MethodInstance,List<Expr>> findMethod(ContextVisitor tc, Context xc, X10ProcedureCall n, Type targetType, Name name, List<Type> typeArgs, List<Type> argTypes, boolean requireStatic) throws SemanticException { MethodInstance mi = null; TypeSystem xts = (TypeSystem) tc.typeSystem(); if (targetType != null) { mi = xts.findMethod(targetType, xts.MethodMatcher(targetType, name, typeArgs, argTypes, xc)); return new Pair<MethodInstance, List<Expr>>(mi, n.arguments()); } if (xc.currentDepType() != null) xc = (Context) xc.pop(); ClassType currentClass = xc.currentClass(); if (currentClass != null && xts.hasMethodNamed(currentClass, name)) { // Override to change the type from C to C{self==this}. Type t = currentClass; XVar thisVar = null; if (XTypeTranslator.THIS_VAR) { CodeDef cd = xc.currentCode(); if (cd instanceof X10MemberDef) { thisVar = ((X10MemberDef) cd).thisVar(); } } else { //thisVar = xts.xtypeTranslator().transThis(currentClass); thisVar = xts.xtypeTranslator().translateThisWithoutTypeConstraint(); } if (thisVar != null) t = Types.setSelfVar(t, thisVar); // Found a class that has a method of the right name. // Now need to check if the method is of the correct type. // First try to find the method without implicit conversions. try { mi = xts.findMethod(t, xts.MethodMatcher(t, name, typeArgs, argTypes, xc)); if (requireStatic && !mi.flags().isStatic()) { mi = mi.error(new Errors.CannotAccessNonStaticFromStaticContext(mi, n.position())); } return new Pair<MethodInstance, List<Expr>>(mi, n.arguments()); } catch (SemanticException e) { // There is an ambiguity. Throw, don't try to use implicit conversions. if (e instanceof Errors.MultipleMethodDefsMatch) { throw e; } // only if we didn't find any methods, try coercions. if (!(e instanceof NoMemberException)) { throw e; } // Now, try to find the method with implicit conversions, making them explicit. try { Pair<MethodInstance,List<Expr>> p = tryImplicitConversions(n, tc, t, name, typeArgs, argTypes); mi = p.fst(); if (requireStatic && !mi.flags().isStatic()) { mi = mi.error(new Errors.CannotAccessNonStaticFromStaticContext(mi, n.position())); p = new Pair<MethodInstance,List<Expr>>(mi, p.snd()); } return p; } catch (SemanticException e2) { throw e; } } } while (xc.pop() != null && xc.pop().currentClass() == currentClass) xc = (Context) xc.pop(); if (xc.pop() != null) { return findMethod(tc, (Context) xc.pop(), n, targetType, name, typeArgs, argTypes, currentClass.flags().isStatic()); } TypeSystem_c.MethodMatcher matcher = xts.MethodMatcher(targetType, name, typeArgs, argTypes, xc); throw new Errors.MethodOrStaticConstructorNotFound(matcher, n.position()); } public static Collection<MethodInstance> findMethods(ContextVisitor tc, Type targetType, Name name, List<Type> typeArgs, List<Type> actualTypes) throws SemanticException { TypeSystem xts = tc.typeSystem(); Context context = (Context) tc.context(); if (targetType == null) { // TODO return Collections.emptyList(); } return xts.findMethods(targetType, xts.MethodMatcher(targetType, name, typeArgs, actualTypes, context)); } }