Search in sources :

Example 6 with SiteVariance

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("}");
}
Also used : TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) Type(org.eclipse.ceylon.model.typechecker.model.Type) UnknownType(org.eclipse.ceylon.model.typechecker.model.UnknownType) NothingType(org.eclipse.ceylon.model.typechecker.model.NothingType) SiteVariance(org.eclipse.ceylon.model.typechecker.model.SiteVariance) HashMap(java.util.HashMap) Map(java.util.Map) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 7 with SiteVariance

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;
}
Also used : TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) IntersectionType(org.eclipse.ceylon.model.typechecker.model.IntersectionType) ModelUtil.intersectionType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType) LazyType(org.eclipse.ceylon.model.typechecker.model.LazyType) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.erasedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.erasedType) SiteVariance(org.eclipse.ceylon.model.typechecker.model.SiteVariance) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Unit(org.eclipse.ceylon.model.typechecker.model.Unit) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 8 with SiteVariance

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;
    }
}
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 9 with SiteVariance

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)));
}
Also used : Type(org.eclipse.ceylon.model.typechecker.model.Type) IntersectionType(org.eclipse.ceylon.model.typechecker.model.IntersectionType) UnionType(org.eclipse.ceylon.model.typechecker.model.UnionType) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) TypeParser(org.eclipse.ceylon.model.loader.TypeParser) SiteVariance(org.eclipse.ceylon.model.typechecker.model.SiteVariance) Test(org.junit.Test)

Example 10 with SiteVariance

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)));
}
Also used : Type(org.eclipse.ceylon.model.typechecker.model.Type) IntersectionType(org.eclipse.ceylon.model.typechecker.model.IntersectionType) UnionType(org.eclipse.ceylon.model.typechecker.model.UnionType) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) TypeParser(org.eclipse.ceylon.model.loader.TypeParser) SiteVariance(org.eclipse.ceylon.model.typechecker.model.SiteVariance) Test(org.junit.Test)

Aggregations

SiteVariance (org.eclipse.ceylon.model.typechecker.model.SiteVariance)20 Type (org.eclipse.ceylon.model.typechecker.model.Type)18 TypeParameter (org.eclipse.ceylon.model.typechecker.model.TypeParameter)18 TypeDeclaration (org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)11 IntersectionType (org.eclipse.ceylon.model.typechecker.model.IntersectionType)8 UnknownType (org.eclipse.ceylon.model.typechecker.model.UnknownType)8 ArrayList (java.util.ArrayList)7 HashMap (java.util.HashMap)7 UnionType (org.eclipse.ceylon.model.typechecker.model.UnionType)7 NothingType (org.eclipse.ceylon.model.typechecker.model.NothingType)6 ModelUtil.appliedType (org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType)5 ModelUtil.intersectionType (org.eclipse.ceylon.model.typechecker.model.ModelUtil.intersectionType)5 Map (java.util.Map)4 Tree (org.eclipse.ceylon.compiler.typechecker.tree.Tree)4 Declaration (org.eclipse.ceylon.model.typechecker.model.Declaration)4 LinkedHashMap (java.util.LinkedHashMap)3 AnalyzerUtil.getPackageTypeDeclaration (org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getPackageTypeDeclaration)3 AnalyzerUtil.getTypeDeclaration (org.eclipse.ceylon.compiler.typechecker.analyzer.AnalyzerUtil.getTypeDeclaration)3 TypeParser (org.eclipse.ceylon.model.loader.TypeParser)3 LazyType (org.eclipse.ceylon.model.typechecker.model.LazyType)3