All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.jet.lang.resolve.calls.inference.TypeBoundsImpl Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.jet.lang.resolve.calls.inference;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstructor;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;

public class TypeBoundsImpl implements TypeBounds {
    private final TypeParameterDescriptor typeVariable;
    private final Variance varianceOfPosition;
    private final Set bounds = Sets.newLinkedHashSet();

    private Collection resultValues;

    public TypeBoundsImpl(
            @NotNull TypeParameterDescriptor typeVariable,
            @NotNull Variance varianceOfPosition
    ) {
        this.typeVariable = typeVariable;
        this.varianceOfPosition = varianceOfPosition;
    }

    @NotNull
    @Override
    public Variance getVarianceOfPosition() {
        return varianceOfPosition;
    }

    public void addBound(@NotNull BoundKind kind, @NotNull JetType type, @NotNull ConstraintPosition position) {
        resultValues = null;
        bounds.add(new Bound(type, kind, position));
    }

    @Override
    public boolean isEmpty() {
        return getValues().isEmpty();
    }

    @NotNull
    @Override
    public TypeParameterDescriptor getTypeVariable() {
        return typeVariable;
    }

    @Override
    @NotNull
    public Collection getBounds() {
        return bounds;
    }

    @NotNull
    private static Set filterBounds(
            @NotNull Collection bounds,
            @NotNull BoundKind kind
    ) {
        return filterBounds(bounds, kind, null);
    }

    @NotNull
    private static Set filterBounds(
            @NotNull Collection bounds,
            @NotNull BoundKind kind,
            @Nullable Collection errorValues
    ) {
        Set result = Sets.newLinkedHashSet();
        for (Bound bound : bounds) {
            if (bound.kind == kind) {
                if (!ErrorUtils.containsErrorType(bound.type)) {
                    result.add(bound.type);
                }
                else if (errorValues != null) {
                    errorValues.add(bound.type);
                }
            }
        }
        return result;
    }

    /*package*/ TypeBoundsImpl copy() {
        TypeBoundsImpl typeBounds = new TypeBoundsImpl(typeVariable, varianceOfPosition);
        typeBounds.bounds.addAll(bounds);
        typeBounds.resultValues = resultValues;
        return typeBounds;
    }

    @NotNull
    public TypeBoundsImpl filter(@NotNull final Condition condition) {
        TypeBoundsImpl result = new TypeBoundsImpl(typeVariable, varianceOfPosition);
        result.bounds.addAll(ContainerUtil.filter(bounds, new Condition() {
            @Override
            public boolean value(Bound bound) {
                return condition.value(bound.position);
            }
        }));
        return result;
    }

    @Nullable
    @Override
    public JetType getValue() {
        Collection values = getValues();
        if (values.size() == 1) {
            return values.iterator().next();
        }
        return null;
    }

    @NotNull
    @Override
    public Collection getValues() {
        if (resultValues == null) {
            resultValues = computeValues();
        }
        return resultValues;
    }

    @NotNull
    private Collection computeValues() {
        Set values = Sets.newLinkedHashSet();
        if (bounds.isEmpty()) {
            return Collections.emptyList();
        }
        boolean hasStrongBound = ContainerUtil.exists(bounds, new Condition() {
            @Override
            public boolean value(Bound bound) {
                return bound.position.isStrong();
            }
        });
        if (!hasStrongBound) {
            return Collections.emptyList();
        }

        Set exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
        if (exactBounds.size() == 1) {
            JetType exactBound = exactBounds.iterator().next();
            if (tryPossibleAnswer(exactBound)) {
                return Collections.singleton(exactBound);
            }
        }
        values.addAll(exactBounds);

        Collection numberLowerBounds = new LinkedHashSet();
        Collection generalLowerBounds = new LinkedHashSet();
        filterNumberTypes(filterBounds(bounds, LOWER_BOUND, values), numberLowerBounds, generalLowerBounds);

        JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds);
        if (tryPossibleAnswer(superTypeOfLowerBounds)) {
            return Collections.singleton(superTypeOfLowerBounds);
        }
        ContainerUtil.addIfNotNull(superTypeOfLowerBounds, values);

        //todo
        //fun  foo(t: T, consumer: Consumer): T
        //foo(1, c: Consumer) - infer Int, not Any here

        JetType superTypeOfNumberLowerBounds = TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds);
        if (tryPossibleAnswer(superTypeOfNumberLowerBounds)) {
            return Collections.singleton(superTypeOfNumberLowerBounds);
        }
        ContainerUtil.addIfNotNull(superTypeOfNumberLowerBounds, values);

        if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
            JetType superTypeOfAllLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(
                    Lists.newArrayList(superTypeOfLowerBounds, superTypeOfNumberLowerBounds));
            if (tryPossibleAnswer(superTypeOfAllLowerBounds)) {
                return Collections.singleton(superTypeOfAllLowerBounds);
            }
        }

        Set upperBounds = filterBounds(bounds, BoundKind.UPPER_BOUND, values);
        JetType intersectionOfUpperBounds = TypeUtils.intersect(JetTypeChecker.DEFAULT, upperBounds);
        if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) {
            if (tryPossibleAnswer(intersectionOfUpperBounds)) {
                return Collections.singleton(intersectionOfUpperBounds);
            }
        }

        values.addAll(filterBounds(bounds, BoundKind.UPPER_BOUND));

        return values;
    }

    private static void filterNumberTypes(
            @NotNull Collection types,
            @NotNull Collection numberTypes,
            @NotNull Collection otherTypes
    ) {
        for (JetType type : types) {
            if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
                numberTypes.add(type);
            }
            else {
                otherTypes.add(type);
            }
        }
    }

    private boolean tryPossibleAnswer(@Nullable JetType possibleAnswer) {
        if (possibleAnswer == null) return false;
        if (!possibleAnswer.getConstructor().isDenotable()) return false;

        for (Bound bound : bounds) {
            switch (bound.kind) {
                case LOWER_BOUND:
                    if (!JetTypeChecker.DEFAULT.isSubtypeOf(bound.type, possibleAnswer)) {
                        return false;
                    }
                    break;

                case UPPER_BOUND:
                    if (!JetTypeChecker.DEFAULT.isSubtypeOf(possibleAnswer, bound.type)) {
                        return false;
                    }
                    break;

                case EXACT_BOUND:
                    if (!JetTypeChecker.DEFAULT.equalTypes(bound.type, possibleAnswer)) {
                        return false;
                    }
                    break;
            }
        }
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy