/*
* 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.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import polyglot.ast.Call;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.ConstructorCall;
import polyglot.ast.Expr;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.TypeNode;
import polyglot.frontend.Globals;
import polyglot.frontend.Job;
import polyglot.main.Reporter;
import polyglot.types.ClassDef;
import polyglot.types.ConstructorInstance;
import polyglot.types.Context;
import polyglot.types.Def;
import polyglot.types.Matcher;
import polyglot.types.MemberInstance;
import polyglot.types.Flags;
import polyglot.types.LazyRef_c;
import polyglot.types.LocalInstance;
import polyglot.types.Name;
import polyglot.types.NoMemberException;
import polyglot.types.ObjectType;
import polyglot.types.ProcedureDef;
import polyglot.types.ProcedureInstance;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.types.UnknownType;
import polyglot.util.ErrorInfo;
import polyglot.util.Pair;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.visit.ContextVisitor;
import x10.Configuration;
import x10.ExtensionInfo;
import x10.X10CompilerOptions;
import x10.ast.OperatorNames;
import x10.ast.X10CanonicalTypeNode;
import x10.ast.X10CanonicalTypeNode_c;
import x10.ast.X10Cast;
import x10.ast.X10Cast_c;
import x10.ast.X10New_c;
import x10.ast.X10ProcedureCall;
import x10.ast.X10New_c.MatcherMaker;
import x10.constraint.XConstraint;
import x10.constraint.XFailure;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.errors.Warnings;
import x10.types.ParameterType;
import x10.types.TypeParamSubst;
import x10.types.X10ClassType;
import x10.types.MethodInstance;
import x10.types.X10LocalDef;
import x10.types.X10LocalDef_c;
import x10.types.X10LocalInstance;
import x10.types.X10LocalInstance_c;
import polyglot.types.Context;
import x10.types.X10ParsedClassType_c;
import x10.types.X10ProcedureDef;
import x10.types.X10ProcedureInstance;
import polyglot.types.TypeSystem;
import x10.types.constants.ConstantValue;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CLocal;
import x10.types.constraints.TypeConstraint;
import x10.types.matcher.Subst;
import x10.util.Synthesizer;
/**
* A set of static methods used to convert an AST representing an X10 expressions of a given type
* into an AST representing an expressions of another type.
*
* @author vj 2/6/2010
*
*/
public class Converter {
public static enum ConversionType {
UNKNOWN_CONVERSION,
UNKNOWN_IMPLICIT_CONVERSION,
CALL_CONVERSION, // vj: Introduced 3/28/10 to implement cast-as-needed call semantics
DESUGAR_LATER,
PRIMITIVE,
CHECKED,
SUBTYPE,
UNBOXING,
BOXING,
UNCHECKED;
public boolean isChecked() { return this==CHECKED || this==DESUGAR_LATER; }
}
/**
* Return the expression, obtained from e through a sequence of operations, that is of type toType
* @param tc -- Visitor to use during construction
* @param e -- the subject expression
* @param toType -- the target type
* @return -- the expression constructed from e of type toType
* @throws SemanticException If this is not possible
*/
public static Expr attemptCoercion(ContextVisitor tc, Expr e, Type toType) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
Type t1 = e.type();
t1 = PlaceChecker.ReplaceHereByPlaceTerm(t1, (Context) tc.context());
if (ts.isSubtype(t1, toType, tc.context()))
return e;
ConversionType ct = ts.numericConversionValid(toType, e.type(), ConstantValue.toJavaObject(e.constantValue()), tc.context())
? ConversionType.UNKNOWN_CONVERSION
: ConversionType.CALL_CONVERSION; // ConversionType.UNKNOWN_IMPLICIT_CONVERSION;
NodeFactory nf = (NodeFactory) tc.nodeFactory();
X10CanonicalTypeNode tn = (X10CanonicalTypeNode) nf.CanonicalTypeNode(e.position(), toType);
Expr result = typeCheckCast(nf.X10Cast(e.position(), tn, e, ct), tc);
if (result instanceof X10Cast && ((X10Cast) result).conversionType()==ConversionType.CHECKED) {
// OK that succeeded. Now ensure that there is a depexpr created for the check.
CConstraint cn = Types.xclause(toType);
if (cn.hasPlaceTerm()) {
// Failed to translate the constraint
// For now the only possibility is the constraint refers
// to a variable synthesized by the compiler.
//throw new Errors.CannotGenerateCast(e, e.position());
return null;
}
tn = new Synthesizer(nf, ts).makeCanonicalTypeNodeWithDepExpr(e.position(), toType, tc);
if (tn.type() != toType) {
// alright, now we actually synthesized a new depexpr.
// lets splice it in.
result = typeCheckCast(nf.X10Cast(e.position(), tn, e, ct), tc);
}
}
return result;
}
private static Expr typeCheckCast(X10Cast cast, ContextVisitor tc) {
if (cast.castType() != null) {
try {
Types.checkMissingParameters(cast.castType());
} catch (SemanticException e) {
return null;
}
}
try {
Expr e = Converter.converterChain((X10Cast_c) cast, tc);
assert e.type() != null;
assert ! (e instanceof X10Cast_c) || ((X10Cast_c) e).conversionType() != Converter.ConversionType.UNKNOWN_CONVERSION;
assert ! (e instanceof X10Cast_c) || ((X10Cast_c) e).conversionType() != Converter.ConversionType.UNKNOWN_IMPLICIT_CONVERSION;
return e;
} catch (SemanticException e) {
return null;
}
//return cast;
}
/**
*
* @param <PD>
* @param <PI>
* @param n
* @param tc
* @param targetType
* @param methods
* Unsubstituted, uninstantiated methods. Need to go through
* MethodMatcher.instantiate to use.
* @param maker
* @return
* @throws SemanticException
*/
public static <PD extends ProcedureDef, PI extends ProcedureInstance<PD>> Pair<PI, List<Expr>>
tryImplicitConversions(X10ProcedureCall n,
ContextVisitor tc, Type targetType, List<PI> methods, X10New_c.MatcherMaker<PI> maker) throws SemanticException {
NodeFactory nf = (NodeFactory) tc.nodeFactory();
TypeSystem ts = (TypeSystem) tc.typeSystem();
Reporter reporter = ts.extensionInfo().getOptions().reporter;
Context xc = (Context) tc.context();
ClassDef currentClassDef = xc.currentClassDef();
List<PI> acceptable = new ArrayList<PI>();
Map<Def, List<Expr>> newArgs = CollectionFactory.newHashMap();
List<Type> typeArgs = new ArrayList<Type>(n.typeArguments().size());
for (TypeNode tn : n.typeArguments()) {
typeArgs.add(tn.type());
}
METHOD: for (PI smi : methods) {
X10ProcedureInstance<?> xmi = (X10ProcedureInstance<?>) smi;
if (reporter.should_report(Reporter.types, 3))
reporter.report(3, "Trying " + smi);
List<ParameterType> typeParameters = ((X10ProcedureDef) xmi.def()).typeParameters();
if (typeParameters.size() != typeArgs.size()) {
if (!typeArgs.isEmpty()) continue METHOD; // Number of arguments doesn't match
Type container = (smi instanceof MemberInstance<?>) ? ((MemberInstance<?>) smi).container() : null;
List<Type> actuals = new ArrayList<Type>();
for (Expr e : n.arguments()) actuals.add(e.type());
List<Type> typeFormals = new ArrayList<Type>(typeParameters);
Type[] ta = TypeConstraint.inferTypeArguments(xmi, container, actuals, smi.formalTypes(), typeFormals, xc);
typeArgs = Arrays.asList(ta);
smi = new TypeParamSubst(ts, typeArgs, typeParameters).reinstantiate(smi);
}
List<Expr> transformedArgs = new ArrayList<Expr>();
List<Type> transformedArgTypes = new ArrayList<Type>();
List<XVar> transformedYs = new ArrayList<XVar>();
List<Type> formals = smi.formalTypes();
ContextVisitor argtc = tc.context(xc.pushBlock());
boolean checkAtRuntime = false;
for (int j = 0; j < n.arguments().size(); j++) {
Expr e = n.arguments().get(j);
Type toType = formals.get(j);
// toType may have occurrences of CLoc's corresponding to the args
// k in 0..j-1. These must be treated as of type transformedArgTypes.get(k).
// Therefore substitute transformedYs.get(k) for the original CLoc.
for (int k=0; k < j; k++) {
toType = Subst.subst(toType, transformedYs.get(k),
ConstraintManager.getConstraintSystem().makeLocal((X10LocalDef) smi.formalNames().get(k).def()));
}
// In DYNAMIC_CHECKS we can't just insert a cast for each argument due to dependencies between arguments, e.g.,
//def m(a:Int, b:Int{self==a}) {}
//def test(x:Int, y:Int) {
// m(x+1,y);
//}
//will be desugared into:
//def m(a:Int, b:Int{self==a}) {}
//def test(x:Int, y:Int) {
// ( (a:Int, b:Int) => if (!(b==a)) throw new ...; m(a,b)) (x+1,y);
//}
Expr e2 = attemptCoercion(argtc, e, toType);
// attemptCoercion is used in many places (for loops, local&field
// init expressions, etc), so we special handle it for method calls
if (e2 instanceof X10Cast) {
X10Cast e2Cast = (X10Cast) e2;
if (e2Cast.conversionType()==ConversionType.DESUGAR_LATER)
e2 = e2Cast
.conversionType(ConversionType.SUBTYPE)
.type(Types.baseType(e2Cast.type()));
// because in instantiate we will flag the method call as
// checkGuardAtRuntime and create a closure for it
}
if (e2 == null)
continue METHOD; // this method def is not applicable for this call
Type e2Type = e2.type();
if (e2Type instanceof UnknownType)
continue METHOD;
Type nType = e2Type;
for (int k = 0; k < j; k++) {
nType = Subst.subst(nType, ConstraintManager.getConstraintSystem().makeEQV(), transformedYs.get(k));
}
if (!nType.typeEquals(e2Type, argtc.context())) {
// Do not add e2. This may contain some of the new variables
// and they won't be in scope for the Desugarer.
// Let the Desugarer again generate e2.
transformedArgs.add(e);
transformedArgTypes.add(toType);
checkAtRuntime = true;
} else {
transformedArgs.add(e2);
transformedArgTypes.add(e2Type);
}
{
// Construct the new transformedY.
Ref<Type> ref = new LazyRef_c<Type>(e2Type);
X10LocalDef def = X10LocalDef_c.makeHidden(ts, Position.COMPILER_GENERATED, Flags.FINAL, ref,
Name.makeFresh("arg"));
XVar y = ConstraintManager.getConstraintSystem().makeLocal(def);
ref.update(Types.addSelfBinding(e2Type, y));
transformedYs.add(y);
argtc.context().addVariable(def.asInstance());
}
}
try {
Matcher<PI> matcher = maker.matcher(targetType, typeArgs, transformedArgTypes);
// ((X10ProcedureInstance) smi).returnType();
// X10MethodInstance_c.checkCall(xc, (X10ProcedureInstance) smi,
// targetType, typeArgs, transformedArgTypes);
// // smi = (PI) matcher.instantiate(smi);
// Reinstantiate using the new argument types.
// Be careful to re-subst in the type arguments of the container type.
// This should be cleaner!
PI raw = (PI) smi.def().asInstance();
if (smi instanceof MemberInstance<?>) {
Type container = ((MemberInstance<?>) smi).container();
Type base = Types.baseType(container);
if (base instanceof X10ClassType) {
X10ParsedClassType_c ct = (X10ParsedClassType_c) base;
raw = ct.subst().reinstantiate(raw);
}
}
PI smi2 = (PI) matcher.instantiate(raw);
if (smi2 instanceof MethodInstance) {
((MethodInstance) smi2).setOrigMI((MethodInstance) raw);
smi2 = (PI) smi2.checkConstraintsAtRuntime(checkAtRuntime);
} else {
if (smi2 instanceof ConstructorInstance) {
((ConstructorInstance) smi2).setOrigMI((ConstructorInstance) raw);
}
}
// ((X10ProcedureInstance) smi2).returnType();
acceptable.add(smi2);
newArgs.put(smi2.def(), transformedArgs);
}
catch (SemanticException e) {
int q = 3;
}
}
if (acceptable.size() == 0) {
if (n instanceof New || n instanceof ConstructorCall)
throw new NoMemberException(NoMemberException.CONSTRUCTOR,
"Could not find matching constructor in " + targetType + ".",
n.position());
else
throw new NoMemberException(NoMemberException.METHOD,
"Could not find matching method in " + targetType + ".",
n.position());
}
Collection<PI> maximal = ts.<PD, PI> findMostSpecificProcedures(acceptable, (Matcher<PI>) null, xc);
if (maximal.size() > 1) {
StringBuffer sb = new StringBuffer();
for (Iterator<PI> i = maximal.iterator(); i.hasNext();) {
PI ma = (PI) i.next();
if (ma instanceof MemberInstance<?>) {
sb.append(((MemberInstance<?>) ma).container());
sb.append(".");
}
sb.append(ma.signature());
if (i.hasNext()) {
if (maximal.size() == 2) {
sb.append(" and ");
}
else {
sb.append(", ");
}
}
}
if (n instanceof New || n instanceof ConstructorCall)
throw new NoMemberException(NoMemberException.CONSTRUCTOR, "Reference to "
+ targetType + " is ambiguous, multiple " + "constructors match: "
+ sb.toString(), n.position());
else
throw new NoMemberException(NoMemberException.METHOD, "Reference to "
+ targetType + " is ambiguous, multiple " + "methods match: "
+ sb.toString(), n.position());
}
PI mi;
mi = (PI) maximal.iterator().next();
List<Expr> args = newArgs.get(mi.def());
assert args != null;
return new Pair<PI, List<Expr>>(mi, args);
}
/** Return list of conversion functions needed to convert from fromType to toType */
public static Expr converterChain(final X10Cast cast, final ContextVisitor tc) throws SemanticException {
try {
return Converter.checkCast(cast, tc);
}
catch (SemanticException e) {
}
TypeSystem ts = (TypeSystem) tc.typeSystem();
final NodeFactory nf = (NodeFactory) tc.nodeFactory();
final Context context = tc.context();
class Helper {
Expr attempt(X10ClassType ct, int i, List<Type>[] alternatives,
Type fromType, List<Type> accum, Type toType, boolean changed)
throws SemanticException {
assert alternatives.length == accum.size();
if (i < alternatives.length) {
try {
accum.set(i, ct.typeArguments().get(i));
return attempt(ct, i+1, alternatives, fromType, accum, toType, changed);
}
catch (SemanticException e) {
}
for (Type ti : alternatives[i]) {
try {
accum.set(i, ti);
return attempt(ct, i+1, alternatives, fromType, accum, toType, true);
}
catch (SemanticException e) {
}
}
}
else if (changed) {
X10ClassType ct2 = ct.typeArguments(accum);
Type newFrom = Types.xclause(Types.baseType(ct2), Types.xclause(fromType));
if (fromType.typeEquals(newFrom, context)) {
assert false;
}
if (newFrom.isSubtype(toType, context))
return cast.expr();
X10Cast newCast = nf.X10Cast(cast.position(),
nf.CanonicalTypeNode(cast.position(), newFrom), cast.expr(),
Converter.ConversionType.UNKNOWN_IMPLICIT_CONVERSION);
Expr newE = converterChain(newCast, tc);
assert newE.type() != null;
X10Cast newC = (X10Cast) cast.expr(newE);
return Converter.checkCast(newC, tc); // FIXME
}
throw new Errors.CannotConvertToType(fromType, toType, cast.position());
}
void addSuperTypes(List<Type> l, Type t) {
Type b = Types.baseType(t);
if (! b.typeSystem().typeEquals(b, t, context)) {
l.add(b);
}
else
if (t instanceof ObjectType) {
ObjectType o = (ObjectType) t;
if (o.superClass() != null) {
l.add(o.superClass());
}
for (Type ti : o.interfaces()) {
l.add(ti);
}
}
}
}
Type fromType = cast.expr().type();
Type toType = cast.castType().type();
// If the fromType has a covariant parameter,
// try supertypes of the corresponding argument type.
Type baseFrom = Types.baseType(fromType);
if (baseFrom instanceof X10ClassType) {
X10ClassType ct = (X10ClassType) baseFrom;
if (ct.typeArguments() != null && ct.typeArguments().size() > 0) {
List<Type>[] alternatives = new List[ct.typeArguments().size()];
List<Type> newArgs = new ArrayList<Type>(ct.typeArguments().size());
if (ct.x10Def().variances().size() != ct.typeArguments().size()) {
// an error would have been reported already
throw new Errors.CannotConvertExprToType(cast.expr(), cast.conversionType(), toType, cast.position());
}
for (int i = 0; i < ct.typeArguments().size(); i++) {
ParameterType.Variance v = ct.x10Def().variances().get(i);
Type ti = ct.typeArguments().get(i);
alternatives[i] = new ArrayList<Type>();
switch (v) {
case COVARIANT:
new Helper().addSuperTypes(alternatives[i], ti);
break;
default:
break;
}
}
// Now, try all possible combinations of the alternative type arguments.
try {
return new Helper().attempt(ct, 0, alternatives, fromType, new ArrayList<Type>(ct.typeArguments()), toType, false);
}
catch (SemanticException e) {
// Fall through.
}
}
}
throw new Errors.CannotConvertExprToType(cast.expr(), cast.conversionType(), toType, cast.position());
}
public static Expr checkCast(X10Cast cast, ContextVisitor tc) throws SemanticException {
X10CompilerOptions opts = (X10CompilerOptions) tc.job().extensionInfo().getOptions();
TypeSystem ts = tc.typeSystem();
Type toType = cast.castType().type();
Type fromType = cast.expr().type();
Context context = tc.context();
if (ts.isUnknown(toType)) {
if (opts.x10_config.CHECK_INVARIANTS)
Errors.issue(tc.job(), new Errors.UnknownType(cast.position()));
return cast;
}
if (ts.isVoid(toType) || ts.isVoid(fromType))
throw new Errors.CannotConvertToType(fromType, toType, cast.position());
Expr withoutCoercion = checkCastWithoutCoercions(cast, tc);
// even if withoutCoercion!=null, I still want to check coercions because I need to produce a warning
// if we favor a system-as over a user-defined-as (both implicit and explicit)
Expr withCoercion = checkCastWithCoercions(cast, tc);
if (withoutCoercion!=null && withCoercion!=null) {
// produce a warning
// todo: add a verbose flag
if (opts.x10_config.VERBOSE)
Warnings.issue(tc.job(), "The casting can be done both by a user-defined coercion and system cast, and the compiler always favors a system cast over a user-defined cast. Make sure this is the desired behavior.", cast.position());
}
if (withoutCoercion!=null) return withoutCoercion;
if (withCoercion!=null) return withCoercion;
throw new Errors.CannotConvertExprToType(cast.expr(), cast.conversionType(), toType, cast.position());
}
private static Expr checkCastWithoutCoercions(X10Cast cast, ContextVisitor tc) throws SemanticException {
X10CompilerOptions opts = (X10CompilerOptions) tc.job().extensionInfo().getOptions();
TypeSystem ts = tc.typeSystem();
Type toType = cast.castType().type();
Type fromType = cast.expr().type();
Context context = tc.context();
// Is it an upcast?
if (ts.isSubtype(fromType, toType, context)) {
// Add the clause self==x if the fromType's self binding is x,
// since for these casts we know the result is identical to expr.
//XTerm sv = Types.selfBinding(fromType);
//if (sv != null)
// toType = Types.addSelfBinding((Type) toType.copy(), sv);
X10Cast n = cast.conversionType(ConversionType.SUBTYPE);
return n.type(toType);
}
// is it a downcast?
if (cast.conversionType() != ConversionType.UNKNOWN_IMPLICIT_CONVERSION
&& cast.conversionType() != ConversionType.CALL_CONVERSION) {
if (! ts.isParameterType(fromType)
&& ! ts.isParameterType(toType)
&& ts.isCastValid(fromType, toType, context)) {
X10Cast n = cast.conversionType(ConversionType.CHECKED);
XTerm sv = Types.selfBinding(fromType);
if (sv != null)
toType = Types.addSelfBinding((Type) toType.copy(), sv);
return n.type(toType);
}
}
l: if (cast.conversionType() != ConversionType.UNKNOWN_IMPLICIT_CONVERSION
&& cast.conversionType() != ConversionType.CALL_CONVERSION) {
if (ts.isParameterType(toType)) {
// Now get the upper bound.
List<Type> upper = ts.env(context).upperBounds(toType, false);
if (upper.isEmpty()) {
// No upper bound. Now a checked conversion is permitted only
// if fromType is not Null.
if (! fromType.isNull())
return checkedConversionForTypeParameter(cast, fromType, toType);
} else {
for (Type t : upper)
if (ts.isSubtype(fromType, t))
return checkedConversionForTypeParameter(cast, fromType, toType);
}
} else if (ts.isParameterType(fromType)) {
// Now get the upper bound.
List<Type> upper = ts.env(context).upperBounds(fromType, false);
for (Type t : upper)
if (! ts.isSubtype(t, toType))
break l;
return checkedConversionForTypeParameter(cast, fromType, toType);
}
}
// Added 03/28/10 to support new call conversion semantics.
Type baseFrom = Types.baseType(fromType);
Type baseTo = Types.baseType(toType);
if (ts.isSubtype(baseFrom, baseTo, context))
if (!opts.x10_config.STATIC_CHECKS)
if (( cast.conversionType() == ConversionType.CALL_CONVERSION)
&& ts.isCastValid(fromType, toType, context)) {
//return cast.conversionType(ConversionType.DESUGAR_LATER).type(baseTo);
X10Cast n = cast.conversionType(ConversionType.DESUGAR_LATER);
XVar sv = Types.selfVarBinding(fromType); // FIXME: Vijay, can this be an XTerm? -Bowen
if (sv != null)
toType = Types.addSelfBinding((Type) toType.copy(), sv);
return n.type(toType);
}
return null;
}
private static Expr checkCastWithCoercions(X10Cast cast, ContextVisitor tc) throws SemanticException {
TypeSystem ts = tc.typeSystem();
Type toType = cast.castType().type();
Type fromType = cast.expr().type();
NodeFactory nf = tc.nodeFactory();
Context context = tc.context();
Type baseTo = Types.baseType(toType);
{
MethodInstance converter = null;
Call c = null;
MethodInstance mi = converter;
Position p = cast.position();
Expr e = cast.expr();
// Can convert if there is a static method toType.$convert(fromType)
if (converter == null && cast.conversionType() != ConversionType.UNKNOWN_IMPLICIT_CONVERSION
&& cast.conversionType() != ConversionType.CALL_CONVERSION) {
try {
mi = ts.findMethod(toType, ts.MethodMatcher(toType, Converter.operator_as,
Collections.singletonList(fromType), context));
Type miType = mi.returnType();
Type baseMiType = Types.baseType(miType);
if (mi.flags().isStatic() && baseMiType.isSubtype(baseTo, context)
&& Types.areConsistent(miType, toType)) {
converter = mi;
// Do the conversion.
c = nf.Call(p, nf.CanonicalTypeNode(p, toType), nf.Id(p, mi.name()), e);
c = c.methodInstance(mi);
c = (Call) c.type(mi.returnType());
}
}
catch (SemanticException z1) {
}
}
// or can convert if there is a static implict cast operator defined on toType: static operator (f:fromType)
if (converter == null) {
try {
mi = ts.findMethod(toType, ts.MethodMatcher(toType, Converter.implicit_operator_as,
Collections.singletonList(fromType), context));
Type miType = mi.returnType();
Type baseMiType = Types.baseType(miType);
if (mi.flags().isStatic() && baseMiType.isSubtype(baseTo, context)
&& Types.areConsistent(miType, toType)) {
converter = mi;
// Do the conversion.
c = nf.Call(p, nf.CanonicalTypeNode(p, toType), nf.Id(p, mi.name()), e);
c = c.methodInstance(mi);
c = (Call) c.type(mi.returnType());
}
}
catch (SemanticException z2) {
try {
mi = ts.findMethod(fromType, ts.MethodMatcher(fromType, Converter.implicit_operator_as,
Collections.singletonList(fromType), context));
Type miType = mi.returnType();
Type baseMiType = Types.baseType(miType);
if (mi.flags().isStatic() && baseMiType.isSubtype(baseTo, context)
&& Types.areConsistent(miType, toType)) {
converter = mi;
c = nf.Call(p, nf.CanonicalTypeNode(p, fromType), nf.Id(p, mi.name()), e);
c = c.methodInstance(mi);
c = (Call) c.type(mi.returnType());
}
} catch (SemanticException z) {
}
// yoav todo: we check 3 conditions:
// toType.operator_as(fromType)
// toType.implicit_operator_as(fromType)
// fromType.implicit_operator_as(fromType)
// but we should also check:
// fromType.operator_as(fromType)
// IMPORTANT: we currently disable defining coercions in the fromType (only in the toType), see X10MethodDecl_c: static boolean SEARCH_CASTS_ONLY_IN_TARGET = true; // see XTENLANG_2667
}
}
if (converter != null) {
// Now, do a coercion if needed to check any additional constraints on the type.
if (! ts.isParameterType(fromType) && ! mi.returnType().isSubtype(toType, context)) {
X10Cast n = cast.exprAndConversionType(c, ConversionType.CHECKED);
return n.type(toType);
}
else {
return c;
}
}
}
return null;
}
static Expr checkedConversionForTypeParameter(X10Cast cast, Type fromType, Type toType) {
return cast.conversionType(ConversionType.CHECKED).type(toType);
}
public static <T extends Node> T check(T n, ContextVisitor tc) throws SemanticException {
return (T) n.del().disambiguate(tc).del().typeCheck(tc).del().checkConstants(tc);
}
public static final Name operator_as = OperatorNames.AS;
public static final Name implicit_operator_as = OperatorNames.IMPLICIT_AS;
}