Search in sources :

Example 86 with ClassOrInterface

use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.

the class AbstractTransformer method makeTypeTest.

private <R> R makeTypeTest(TypeTestTransformation<R> typeTester, JCExpression firstTimeExpr, Naming.CName varName, Type testedType, Type expressionType) {
    R result = null;
    // make sure aliases are resolved
    testedType = testedType.resolveAliases();
    // optimisation when all we're doing is making sure it is not null
    if (expressionType != null && testedType.getSupertype(typeFact().getObjectDeclaration()) != null && expressionType.isExactly(typeFact().getOptionalType(testedType))) {
        JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
        return typeTester.nullTest(varExpr, JCTree.Tag.NE);
    }
    TypeDeclaration declaration = testedType.getDeclaration();
    if (declaration instanceof ClassOrInterface) {
        JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
        if (isAnything(testedType)) {
            // everything is Void, it's the root of the hierarchy
            return typeTester.eval(varExpr, true);
        } else if (isNull(testedType)) {
            // is Null => is null
            return typeTester.nullTest(varExpr, JCTree.Tag.EQ);
        } else if (testedType.isExactly(typeFact().getObjectType())) {
            // is Object => is not null
            return typeTester.nullTest(varExpr, JCTree.Tag.NE);
        } else if (testedType.isExactly(typeFact().getIdentifiableType())) {
            // it's erased
            return typeTester.isIdentifiable(varExpr);
        } else if (testedType.getDeclaration().equals(typeFact().getTrueValueDeclaration().getTypeDeclaration())) {
            return typeTester.isTrue(varExpr);
        } else if (testedType.getDeclaration().equals(typeFact().getFalseValueDeclaration().getTypeDeclaration())) {
            return typeTester.isFalse(varExpr);
        } else if (testedType.isExactly(typeFact().getBasicType())) {
            // it's erased
            return typeTester.isBasic(varExpr);
        } else if (testedType.getDeclaration().getQualifiedNameString().equals("java.lang::Error")) {
            // need to exclude AssertionError
            return typeTester.andOr(typeTester.isInstanceof(varExpr, testedType, expressionType), typeTester.not(typeTester.isInstanceof(varName.makeIdent(), typeFact().getAssertionErrorDeclaration().getType(), expressionType)), JCTree.Tag.AND);
        } else if (!hasTypeArguments(testedType)) {
            // non-generic Class or interface, use instanceof
            return typeTester.isInstanceof(varExpr, testedType, expressionType);
        } else {
            // generic class or interface...
            if (declaration.getSelfType() != null && // of TypeArg
            declaration.getSelfType().getDeclaration() instanceof TypeParameter && // given TypeArg satisfies SelfType<TypeArg>
            declaration.getSelfType().isSubtypeOf(declaration.getType())) {
                Type selfTypeArg = testedType.getTypeArguments().get(declaration.getSelfType().getDeclaration());
                if (selfTypeArg.getDeclaration() instanceof ClassOrInterface) {
                    // first check if the type is inhabited or not
                    if (selfTypeArg.getDeclaration().inherits(declaration)) {
                        // "is SelfType<ClassOrInterface>" can be written "is ClassOrInterface"
                        return makeTypeTest(typeTester, firstTimeExpr, varName, selfTypeArg, expressionType);
                    } else {
                        // always false, for example Comparable<Anything> is uninhabited because Anything does not inherit from Comparable
                        return typeTester.eval(varExpr, false);
                    }
                }
            // if not, keep trying
            }
            if (canOptimiseReifiedTypeTest(testedType)) {
                // Use an instanceof
                return typeTester.isInstanceof(varExpr, testedType, expressionType);
            } else {
                // Have to use a reified test
                if (!Decl.equal(declaration, expressionType.getDeclaration()) && canUseFastFailTypeTest(testedType)) {
                    // instanceof shortcircuit doesn't achieve anything
                    return typeTester.andOr(typeTester.isInstanceof(varExpr, testedType, expressionType), typeTester.isReified(varName.makeIdent(), testedType), JCTree.Tag.AND);
                } else {
                    return typeTester.isReified(varExpr, testedType);
                }
            }
        }
    } else if (typeFact().isUnion(testedType)) {
        for (Type pt : testedType.getCaseTypes()) {
            R partExpr = makeTypeTest(typeTester, firstTimeExpr, varName, pt, expressionType);
            firstTimeExpr = null;
            if (result == null) {
                result = partExpr;
            } else {
                result = typeTester.andOr(result, partExpr, JCTree.Tag.OR);
            }
        }
        return result;
    } else if (typeFact().isIntersection(testedType)) {
        for (Type pt : testedType.getSatisfiedTypes()) {
            R partExpr = makeTypeTest(typeTester, firstTimeExpr, varName, pt, expressionType);
            firstTimeExpr = null;
            if (result == null) {
                result = partExpr;
            } else {
                result = typeTester.andOr(result, partExpr, JCTree.Tag.AND);
            }
        }
        return result;
    } else if (testedType.isNothing()) {
        // nothing is Bottom
        JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
        return typeTester.eval(varExpr, false);
    } else if (declaration instanceof TypeParameter) {
        JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
        if (!reifiableUpperBounds((TypeParameter) declaration, expressionType).isEmpty()) {
            // If we're testing against a type parameter with
            // class or interface upper bounds we can again shortcircuit the
            // Util.isReified() using instanceof against the bounds
            result = typeTester.isReified(varName.makeIdent(), testedType);
            Iterator<Type> iterator = reifiableUpperBounds((TypeParameter) declaration, expressionType).iterator();
            while (iterator.hasNext()) {
                Type type = iterator.next();
                ClassOrInterface c = ((ClassOrInterface) type.resolveAliases().getDeclaration());
                result = typeTester.andOr(typeTester.isInstanceof(iterator.hasNext() ? varName.makeIdent() : varExpr, c.getType(), expressionType), result, JCTree.Tag.AND);
            }
            return result;
        } else {
            return typeTester.isReified(varExpr, testedType);
        }
    } else {
        throw BugException.unhandledDeclarationCase(declaration);
    }
}
Also used : ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) TypeParameter(org.eclipse.ceylon.model.typechecker.model.TypeParameter) JCTypeParameter(org.eclipse.ceylon.langtools.tools.javac.tree.JCTree.JCTypeParameter) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) JCExpression(org.eclipse.ceylon.langtools.tools.javac.tree.JCTree.JCExpression) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 87 with ClassOrInterface

use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.

the class AbstractTransformer method substituteTypeArgumentsForTypeParameterBound.

protected Type substituteTypeArgumentsForTypeParameterBound(Reference target, Type bound) {
    Declaration declaration = target.getDeclaration();
    if (declaration.getContainer() instanceof ClassOrInterface) {
        Type targetType = target.getQualifyingType();
        // static methods have a container but do not capture type parameters
        if (targetType != null && !declaration.isStatic()) {
            ClassOrInterface methodContainer = (ClassOrInterface) declaration.getContainer();
            Type supertype = targetType.getSupertype(methodContainer);
            // we need type arguments that may come from the method container
            bound = bound.substitute(supertype);
        }
    }
    // and those that may come from the method call itself
    return bound.substitute(target.getTypeArguments(), null);
}
Also used : ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) Declaration(org.eclipse.ceylon.model.typechecker.model.Declaration) TypedDeclaration(org.eclipse.ceylon.model.typechecker.model.TypedDeclaration) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 88 with ClassOrInterface

use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.

the class AbstractTransformer method getSimpleNumParametersOfCallable.

private int getSimpleNumParametersOfCallable(Type args) {
    // can be a defaulted tuple of Empty|Tuple
    if (args.isUnion()) {
        java.util.List<Type> caseTypes = args.getCaseTypes();
        if (caseTypes == null || caseTypes.size() != 2)
            return -1;
        Type caseA = caseTypes.get(0);
        TypeDeclaration caseADecl = caseA.getDeclaration();
        Type caseB = caseTypes.get(1);
        TypeDeclaration caseBDecl = caseB.getDeclaration();
        if (caseADecl instanceof ClassOrInterface == false || caseBDecl instanceof ClassOrInterface == false)
            return -1;
        if (caseADecl.isEmpty() && caseBDecl.isTuple())
            return getSimpleNumParametersOfCallable(caseB);
        if (caseBDecl.isEmpty() && caseADecl.isTuple())
            return getSimpleNumParametersOfCallable(caseA);
        return -1;
    }
    // can be Tuple, Empty, Sequence or Sequential
    if (!args.isClassOrInterface())
        return -1;
    TypeDeclaration declaration = args.getDeclaration();
    if (declaration.isTuple()) {
        Type rest = args.getTypeArgumentList().get(2);
        int ret = getSimpleNumParametersOfCallable(rest);
        if (ret == -1)
            return -1;
        return ret + 1;
    }
    if (declaration.isEmpty()) {
        return 0;
    }
    if (declaration.isSequential() || declaration.isSequence()) {
        return 1;
    }
    return -1;
}
Also used : ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Example 89 with ClassOrInterface

use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.

the class AbstractTransformer method makeTupleTypeDescriptor.

private JCExpression makeTupleTypeDescriptor(Type pt, boolean firstElementOptional) {
    java.util.List<Type> tupleElementTypes = typeFact().getTupleElementTypes(pt);
    boolean isVariadic = typeFact().isTupleLengthUnbounded(pt);
    boolean atLeastOne = false;
    boolean needsRestSplit = false;
    Type restType = null;
    if (isVariadic) {
        // unwrap the last element
        restType = tupleElementTypes.get(tupleElementTypes.size() - 1);
        // to optimise
        if (restType.isUnknown())
            return null;
        tupleElementTypes.set(tupleElementTypes.size() - 1, typeFact.getSequentialElementType(restType));
        atLeastOne = restType.getDeclaration().inherits(typeFact().getSequenceDeclaration());
        // the last rest element may be a type param, in which case we resolve it at runtime
        needsRestSplit = restType.getDeclaration() instanceof ClassOrInterface == false || (!restType.getDeclaration().equals(typeFact.getSequenceDeclaration()) && !restType.getDeclaration().equals(typeFact.getSequentialDeclaration()));
    }
    int firstDefaulted;
    if (!firstElementOptional) {
        // only do this crazy computation if the first element is not optional (case of []|[A] which is a union type really)
        int minimumLength = typeFact().getTupleMinimumLength(pt);
        // [B+] -> 1
        if (atLeastOne)
            minimumLength--;
        // [A,B=] -> 1
        // [A=,B*] -> 0
        // [A,B+] -> 1
        // [B*] -> 0
        // [B+] -> 0
        // [A,B=] -> 2
        // [A=,B*] -> 1
        // [A,B+] -> 1
        // [B*] -> 0
        // [B+] -> 0
        int nonVariadicParams = tupleElementTypes.size();
        if (isVariadic)
            nonVariadicParams--;
        // [A,B=] -> 2!=1 -> 1
        // [A=,B*] -> 1!=0 -> 0
        // [A,B+] -> 1==1 -> -1
        // [B*] -> 0==0 -> -1
        // [B+] -> 0==0 -> -1
        firstDefaulted = nonVariadicParams != minimumLength ? minimumLength : -1;
    } else {
        firstDefaulted = 0;
    }
    JCExpression restTypeDescriptor = null;
    JCExpression restElementTypeDescriptor = null;
    if (needsRestSplit) {
        Type restElementType = tupleElementTypes.get(tupleElementTypes.size() - 1);
        tupleElementTypes.remove(tupleElementTypes.size() - 1);
        restTypeDescriptor = makeReifiedTypeArgumentResolved(restType, false);
        restElementTypeDescriptor = makeReifiedTypeArgumentResolved(restElementType, false);
    }
    ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
    if (needsRestSplit) {
        args.append(restTypeDescriptor);
        args.append(restElementTypeDescriptor);
    } else {
        args.append(makeBoolean(isVariadic));
        args.append(makeBoolean(atLeastOne));
    }
    args.append(makeInteger(firstDefaulted));
    for (Type element : tupleElementTypes) {
        args.append(makeReifiedTypeArgumentResolved(element, false));
    }
    JCExpression tupleDescriptor = make().Apply(null, makeSelect(makeTypeDescriptorType(), needsRestSplit ? "tupleWithRest" : "tuple"), args.toList());
    return tupleDescriptor;
}
Also used : ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) JCExpression(org.eclipse.ceylon.langtools.tools.javac.tree.JCTree.JCExpression) ListBuffer(org.eclipse.ceylon.langtools.tools.javac.util.ListBuffer)

Example 90 with ClassOrInterface

use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.

the class AbstractTransformer method getRefinedDeclaration.

/*
     * We have several special cases here to find the best non-widening refinement in case of multiple inheritace:
     * 
     * - The first special case is for some decls like None.first, which inherits from ContainerWithFirstElement
     * twice: once with Nothing (erased to j.l.Object) and once with Element (a type param). Now, in order to not widen the
     * return type it can't be Nothing (j.l.Object), it must be Element (a type param that is not instantiated), because in Java
     * a type param refines j.l.Object but not the other way around.
     * - The second special case is when implementing an interface first with a non-erased type, then with an erased type. In this
     * case we want the refined decl to be the one with the non-erased type.
     * - The third special case is when we implement a declaration via multiple super types, without having any refining
     * declarations in those supertypes, simply by instantiating a common super type with different type parameters
     */
TypedReference getRefinedDeclaration(TypedReference typedReference, Type currentType) {
    TypedDeclaration decl = typedReference.getDeclaration();
    TypedDeclaration modelRefinedDecl = (TypedDeclaration) decl.getRefinedDeclaration();
    Type referenceQualifyingType = typedReference.getQualifyingType();
    boolean forMixinMethod = currentType != null && decl.getContainer() instanceof ClassOrInterface && referenceQualifyingType != null && !Decl.equal(referenceQualifyingType.getDeclaration(), currentType.getDeclaration());
    // quick exit
    if (Decl.equal(decl, modelRefinedDecl) && !forMixinMethod)
        return null;
    // modelRefinedDecl exists, but perhaps it's the toplevel refinement and not the one Java will look at
    if (!forMixinMethod)
        modelRefinedDecl = getFirstRefinedDeclaration(decl);
    TypeDeclaration qualifyingDeclaration = currentType.getDeclaration();
    if (qualifyingDeclaration instanceof ClassOrInterface) {
        // if both are returning unboxed void we're good
        if (Decl.isUnboxedVoid(decl) && Decl.isUnboxedVoid(modelRefinedDecl))
            return null;
        // only try to find better if we're erasing to Object and we're not returning a type param
        if (willEraseToObject(typedReference.getType()) || isWideningTypeArguments(decl.getType(), modelRefinedDecl.getType(), true) && !isTypeParameter(typedReference.getType())) {
            ClassOrInterface declaringType = (ClassOrInterface) qualifyingDeclaration;
            Set<TypedDeclaration> refinedMembers = getRefinedMembers(declaringType, decl.getName(), org.eclipse.ceylon.model.typechecker.model.ModelUtil.getSignature(decl), org.eclipse.ceylon.model.typechecker.model.ModelUtil.isVariadic(decl));
            // now we must select a different refined declaration if we refine it more than once
            if (refinedMembers.size() > (forMixinMethod ? 0 : 1)) {
                // first case
                for (TypedDeclaration refinedDecl : refinedMembers) {
                    // get the type reference to see if any eventual type param is instantiated in our inheritance of this type/method
                    TypedReference refinedTypedReference = getRefinedTypedReference(typedReference, refinedDecl);
                    // if it is not instantiated, that's the one we're looking for
                    if (isTypeParameter(refinedTypedReference.getType()))
                        return refinedTypedReference;
                }
                // second case
                for (TypedDeclaration refinedDecl : refinedMembers) {
                    // get the type reference to see if any eventual type param is instantiated in our inheritance of this type/method
                    TypedReference refinedTypedReference = getRefinedTypedReference(typedReference, refinedDecl);
                    // if we're not erasing this one to Object let's select it
                    if (!willEraseToObject(refinedTypedReference.getType()) && !isWideningTypeArguments(refinedDecl.getType(), modelRefinedDecl.getType(), true))
                        return refinedTypedReference;
                }
                // third case
                if (isTypeParameter(modelRefinedDecl.getType())) {
                    // it can happen that we have inherited a method twice from a single refined declaration
                    // via different supertype instantiations, without having ever refined them in supertypes
                    // so we try each super type to see if we already have a matching typed reference
                    // first super type
                    Type extendedType = declaringType.getExtendedType();
                    if (extendedType != null) {
                        TypedReference refinedTypedReference = getRefinedTypedReference(extendedType, modelRefinedDecl);
                        Type refinedType = refinedTypedReference.getType();
                        if (!isTypeParameter(refinedType) && !willEraseToObject(refinedType))
                            return refinedTypedReference;
                    }
                    // then satisfied interfaces
                    for (Type satisfiedType : declaringType.getSatisfiedTypes()) {
                        TypedReference refinedTypedReference = getRefinedTypedReference(satisfiedType, modelRefinedDecl);
                        Type refinedType = refinedTypedReference.getType();
                        if (!isTypeParameter(refinedType) && !willEraseToObject(refinedType))
                            return refinedTypedReference;
                    }
                }
            }
        }
        /*
             * Now there's another crazy case:
             * 
             * interface Top<out Element> {
             *  Top<Element> ret => nothing;
             * }
             * interface Left satisfies Top<Integer> {}
             * interface Right satisfies Top<String> {}
             * class Bottom() satisfies Left&Right {}
             * 
             * Where Bottom.ret does not exist and is typed as returning Integer&String which is Nothing, erased to Object,
             * and we look at what it refines and find only a single definition Top.ret typed as returning Integer&String (Nothing),
             * so we think there's no widening, but Java will only see Top<Integer>.ret from Left, and that's the one we want
             * to use for determining widening.
             * See https://github.com/ceylon/ceylon-compiler/issues/1765
             */
        Type firstInstantiation = isInheritedWithDifferentTypeArguments(modelRefinedDecl.getContainer(), currentType);
        if (firstInstantiation != null) {
            TypedReference firstInstantiationTypedReference = getRefinedTypedReference(firstInstantiation, modelRefinedDecl);
            Type firstInstantiationType = firstInstantiationTypedReference.getType();
            if (isWidening(decl.getType(), firstInstantiationType) || isWideningTypeArguments(decl.getType(), firstInstantiationType, true))
                return firstInstantiationTypedReference;
        }
    }
    return getRefinedTypedReference(typedReference, modelRefinedDecl);
}
Also used : TypedDeclaration(org.eclipse.ceylon.model.typechecker.model.TypedDeclaration) ClassOrInterface(org.eclipse.ceylon.model.typechecker.model.ClassOrInterface) Type(org.eclipse.ceylon.model.typechecker.model.Type) ModelUtil.appliedType(org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType) TypedReference(org.eclipse.ceylon.model.typechecker.model.TypedReference) TypeDeclaration(org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)

Aggregations

ClassOrInterface (org.eclipse.ceylon.model.typechecker.model.ClassOrInterface)102 TypeDeclaration (org.eclipse.ceylon.model.typechecker.model.TypeDeclaration)62 Declaration (org.eclipse.ceylon.model.typechecker.model.Declaration)48 TypedDeclaration (org.eclipse.ceylon.model.typechecker.model.TypedDeclaration)46 Type (org.eclipse.ceylon.model.typechecker.model.Type)44 Tree (org.eclipse.ceylon.compiler.typechecker.tree.Tree)27 Class (org.eclipse.ceylon.model.typechecker.model.Class)24 Interface (org.eclipse.ceylon.model.typechecker.model.Interface)23 Scope (org.eclipse.ceylon.model.typechecker.model.Scope)23 TypeParameter (org.eclipse.ceylon.model.typechecker.model.TypeParameter)20 ModelUtil.getContainingClassOrInterface (org.eclipse.ceylon.model.typechecker.model.ModelUtil.getContainingClassOrInterface)19 Value (org.eclipse.ceylon.model.typechecker.model.Value)19 ModelUtil.appliedType (org.eclipse.ceylon.model.typechecker.model.ModelUtil.appliedType)18 ArrayList (java.util.ArrayList)17 FunctionOrValue (org.eclipse.ceylon.model.typechecker.model.FunctionOrValue)17 Constructor (org.eclipse.ceylon.model.typechecker.model.Constructor)16 Function (org.eclipse.ceylon.model.typechecker.model.Function)14 LazyInterface (org.eclipse.ceylon.model.loader.model.LazyInterface)13 JCExpression (org.eclipse.ceylon.langtools.tools.javac.tree.JCTree.JCExpression)12 UnknownType (org.eclipse.ceylon.model.typechecker.model.UnknownType)12