Search in sources :

Example 81 with TypeParameter

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() + "'");
                }
            }
        }
    }
}
Also used : ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) Node(org.eclipse.ceylon.compiler.typechecker.tree.Node) AnalyzerUtil.checkCasesDisjoint(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.checkCasesDisjoint) Type(org.eclipse.ceylon.model.typechecker.model.Type) Tree(org.eclipse.ceylon.compiler.typechecker.tree.Tree) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration) HashSet(java.util.HashSet)

Example 82 with TypeParameter

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);
                    }
                }
            }
        }
    }
}
Also used : TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) Type(org.eclipse.ceylon.model.typechecker.model.Type) Class(org.eclipse.ceylon.model.typechecker.model.Class) Unit(org.eclipse.ceylon.model.typechecker.model.Unit) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration) AnalyzerUtil.checkCasesDisjoint(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.checkCasesDisjoint) Interface(org.eclipse.ceylon.model.typechecker.model.Interface) ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface)

Example 83 with TypeParameter

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));
        }
    }
}
Also used : Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.intersectionType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) AnalyzerUtil.spreadType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.spreadType) AnalyzerUtil.getTupleType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getTupleType) Tree(org.eclipse.ceylon.compiler.typechecker.tree.Tree) AnalyzerUtil.getUnspecifiedParameter(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getUnspecifiedParameter) Parameter(org.eclipse.ceylon.model.typechecker.model.Parameter) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) AnalyzerUtil.getMatchingParameter(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getMatchingParameter)

Example 84 with TypeParameter

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;
    }
}
Also used : TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.intersectionType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) AnalyzerUtil.spreadType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.spreadType) AnalyzerUtil.getTupleType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getTupleType) SiteVariance(org.eclipse.ceylon.model.typechecker.model.SiteVariance) ArrayList(java.util.ArrayList) ParameterList(org.eclipse.ceylon.model.typechecker.model.ParameterList) ArrayList(java.util.ArrayList) ModelUtil.typeParametersAsArgList(org.eclipse.ceylon.model.typechecker.model.ModelUtil.typeParametersAsArgList) List(java.util.List) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 85 with TypeParameter

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;
    }
}
Also used : Functional(org.eclipse.ceylon.model.typechecker.model.Functional) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.intersectionType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) AnalyzerUtil.spreadType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.spreadType) AnalyzerUtil.getTupleType(org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getTupleType) TypedReference(org.eclipse.ceylon.model.typechecker.model.TypedReference) Tree(org.eclipse.ceylon.compiler.typechecker.tree.Tree) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration) Declaration(org.eclipse.ceylon.model.typechecker.model.Declaration)

Aggregations

TypeParameter (org.eclipse.ceylon.model.typechecker.model.TypeParameter)181 Type (org.eclipse.ceylon.model.typechecker.model.Type)138 TypeDeclaration (org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)82 ArrayList (java.util.ArrayList)57 ModelUtil.appliedType (org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType)57 Tree (org.eclipse.ceylon.compiler.typechecker.tree.Tree)54 UnknownType (org.eclipse.ceylon.model.typechecker.model.UnknownType)46 Declaration (org.eclipse.ceylon.model.typechecker.model.Declaration)45 ModelUtil.intersectionType (org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType)35 TypedDeclaration (org.eclipse.ceylon.model.typechecker.model.TypedDeclaration)32 ClassOrInterface (org.eclipse.ceylon.model.typechecker.model.ClassOrInterface)30 Function (org.eclipse.ceylon.model.typechecker.model.Function)29 AnalyzerUtil.getTupleType (org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getTupleType)28 AnalyzerUtil.spreadType (org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.spreadType)28 Parameter (org.eclipse.ceylon.model.typechecker.model.Parameter)28 IntersectionType (org.eclipse.ceylon.model.typechecker.model.IntersectionType)27 JCTypeParameter (org.eclipse.ceylon.langtools.tools.javac.tree.JCTree.JCTypeParameter)26 Class (org.eclipse.ceylon.model.typechecker.model.Class)26 UnionType (org.eclipse.ceylon.model.typechecker.model.UnionType)25 HashMap (java.util.HashMap)24