use of org.eclipse.ceylon.model.typechecker.model.SiteVariance in project ceylon by eclipse.
the class TypeUtils method printTypeArguments.
/**
* Prints the type arguments, usually for their reification.
*/
public static void printTypeArguments(final Node node, final Map<TypeParameter, Type> targs, final GenerateJsVisitor gen, final boolean skipSelfDecl, final Map<TypeParameter, SiteVariance> overrides) {
if (targs == null)
return;
gen.out("{");
boolean first = true;
for (Map.Entry<TypeParameter, Type> e : targs.entrySet()) {
if (first) {
first = false;
} else {
gen.out(",");
}
gen.out(gen.getNames().typeParameterName(e.getKey()), ":");
final Type pt = e.getValue() == null ? null : e.getValue().resolveAliases();
if (pt == null) {
gen.out("'", e.getKey().getName(), "'");
} else if (!outputTypeList(node, pt, gen, skipSelfDecl)) {
boolean hasParams = pt.getTypeArgumentList() != null && !pt.getTypeArgumentList().isEmpty();
boolean closeBracket = false;
final TypeDeclaration d = pt.getDeclaration();
if (pt.isTypeParameter()) {
resolveTypeParameter(node, (TypeParameter) d, gen, skipSelfDecl);
if (((TypeParameter) d).isInvariant() && (e.getKey().isCovariant() || e.getKey().isContravariant())) {
gen.out("/*ORALE!", d.getQualifiedNameString(), " inv pero ", e.getKey().getQualifiedNameString(), e.getKey().isCovariant() ? " out" : " in", "*/");
}
} else {
closeBracket = !pt.isTypeAlias();
if (closeBracket)
gen.out("{t:");
outputQualifiedTypename(node, node != null && gen.isImported(node.getUnit().getPackage(), pt.getDeclaration()), pt, gen, skipSelfDecl);
}
if (hasParams) {
gen.out(",a:");
printTypeArguments(node, pt.getTypeArguments(), gen, skipSelfDecl, pt.getVarianceOverrides());
}
SiteVariance siteVariance = overrides == null ? null : overrides.get(e.getKey());
printSiteVariance(siteVariance, gen);
if (closeBracket) {
gen.out("}");
}
}
}
gen.out("}");
}
use of org.eclipse.ceylon.model.typechecker.model.SiteVariance in project ceylon by eclipse.
the class RefinementVisitor method checkRefiningMemberUpperBounds.
private List<Type> checkRefiningMemberUpperBounds(Tree.Declaration that, ClassOrInterface ci, Declaration refined, List<TypeParameter> refinedTypeParams, List<TypeParameter> refiningTypeParams) {
int refiningSize = refiningTypeParams.size();
int refinedSize = refinedTypeParams.size();
int max = refiningSize <= refinedSize ? refiningSize : refinedSize;
if (max == 0) {
return NO_TYPE_ARGS;
}
// we substitute the type parameters of the refined
// declaration into the bounds of the refining
// declaration
Map<TypeParameter, Type> substitution = new HashMap<TypeParameter, Type>();
for (int i = 0; i < max; i++) {
TypeParameter refinedTypeParam = refinedTypeParams.get(i);
TypeParameter refiningTypeParam = refiningTypeParams.get(i);
substitution.put(refiningTypeParam, refinedTypeParam.getType());
}
Map<TypeParameter, SiteVariance> noVariances = emptyMap();
TypeDeclaration rc = (TypeDeclaration) refined.getContainer();
// we substitute the type arguments of the subtype's
// instantiation of the supertype into the bounds of
// the refined declaration
Type supertype = ci.getType().getSupertype(rc);
Map<TypeParameter, Type> args = supertype.getTypeArguments();
Map<TypeParameter, SiteVariance> variances = supertype.getVarianceOverrides();
List<Type> typeArgs = new ArrayList<Type>(max);
for (int i = 0; i < max; i++) {
TypeParameter refinedTypeParam = refinedTypeParams.get(i);
TypeParameter refiningTypeParam = refiningTypeParams.get(i);
refiningTypeParam.setReified(refinedTypeParam.isReified());
Type refinedProducedType = refinedTypeParam.getType();
List<Type> refinedBounds = refinedTypeParam.getSatisfiedTypes();
List<Type> refiningBounds = refiningTypeParam.getSatisfiedTypes();
Unit unit = that.getUnit();
for (Type bound : refiningBounds) {
Type refiningBound = bound.substitute(substitution, noVariances);
// for every type constraint of the refining member, there must
// be at least one type constraint of the refined member which
// is assignable to it, guaranteeing that the intersection of
// the refined member bounds is assignable to the intersection
// of the refining member bounds
// TODO: would it be better to just form the intersections and
// test assignability directly (the error messages might
// not be as helpful, but it might be less restrictive)
boolean ok = false;
for (Type refinedBound : refinedBounds) {
refinedBound = refinedBound.substitute(args, variances);
if (refinedBound.isSubtypeOf(refiningBound)) {
ok = true;
}
}
if (!ok) {
that.addError("refining member type parameter '" + refiningTypeParam.getName() + "' has upper bound which refined member type parameter '" + refinedTypeParam.getName() + "' of " + message(refined) + " does not satisfy: '" + bound.asString(unit) + "' ('" + refiningTypeParam.getName() + "' should be upper bounded by '" + intersectionOfSupertypes(refinedTypeParam).substitute(args, variances).asString(unit) + "')");
}
}
for (Type bound : refinedBounds) {
Type refinedBound = bound.substitute(args, variances);
boolean ok = false;
for (Type refiningBound : refiningBounds) {
refiningBound = refiningBound.substitute(substitution, noVariances);
if (refinedBound.isSubtypeOf(refiningBound)) {
ok = true;
}
}
if (!ok) {
that.addUnsupportedError("refined member type parameter '" + refinedTypeParam.getName() + "' of " + message(refined) + " has upper bound which refining member type parameter '" + refiningTypeParam.getName() + "' does not satisfy: '" + bound.asString(unit) + "' ('" + refiningTypeParam.getName() + "' should be upper bounded by '" + intersectionOfSupertypes(refinedTypeParam).substitute(args, variances).asString(unit) + "')");
}
}
typeArgs.add(refinedProducedType);
}
return typeArgs;
}
use of org.eclipse.ceylon.model.typechecker.model.SiteVariance in project ceylon by eclipse.
the class TypeArgumentInference method inferTypeArg.
private Type inferTypeArg(TypeParameter tp, Type paramType, Type argType, boolean covariant, boolean contravariant, boolean findingUpperBounds, List<TypeParameter> visited, Node argNode) {
if (paramType != null && argType != null) {
paramType = paramType.resolveAliases();
argType = argType.resolveAliases();
TypeDeclaration paramTypeDec = paramType.getDeclaration();
Map<TypeParameter, Type> paramTypeArgs = paramType.getTypeArguments();
Map<TypeParameter, SiteVariance> paramVariances = paramType.getVarianceOverrides();
if (paramType.isTypeParameter() && paramTypeDec.equals(tp)) {
if (tp.isTypeConstructor()) {
if (argType.isTypeConstructor()) {
return argType;
} else {
return null;
}
} else if (findingUpperBounds && covariant || !findingUpperBounds && contravariant) {
// covariant locations in the list
return null;
} else if (argType.isUnknown()) {
// TODO: is this error really necessary now!?
if (!argNode.hasErrors()) {
argNode.addError("argument of unknown type assigned to inferred type parameter: '" + tp.getName() + "' of '" + tp.getDeclaration().getName(unit) + "'");
}
// TODO: would it be better to return UnknownType here?
return null;
} else {
return unit.denotableType(argType);
}
} else if (paramType.isTypeParameter() && !paramTypeDec.isParameterized()) {
TypeParameter tp2 = (TypeParameter) paramTypeDec;
if (!findingUpperBounds && // in the upper bounds
!visited.contains(tp2)) {
visited.add(tp2);
List<Type> sts = tp2.getSatisfiedTypes();
List<Type> list = new ArrayList<Type>(sts.size());
for (Type upperBound : sts) {
// recurse to the upper bounds
addToUnionOrIntersection(findingUpperBounds, list, inferTypeArg(tp, upperBound, argType, covariant, contravariant, findingUpperBounds, visited, argNode));
}
visited.remove(tp2);
return unionOrIntersectionOrNull(findingUpperBounds, list);
} else {
return null;
}
} else if (paramType.isUnion()) {
// If there is more than one type parameter in
// the union, ignore this union when inferring
// types.
// TODO: This is all a bit adhoc. The problem
// is that when a parameter type involves
// a union of type parameters, it in theory
// imposes a compound constraint upon the
// type parameters, but our algorithm
// doesn't know how to deal with compound
// constraints
/*Type typeParamType = null;
boolean found = false;
for (Type ct:
paramType.getDeclaration()
.getCaseTypes()) {
TypeDeclaration ctd =
ct.getDeclaration();
if (ctd instanceof TypeParameter) {
typeParamType = ct;
}
if (ct.containsTypeParameters()) { //TODO: check that they are "free" type params
if (found) {
//the parameter type involves two type
//parameters which are being inferred
return null;
}
else {
found = true;
}
}
}*/
Type pt = paramType;
Type apt = argType;
if (argType.isUnion()) {
for (Type act : argType.getCaseTypes()) {
// from both unions
if (// in a recursive generic function, T can get assigned to T
!act.involvesDeclaration(tp) && act.substitute(argType).isSubtypeOf(paramType)) {
pt = pt.shallowMinus(act);
apt = apt.shallowMinus(act);
}
}
}
if (pt.isUnion()) {
boolean found = false;
for (Type ct : pt.getCaseTypes()) {
if (ct.isTypeParameter()) {
if (found) {
return null;
}
found = true;
}
}
// just one type parameter left in the union
Map<TypeParameter, Type> args = pt.getTypeArguments();
Map<TypeParameter, SiteVariance> variances = pt.getVarianceOverrides();
List<Type> cts = pt.getCaseTypes();
List<Type> list = new ArrayList<Type>(cts.size());
for (Type ct : cts) {
addToUnionOrIntersection(findingUpperBounds, list, inferTypeArg(tp, ct.substitute(args, variances), apt, covariant, contravariant, findingUpperBounds, visited, argNode));
}
return unionOrIntersectionOrNull(findingUpperBounds, list);
} else {
return inferTypeArg(tp, pt, apt, covariant, contravariant, findingUpperBounds, visited, argNode);
}
/*else {
//if the param type is of form T|A1 and the arg type is
//of form A2|B then constrain T by B and A1 by A2
Type pt = paramType.minus(typeParamType);
addToUnionOrIntersection(tp, list, inferTypeArg(tp,
paramType.minus(pt), argType.minus(pt), visited));
addToUnionOrIntersection(tp, list, inferTypeArg(tp,
paramType.minus(typeParamType), pt, visited));
//return null;
}*/
} else if (paramType.isIntersection()) {
List<Type> sts = paramTypeDec.getSatisfiedTypes();
List<Type> list = new ArrayList<Type>(sts.size());
for (Type ct : sts) {
// recurse to intersected types
addToUnionOrIntersection(findingUpperBounds, list, inferTypeArg(tp, ct.substitute(paramTypeArgs, paramVariances), argType, covariant, contravariant, findingUpperBounds, visited, argNode));
}
return unionOrIntersectionOrNull(findingUpperBounds, list);
} else if (argType.isUnion()) {
List<Type> cts = argType.getCaseTypes();
List<Type> list = new ArrayList<Type>(cts.size());
for (Type ct : cts) {
// recurse to union types
addToUnion(list, inferTypeArg(tp, paramType, ct.substitute(paramTypeArgs, paramVariances), covariant, contravariant, findingUpperBounds, visited, argNode));
}
return unionOrNull(list);
} else if (argType.isIntersection()) {
List<Type> sts = argType.getSatisfiedTypes();
List<Type> list = new ArrayList<Type>(sts.size());
for (Type st : sts) {
// recurse to intersected types
addToIntersection(list, inferTypeArg(tp, paramType, st.substitute(paramTypeArgs, paramVariances), covariant, contravariant, findingUpperBounds, visited, argNode), unit);
}
return intersectionOrNull(list);
} else {
Type supertype = argType.getSupertype(paramTypeDec);
if (supertype != null) {
List<Type> list = new ArrayList<Type>(2);
Type pqt = paramType.getQualifyingType();
Type sqt = supertype.getQualifyingType();
if (pqt != null && sqt != null) {
// recurse to qualifying types
addToUnionOrIntersection(findingUpperBounds, list, inferTypeArg(tp, pqt, sqt, covariant, contravariant, findingUpperBounds, visited, argNode));
}
inferTypeArg(tp, paramType, supertype, covariant, contravariant, findingUpperBounds, list, visited, argNode);
return unionOrIntersectionOrNull(findingUpperBounds, list);
} else {
return null;
}
}
} else {
return null;
}
}
use of org.eclipse.ceylon.model.typechecker.model.SiteVariance in project ceylon by eclipse.
the class TypeParserTests method testParamsVariance3.
@Test
public void testParamsVariance3() {
Type type = new TypeParser(MockLoader.instance).decodeType("t2<in b,c>", null, mockDefaultModule, mockPkgUnit);
assertTypeWithParameters(type);
Map<TypeParameter, SiteVariance> varianceOverrides = type.getVarianceOverrides();
Assert.assertNotNull(varianceOverrides);
Assert.assertEquals(1, varianceOverrides.size());
List<TypeParameter> tps = type.getDeclaration().getTypeParameters();
Assert.assertEquals(SiteVariance.IN, varianceOverrides.get(tps.get(0)));
Assert.assertEquals(null, varianceOverrides.get(tps.get(1)));
}
use of org.eclipse.ceylon.model.typechecker.model.SiteVariance in project ceylon by eclipse.
the class TypeParserTests method testParamsVariance1.
@Test
public void testParamsVariance1() {
Type type = new TypeParser(MockLoader.instance).decodeType("t2<in b,out c>", null, mockDefaultModule, mockPkgUnit);
assertTypeWithParameters(type);
Map<TypeParameter, SiteVariance> varianceOverrides = type.getVarianceOverrides();
Assert.assertNotNull(varianceOverrides);
Assert.assertEquals(2, varianceOverrides.size());
List<TypeParameter> tps = type.getDeclaration().getTypeParameters();
Assert.assertEquals(SiteVariance.IN, varianceOverrides.get(tps.get(0)));
Assert.assertEquals(SiteVariance.OUT, varianceOverrides.get(tps.get(1)));
}
Aggregations