use of org.eclipse.ceylon.model.typechecker.model.TypeParameter in project ceylon by eclipse.
the class InheritanceVisitor method checkCaseType.
void checkCaseType(TypeDeclaration type, Tree.StaticType ct, TypeDeclaration caseTypeDec) {
if (caseTypeDec instanceof ClassOrInterface && ct instanceof Tree.SimpleType && caseTypeDec.isParameterized()) {
Tree.SimpleType t = (Tree.SimpleType) ct;
Tree.TypeArgumentList tal = t.getTypeArgumentList();
List<Tree.Type> args = tal == null ? Collections.<Tree.Type>emptyList() : tal.getTypes();
List<TypeParameter> typeParameters = caseTypeDec.getTypeParameters();
Set<TypeParameter> used = new HashSet<TypeParameter>();
for (int i = 0; i < typeParameters.size(); i++) {
TypeParameter typeParameter = typeParameters.get(i);
Type argType;
Node node;
String typeArg;
if (i < args.size()) {
Tree.Type arg = args.get(i);
argType = arg.getTypeModel();
node = arg;
typeArg = "type argument";
} else {
argType = typeParameter.getDefaultTypeArgument();
node = tal;
typeArg = "default type argument '" + argType.asString(node.getUnit()) + "' of '" + typeParameter.getName() + "' ";
}
if (argType != null) {
TypeDeclaration argTypeDec = argType.getDeclaration();
if (argType.isTypeParameter()) {
TypeParameter tp = (TypeParameter) argTypeDec;
if (!tp.getDeclaration().equals(type)) {
node.addError(typeArg + "is not a type parameter of the enumerated type: '" + tp.getName() + "' is not a type parameter of '" + type.getName() + "'");
} else if (!used.add(tp)) {
node.addError("type parameter of the enumerated type is used twice as a type argument: '" + argTypeDec.getName());
}
} else if (typeParameter.isCovariant()) {
checkAssignable(typeParameter.getType(), argType, node, typeArg + " is not an upper bound of the type parameter '" + typeParameter.getName() + "' ");
} else if (typeParameter.isContravariant()) {
checkAssignable(argType, typeParameter.getType(), node, typeArg + " is not a lower bound of the type parameter '" + typeParameter.getName() + "' ");
} else {
node.addError(typeArg + "is not a type parameter of the enumerated type: '" + argTypeDec.getName() + "'");
}
}
}
}
}
use of org.eclipse.ceylon.model.typechecker.model.TypeParameter in project ceylon by eclipse.
the class InheritanceVisitor method checkSelfTypes.
private void checkSelfTypes(Tree.StaticType that, TypeDeclaration td, Type type) {
if (!(td instanceof TypeParameter)) {
// TODO: is this really ok?!
List<TypeParameter> params = type.getDeclaration().getTypeParameters();
List<Type> args = type.getTypeArgumentList();
Unit unit = that.getUnit();
for (int i = 0; i < params.size(); i++) {
TypeParameter param = params.get(i);
if (param.isSelfType() && !args.isEmpty()) {
Type arg = args.get(i);
if (arg == null) {
arg = unit.getUnknownType();
}
TypeDeclaration std = param.getSelfTypedDeclaration();
Type at;
TypeDeclaration mtd;
if (param.getContainer().equals(std)) {
at = td.getType();
mtd = td;
} else {
// TODO: lots wrong here?
mtd = (TypeDeclaration) td.getMember(std.getName(), null, false);
at = mtd == null ? null : mtd.getType();
}
if (at != null && !at.isSubtypeOf(arg)) {
Type st = mtd.getSelfType();
if (st == null || !st.isExactly(arg)) {
String help = "";
TypeDeclaration atd = at.getDeclaration();
TypeDeclaration ad = arg.getDeclaration();
if (ad instanceof TypeParameter) {
TypeParameter tp = (TypeParameter) ad;
if (tp.getDeclaration().equals(td)) {
help = " (try making '" + ad.getName() + "' a self type of '" + td.getName() + "')";
}
} else if (ad instanceof Interface) {
help = " (try making " + message(atd) + " satisfy '" + ad.getName() + "')";
} else if (ad instanceof Class && td instanceof Class) {
help = " (try making " + message(atd) + " extend '" + ad.getName() + "')";
}
that.addError("type argument does not satisfy self type constraint on type parameter '" + param.getName() + "' of '" + type.getDeclaration().getName(unit) + "': '" + arg.asString(unit) + "' is not a supertype or self type of " + message(atd) + help);
}
}
}
}
}
}
use of org.eclipse.ceylon.model.typechecker.model.TypeParameter in project ceylon by eclipse.
the class TypeArgumentInference method inferTypeArgFromNamedArg.
private void inferTypeArgFromNamedArg(Tree.NamedArgument arg, TypeParameter tp, Type receiverType, ParameterList parameters, boolean findingUpperBounds, List<Type> inferredTypes, Declaration invoked, Set<Parameter> foundParameters) {
Type type = null;
if (arg instanceof Tree.SpecifiedArgument) {
Tree.SpecifiedArgument sa = (Tree.SpecifiedArgument) arg;
Tree.SpecifierExpression se = sa.getSpecifierExpression();
Tree.Expression e = se.getExpression();
if (e != null) {
type = e.getTypeModel();
}
} else if (arg instanceof Tree.TypedArgument) {
// copy/pasted from checkNamedArgument()
Tree.TypedArgument ta = (Tree.TypedArgument) arg;
type = ta.getDeclarationModel().getTypedReference().getFullType();
}
if (type != null) {
Parameter parameter = getMatchingParameter(parameters, arg, foundParameters);
if (parameter != null) {
foundParameters.add(parameter);
Type paramType = parameterType(receiverType, parameter, invoked);
addToUnionOrIntersection(findingUpperBounds, inferredTypes, inferTypeArg(tp, paramType, type, findingUpperBounds, arg));
}
}
}
use of org.eclipse.ceylon.model.typechecker.model.TypeParameter 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.TypeParameter in project ceylon by eclipse.
the class TypeArgumentInference method getInferredTypeArgsForFunctionRef.
/**
* Infer the type arguments for a reference to a
* generic function that occurs as an argument in
* an invocation.
* @param receiverType
*/
List<Type> getInferredTypeArgsForFunctionRef(Tree.StaticMemberOrTypeExpression smte, Type receiverType, boolean secondList) {
Tree.TypeArguments typeArguments = smte.getTypeArguments();
if (typeArguments instanceof Tree.InferredTypeArguments) {
// the model object for the function ref
Declaration reference = smte.getDeclaration();
List<TypeParameter> typeParameters = getTypeParametersAccountingForTypeConstructor(reference);
if (typeParameters == null || typeParameters.isEmpty()) {
// nothing to infer
return NO_TYPE_ARGS;
} else {
// set earlier in inferParameterTypes()
TypedReference paramTypedRef = smte.getTargetParameter();
Type paramType = smte.getParameterType();
// the function ref is being passed
if (paramType == null && paramTypedRef != null) {
paramType = paramTypedRef.getFullType();
}
if (paramType == null) {
return null;
}
if (isArgumentToGenericParameter(paramTypedRef, paramType)) {
return null;
}
Declaration paramDec;
Declaration parameterizedDec;
if (paramTypedRef != null) {
paramDec = paramTypedRef.getDeclaration();
parameterizedDec = (Declaration) paramDec.getContainer();
} else {
paramDec = null;
parameterizedDec = null;
}
// better algorithm
if (!smte.getStaticMethodReferencePrimary() && reference instanceof Functional && paramDec instanceof Functional && paramTypedRef != null) {
// callable parameter
return inferFunctionRefTypeArgs(smte, receiverType, secondList, reference, typeParameters, paramTypedRef, paramDec, parameterizedDec);
} else {
// improve this, including in the spec)
if (unit.isSequentialType(paramType)) {
paramType = unit.getSequentialElementType(paramType);
}
if (unit.isCallableType(paramType)) {
// the parameter has type Callable
return inferFunctionRefTypeArgs(smte, receiverType, secondList, reference, typeParameters, paramType, parameterizedDec);
} else if (secondList) {
// arguments (NOT BLESSED BY SPEC!)
return inferNullaryFunctionCallTypeArgs(smte, receiverType, reference, typeParameters, paramType, parameterizedDec);
} else {
return null;
}
}
}
} else {
// not inferring
return null;
}
}
Aggregations