/*
* 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.matcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LazyRef_c;
import polyglot.types.LocalInstance;
import polyglot.types.LocalInstance_c;
import polyglot.types.Name;
import polyglot.types.Ref;
import polyglot.types.UnknownType;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.util.Position;
import polyglot.util.Transformation;
import polyglot.util.TransformingList;
import x10.constraint.XFailure;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.errors.Errors;
import x10.types.ConstrainedType;
import x10.types.ParameterType;
import x10.types.X10ClassDef;
import x10.types.X10LocalDef;
import x10.types.X10LocalDef_c;
import x10.types.X10LocalInstance;
import x10.types.X10LocalInstance_c;
import x10.types.X10ProcedureDef;
import x10.types.X10ProcedureInstance;
import x10.types.MacroType;
import polyglot.types.TypeSystem;
import x10.types.checker.PlaceChecker;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CLocal;
import x10.types.constraints.ConstraintMaker;
import x10.types.constraints.SubtypeConstraint;
import x10.types.constraints.TypeConstraint;
import x10.types.constraints.XConstrainedTerm;
import x10.X10CompilerOptions;
/**
* A set of static methods used for matching types and type elements (fields, constructors, methods).
*
* @author vj 2/6/2010
*
*/
public class Matcher {
// Generic method resolution interacts with type inference.
// The new (2.1) semantics requires that for instance methods, given a this Type
// t and a method name m and actual type args, we proceed in two stages.
// First we determine the set S of applicable and available methods.
//
public static <PI extends X10ProcedureInstance<?>> PI inferAndCheckAndInstantiate(Context context, PI me,
Type thisType,
List<Type> typeActuals,
final List<Type> actuals,
Position pos) throws SemanticException {
final List<Type> typeFormals = me.typeParameters();
final List<Type> formals = me.formalTypes();
if (typeActuals.isEmpty() && ! typeFormals.isEmpty()) {
Type[] Y = TypeConstraint.inferTypeArguments(me, thisType, actuals, formals, typeFormals, context);
return inferAndCheckAndInstantiate(context, me, thisType, Arrays.asList(Y), actuals, pos);
}
// Instantiate the proposed PI.
Type[] thisTypeArray = new Type[] { thisType };
PI newMe = instantiate(context, me, thisTypeArray, typeActuals, actuals, true);
return newMe;
}
public static <PI extends X10ProcedureInstance<?>> PI instantiate(Context context, PI me,
Type thisType,
List<Type> typeActuals,
final List<Type> actuals) throws SemanticException {
PI me2 = instantiate(context, me, new Type[] {thisType}, typeActuals, actuals, false);
return me2;
}
/**
* This method is the heart of method and constructor call type-checking. This
* is a hot spot for X10 compilation.
*
* <p> It takes a context, an X10ProcedureInstance (this can represent a new or
* method invocation or closure invocation), the array of types for
* referenced this variables (including :"outer" this variables), the
* actual type parameters to the invocation, and the list of types of
* the actual arguments, and an indication for whether checks are to be done
* or not.
*
* <p> Let the PI (me) be of the form def m[~X](~x:~S){c}:T. Let the
* actual call be of the form e.m[~V](~a), where e:U and ~a:~W.
*
* <p> If a PI is returned, then it will be of the form
* (def m[~V](~y:~S'){c'}:T') where S' = S[~y/~x][~V/~X] and with the this's
* replaced as well,and c' and T' are obtained from c and T in a similar
* fashion. Thus PI returns an instance of me whose
*
* <p> Note: In order to obtain the parameters of the original
* method/constructor definition, please use mi.def(), where mi is the
* return value of this method.
*
* <p> If checking is turned on, a SemanticException is thrown if
* the type of an actual is inconsistent or is not a subtype of the
* corresponding formal in newMe, or the guard is inconsistent or not
* satisfied.
*
* @param <PI> -- The type of the formal descriptor
* @param context -- The context in which the type-checking is being done
* @param me -- The formal descriptor for this call
* @param thisTypeArray -- An inout parameter containing thisType.
* @param typeActuals -- The actual type parameters to the call
* @param actualsIn -- The types of the actual parameters for the call.
* @param checkActuals -- Check actual types are subtypes of the formal
* and the guard is satisfied.
* @return -- An instantiated version of me, with actuals substituted for
* formals in actual types and return types.
* @throws SemanticException
*/
private static <PI extends X10ProcedureInstance<?>> PI instantiate(
final Context context,
final PI me,
final /*inout*/ Type[] thisTypeArray,
List<Type> typeActuals,
final List<Type> actualsIn,
boolean checkActuals) throws SemanticException
{
final XVar[] ys = new XVar[actualsIn.size()+1];
final TypeSystem xts = (TypeSystem) me.typeSystem();
List<Type> formals = new ArrayList<Type>();
for (Type formal : me.formalTypes()) {
//formal = PlaceChecker.ReplaceHereByPlaceTerm((Type) formal.copy(), context);
formals.add(formal);
}
final List<LocalInstance> formalNames = me.formalNames();
final List<Type> typeFormals = me.typeParameters();
final boolean isStatic = Types.isStatic(me);
if (actualsIn.size() != formals.size())
throw new SemanticException("Call not valid; incorrect number of actual arguments.", me.position());
if (typeActuals.size() != typeFormals.size())
throw new SemanticException("Call not valid; incorrect number of actual type arguments.", me.position());
formals = Types.expandTypes(formals, xts);
final List<Type> actuals = Types.expandTypes(actualsIn, xts);
// actuals cannot be of void type
for (Type actual : actuals)
if (xts.isVoid(actual))
throw new SemanticException("An actual cannot have a 'void' type.", me.position());
Type thisType = thisTypeArray[0];
final XVar ythiseqv = ys[0] = getSymbolVar(thisType);
XVar st = Types.selfVarBinding(thisType);
final boolean yeqvIsSymbol = (! isStatic) && (st !=null);
if (! isStatic) {
if (st == null)
thisType = Types.instantiateSelf(ythiseqv, thisType);
}
final Type thisTypeFinal = thisType;
thisTypeArray[0] = thisType;
XVar[] x = getSymbolicNames(me.formalNames(), xts);
// Generate new local variable y's. These are local variables, from which
// XVar's can be generated as needed.
// We will need to substitute the symbolic names generated from
// these new local variables for the xi.
// Then, in the newMe that is returned the formal names will be set
// to these new local variables so that the newMe will be consistent.
// i.e. the variables occurring in the constraints of the arg
// types in newMe will be the formals.
// This fixes a major bug in which the variables in constraints
// got disconnected from the formals, and uqvs would sometimes show
// up in error messages. This also supports implicit coercions
// performed using the data in the MI returned from this call.
// See Converter.tryImplicitConversions.
final List<LocalInstance> yLocalInstances = getSymbolicNames(actuals);
final XVar[] ySymbols = getSymbolicNames(yLocalInstances, xts);
System.arraycopy(ySymbols, 0, ys, 1, actuals.size());
final CConstraint returnEnv = computeNewSigma(thisType, actuals, ythiseqv, ySymbols, isStatic, xts);
final CConstraint returnEnv2 = computeNewSigma2(thisType, actuals, ythiseqv, ySymbols, isStatic, xts);
// We'll subst selfVar for THIS.
XVar xthis = null;
if (! isStatic ) {
if (me.def() instanceof X10ProcedureDef)
xthis = (XVar) ((X10ProcedureDef) me.def()).thisVar();
if (xthis == null)
xthis = ConstraintManager.getConstraintSystem().makeThis();
}
final XVar codePlace = Types.getPlaceTerm(me);
XConstrainedTerm currentPlaceTerm = context.currentPlaceTerm();
final XTerm currentPlace = currentPlaceTerm != null ? currentPlaceTerm.term() : ConstraintManager.getConstraintSystem().makeEQV();
final ParameterType[] X = new ParameterType[typeFormals.size()];
final Type[] Y = new Type[typeFormals.size()];
for (int i = 0; i < typeFormals.size(); i++) {
Type xtype = xts.expandMacros(typeFormals.get(i));
Y[i] = xts.expandMacros(typeActuals.get(i));
Y[i] = Subst.subst(Y[i], currentPlace, codePlace);
// TODO: should enforce this statically
assert xtype instanceof ParameterType : xtype + " is not a ParameterType, is a "
+ (xtype != null ? xtype.getClass().getName() : "null");
X[i] = (ParameterType) xtype;
}
// Start assembling the pieces of the PI with actual information.
X10ProcedureInstance<?> newMe = me.typeParameters(Arrays.asList(Y));
final XVar[] x2 = isStatic ? x : new XVar[x.length+2];
final XTerm[] y2eqv = isStatic ? ySymbols : new XTerm[ySymbols.length+2];
if (! isStatic) {
x2[0] = xthis;
x2[1] = Types.thisVar(xthis, thisType);
System.arraycopy(x, 0, x2, 2, x.length);
y2eqv[0] = ythiseqv;
y2eqv[1] = ythiseqv;
System.arraycopy(ySymbols, 0, y2eqv, 2, ySymbols.length);
}
{ // set up the return type.
final LazyRef_c<Type> newReturnTypeRef = new LazyRef_c<Type>(null);
newReturnTypeRef.setResolver(new Runnable() {
public void run() {
try {
Type rt = me.returnType(); // may be a macrotype
rt = Subst.subst(rt, currentPlace, codePlace);
Type newReturnType = Subst.subst(rt, y2eqv, x2, Y, X);
// Replace all outer#this variables in the constraint
// (if any) by QualifiedVar's.
if (newReturnType instanceof ConstrainedType) {
if (! isStatic) {
List<X10ClassDef> outers = Types.outerTypes(thisTypeFinal);
// Do not replace the lowest level this by qvar's -- only outer this.
// That will be taken care of by existing code.
if (outers != null && outers.size() > 1) {
XVar[] outerThis = new XVar[outers.size()-1];
XVar[] outerYs = new XVar[outers.size()-1];
for (int i=1; i < outers.size(); ++i) {
outerYs[i-1] = ConstraintManager.getConstraintSystem().makeQualifiedVar(outers.get(i).asType(), (XVar) y2eqv[0]);
outerThis[i-1]= outers.get(i).thisVar();
}
newReturnType = Subst.subst(newReturnType, outerYs, outerThis);
}
}
}
if (! newReturnType.isVoid() && ! xts.isUnknown(newReturnType)) {
Type nrt = Subst.addIn(newReturnType, returnEnv2);
if (xts.consistent(nrt, context))
newReturnType = nrt;
if ((! isStatic) && (! yeqvIsSymbol) ) {
nrt = Subst.project(newReturnType, ythiseqv);
if (xts.consistent(nrt, context))
newReturnType = nrt;
}
for (int i= 1; i < actuals.size()+1; ++i) {
nrt = Subst.project(newReturnType, (XVar) ys[i]);
if (xts.consistent(nrt, context))
newReturnType = nrt;
Type t = actualsIn.get(i-1);
XVar self = t instanceof ConstrainedType ? Types.selfVar((ConstrainedType) t) : null;
if (self != null) {
nrt = Subst.project(newReturnType, self);
if (xts.consistent(nrt, context))
newReturnType = nrt;
}
}
}
if (! xts.consistent(newReturnType, context)) {
throw new Errors.InconsistentReturnType(newReturnType, me);
}
newReturnTypeRef.update(newReturnType);
}
catch (SemanticException e) {
newReturnTypeRef.update(xts.unknownType(me.position()));
}
}
});
newMe = (X10ProcedureInstance<?>) newMe.returnTypeRef(newReturnTypeRef);
}
{ // set up the new formal types. These are obtained from the
// formal types by replacing x's by y's and this by the yeqv, and
// substituting in type parameters. With this normalization,
// checkCall will simply have to check that the types of the
// actuals are a subtype of the formals.
// substitute in the information about this.
List<Type> newFormalTypes = new ArrayList<Type>();
if (checkActuals) {
for (Type t : formals) {
t = Subst.subst(t, y2eqv, x2, Y, X);
t = Subst.subst(t, currentPlace, codePlace);
newFormalTypes.add(t);
}
} else {
CConstraint env = null;
if (! isStatic) {
env = Types.xclause(thisType);
if (env != null && ythiseqv != null && ! ((env == null) || env.valid())) {
env = env.instantiateSelf(ythiseqv);
}
}
for (Type t : formals) {
t = Subst.subst(t, y2eqv, x2, Y, X);
t = Subst.subst(t, currentPlace, codePlace);
if (! (env == null || env.valid())) {
if (! isStatic)
t = Subst.addIn(t, env);
}
if (! isStatic && ! yeqvIsSymbol) {
t = Subst.project(t, (XVar) ys[0]);
}
newFormalTypes.add(t);
}
}
newMe = (X10ProcedureInstance<?>) newMe.formalTypes(newFormalTypes);
}
{ // set up the new formals as well
// the formal parameters might appear in constraints.
// Note that checkActuals may be false, so we are not going to check
// now simply return a newMe which will be used later in coercions
newMe = newMe.formalNames(yLocalInstances);
// At this point each yLocalInstances(i) has the type of the
// corresponding actual. This will be replaced after checking with the
// formal type. They type cannot be changed to the formal type before
// checking.
}
{ // set up throws types
List<Type> newThrowTypes = new ArrayList<Type>();
// [DC] don't think we allow constraints on throws types
for (Type t : Types.expandTypes(me.throwTypes(), xts)) {
t = Subst.subst(t, y2eqv, x2, Y, X);
// [DC] no idea what this place stuff is for
t = Subst.subst(t, currentPlace, codePlace);
newThrowTypes.add(t);
}
newMe = (X10ProcedureInstance<?>) newMe.throwTypes(newThrowTypes);
}
{ // set up the guard.
CConstraint newWhere = Subst.subst(me.guard(), y2eqv, x2, Y, X);
newWhere = Subst.subst(newWhere, currentPlace, codePlace);
newMe = newMe.guard(newWhere);
}
{ // set up the type guard.
TypeConstraint newTWhere = Subst.subst(me.typeGuard(), y2eqv, x2, Y, X);
newTWhere = Subst.subst(newTWhere, currentPlace, codePlace);
newMe = newMe.typeGuard(newTWhere);
}
if (! checkActuals) {
// Update the types to reflect the newly computed formalTypes before returning.
for (int i=0; i < yLocalInstances.size(); i++) {
LocalInstance li = yLocalInstances.get(i);
Ref<Type> ref = (Ref<Type>) li.def().type();
ref.update(newMe.formalTypes().get(i));
}
return (PI) newMe;
}
// Now check that the actual types are a subtype of the formal types,
// and the method guards are satisfied.
final Context context2 = context.pushAdditionalConstraint(returnEnv, me.position());
final CConstraint query = newMe.guard();
X10CompilerOptions opts = (X10CompilerOptions) context.typeSystem().extensionInfo().getOptions();
// we can do dynamic checks on method calls when using DYNAMIC_CHECKS or VERBOSE_CHECKS
boolean dynamicChecks = !opts.x10_config.STATIC_CHECKS &&
!(newMe instanceof MacroType); // MacroType cannot have its guard checked at runtime
if ( query != null) {
if (! query.consistent())
throw new SemanticException("Call invalid; guard inconsistent for actual parameters of call.");
if (! returnEnv.entails(query,
new ConstraintMaker() {
public CConstraint make() throws XFailure {
return context2.constraintProjection(returnEnv, query);
}})) {
if (dynamicChecks)
newMe = newMe.checkConstraintsAtRuntime(true);
else
throw new SemanticException("Call invalid; calling environment does not entail the method guard."
+ "\n\t arg types:" + actualsIn
+ "\n\t query residue: " + returnEnv.residue(query));
}
}
List<Type> typeFormals2 = newMe.typeParameters();
TypeConstraint tenv = new TypeConstraint();
for (int i = 0; i < typeFormals.size(); i++) {
tenv.addTerm(new SubtypeConstraint(typeFormals2.get(i), Y[i], true));
}
if (! tenv.consistent(context2)) {
throw new SemanticException("Call invalid; type environment is inconsistent.");
}
TypeConstraint tQuery = newMe.typeGuard();
if (tQuery != null) {
if ( ! xts.consistent(tQuery, context2)) {
throw new SemanticException("Type guard " + tQuery
+ " cannot be established; inconsistent in calling context.");
}
if (! tenv.entails(tQuery, context2)) {
throw new SemanticException("Call invalid; calling environment does not entail the method type guard."
+ "\n\t Type args:" + typeActuals
+ "\n\t Type env:" + tenv
+ "\n\t Query residue: " + tQuery);
}
}
final List<Type> myFormals = new ArrayList<Type>(newMe.formalTypes()); // copy
for (int i = 0; i < formals.size(); i++) {
Type ytype = Subst.subst(actuals.get(i), y2eqv, x2, Y, X);
Type xtype = Subst.subst(myFormals.get(i), y2eqv, x2, Y, X);
if (! xts.consistent(xtype, context2)) {
throw new SemanticException("Parameter type " + xtype
+ " of call is inconsistent in calling context.");
}
if (! xts.isSubtype(ytype, xtype, context2)) {
if (dynamicChecks && xts.isSubtype(Types.baseType(ytype),
Types.baseType(xtype), context2))
newMe = newMe.checkConstraintsAtRuntime(true);
else
throw Errors.InvalidParameter.make(i, newMe, ytype, xtype, context2, me.position());
}
}
// Update the types to reflect the newly computed formalTypes.
// These have now been verified, unless dynamicChecks is true
// in which case Desugarer will generate the checks to
// ensure that the actuals meet the constraints of the formalTypes.
for (int i=0; i < yLocalInstances.size(); i++) {
LocalInstance li = yLocalInstances.get(i);
Ref<Type> ref = (Ref<Type>) li.def().type();
ref.update(newMe.formalTypes().get(i));
}
return (PI) newMe;
}
/**
* Return the conjunction of the constraints from the receiver and
* actuals.
* @param thisType
* @param actuals
* @param ythis
* @param y
* @param isStatic
* @param xts
* @return
* @throws SemanticException
*/
private static CConstraint computeNewSigma(Type thisType, List<Type> actuals,
XVar ythis, XVar[] y, boolean isStatic, TypeSystem xts)
throws SemanticException {
CConstraint env = null;
if (! isStatic) {
env = Types.xclause(thisType);
if (env != null && ythis != null && ! ((env == null) || env.valid()))
env = env.instantiateSelf(ythis);
}
if (env == null)
env = ConstraintManager.getConstraintSystem().makeCConstraint();
for (int i = 0; i < actuals.size(); i++) { // conjoin ytype's realX
Type ytype = actuals.get(i);
final CConstraint yc = Types.realX(ytype);
if (yc != null && ! yc.valid()) {
env.addIn(y[i], yc);
if (! env.consistent())
throw new Errors.InconsistentContext(ytype, Position.COMPILER_GENERATED);
}
}
return env;
}
/**
* Return thisType's xclause, with ythis substituted for self, and
* with each ag y(i)'s constraint added in (with y(i)/self).
* The return type may reference an arg,
* @param thisType
* @param actuals
* @param ythis
* @param y
* @param isStatic
* @param xts
* @return
* @throws SemanticException
*/
private static CConstraint computeNewSigma2(Type thisType, List<Type> actuals,
XVar ythis, XVar[] y, boolean isStatic, TypeSystem xts)
throws SemanticException {
CConstraint env = null;
if (! isStatic) {
env = Types.xclause(thisType);
if (env != null && ythis != null && ! ((env == null) || env.valid()))
env = env.instantiateSelf(ythis);
}
if (env == null)
env = ConstraintManager.getConstraintSystem().makeCConstraint();
//To do: Not sure these need to be added to Gamma. Constraint projection will retrieve them
// from the types of the variables.
for (int i = 0; i < actuals.size(); i++) { // update Gamma
Type ytype = actuals.get(i);
final CConstraint yc = Types.realX(ytype);
if (yc != null && ! yc.valid()) {
env.addIn(y[i], yc);
if (! env.consistent()) {
throw new Errors.InconsistentContext(ytype, Position.COMPILER_GENERATED);
}
}
}
return env;
}
/**
* If the given type says that self == x, then return x.
* Otherwise make up a new symbol (with no associated type
* information) and return it.
* @param type
* @param prefix
* @return
*/
private static XVar getSymbolVar(Type type) {
XVar symbol = Types.selfVarBinding(type);
if (symbol == null) {
symbol = ConstraintManager.getConstraintSystem().makeUQV();
}
return symbol;
}
private static X10LocalInstance getSymbol( Type type) {
TypeSystem ts = type.typeSystem();
Ref<Type> ref = new LazyRef_c<Type>(type);
X10LocalDef def = new X10LocalDef_c(ts,
Position.compilerGenerated(type.position()), Flags.FINAL, ref,
Name.makeFresh("arg"));
X10LocalInstance li = new X10LocalInstance_c(ts,
Position.compilerGenerated(type.position()),
new LazyRef_c<X10LocalDef>(def));
if (! (type instanceof UnknownType))
ref.update(Types.addSelfBinding(type, type.typeSystem().xtypeTranslator().translate(li)));
return li;
}
private static List<LocalInstance> getSymbolicNames(List<Type> actuals) {
List<LocalInstance> ySymbols = new ArrayList<LocalInstance>(actuals.size());
for (Type actual : actuals) {
ySymbols.add(getSymbol(actual));
}
return ySymbols;
}
public static XVar[] getSymbolicNames(List<? extends LocalInstance> formalNames, TypeSystem xts)
throws SemanticException {
XVar[] x = new XVar[formalNames.size()];
for (int i = 0; i < formalNames.size(); i++) {
x[i]=xts.xtypeTranslator().translate(formalNames.get(i));
assert x[i] != null;
}
return x;
}
}