001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.types;
018    
019    import kotlin.Unit;
020    import kotlin.jvm.functions.Function1;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024    import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
025    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026    import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
027    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
028    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl;
029    import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
030    import org.jetbrains.kotlin.resolve.scopes.KtScope;
031    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
032    
033    import java.util.*;
034    
035    import static org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImplKt.registerTypeVariables;
036    import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
037    
038    public class TypeIntersector {
039    
040        public static boolean isIntersectionEmpty(@NotNull KotlinType typeA, @NotNull KotlinType typeB) {
041            return intersectTypes(KotlinTypeChecker.DEFAULT, new LinkedHashSet<KotlinType>(Arrays.asList(typeA, typeB))) == null;
042        }
043    
044        @Nullable
045        public static KotlinType intersectTypes(
046                @NotNull KotlinTypeChecker typeChecker,
047                @NotNull Set<KotlinType> types
048        ) {
049            assert (!types.isEmpty()) : "Attempting to intersect empty set of types, this case should be dealt with on the call site.";
050    
051            if (types.size() == 1) {
052                return types.iterator().next();
053            }
054    
055            // Intersection of T1..Tn is an intersection of their non-null versions,
056            //   made nullable is they all were nullable
057            KotlinType nothingOrNullableNothing = null;
058            boolean allNullable = true;
059            List<KotlinType> nullabilityStripped = new ArrayList<KotlinType>(types.size());
060            for (KotlinType type : types) {
061                if (type.isError()) continue;
062    
063                if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
064                    nothingOrNullableNothing = type;
065                }
066                allNullable &= type.isMarkedNullable();
067                nullabilityStripped.add(TypeUtils.makeNotNullable(type));
068            }
069    
070            if (nothingOrNullableNothing != null) {
071                return TypeUtils.makeNullableAsSpecified(nothingOrNullableNothing, allNullable);
072            }
073    
074            if (nullabilityStripped.isEmpty()) {
075                // All types were errors
076                return ErrorUtils.createErrorType("Intersection of errors types: " + types);
077            }
078    
079            // Now we remove types that have subtypes in the list
080            List<KotlinType> resultingTypes = new ArrayList<KotlinType>();
081            outer:
082            for (KotlinType type : nullabilityStripped) {
083                if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
084                    for (KotlinType other : nullabilityStripped) {
085                        // It makes sense to check for subtyping (other <: type), despite that
086                        // type is not supposed to be open, for there're enums
087                        if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
088                            return null;
089                        }
090                    }
091                    return TypeUtils.makeNullableAsSpecified(type, allNullable);
092                }
093                else {
094                    for (KotlinType other : nullabilityStripped) {
095                        if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
096                            continue outer;
097                        }
098    
099                    }
100                }
101    
102                // Don't add type if it is already present, to avoid trivial type intersections in result
103                for (KotlinType other : resultingTypes) {
104                    if (typeChecker.equalTypes(other, type)) {
105                        continue outer;
106                    }
107                }
108                resultingTypes.add(type);
109            }
110    
111            if (resultingTypes.isEmpty()) {
112                // If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
113                // most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
114                // in that case, we can safely select the best representative out of that set and return it
115                // TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
116                // TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
117                KotlinType bestRepresentative = FlexibleTypesKt.singleBestRepresentative(nullabilityStripped);
118                if (bestRepresentative == null) {
119                    throw new AssertionError("Empty intersection for types " + types);
120                }
121                return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
122            }
123    
124            if (resultingTypes.size() == 1) {
125                return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
126            }
127    
128            TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.Companion.getEMPTY(), resultingTypes);
129    
130            KtScope[] scopes = new KtScope[resultingTypes.size()];
131            int i = 0;
132            for (KotlinType type : resultingTypes) {
133                scopes[i] = type.getMemberScope();
134                i++;
135            }
136    
137            return KotlinTypeImpl.create(
138                    Annotations.Companion.getEMPTY(),
139                    constructor,
140                    allNullable,
141                    Collections.<TypeProjection>emptyList(),
142                    new IntersectionScope(constructor, scopes)
143            );
144        }
145    
146        // TODO : check intersectibility, don't use a chanied scope
147        private static class IntersectionScope extends ChainedScope {
148            public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull KtScope[] scopes) {
149                super(null, "member scope for intersection type " + constructor, scopes);
150            }
151    
152            @NotNull
153            @Override
154            public DeclarationDescriptor getContainingDeclaration() {
155                throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
156            }
157        }
158    
159        private static class TypeUnifier {
160            private static class TypeParameterUsage {
161                private final TypeParameterDescriptor typeParameterDescriptor;
162                private final Variance howTheTypeParameterIsUsed;
163    
164                public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
165                    this.typeParameterDescriptor = typeParameterDescriptor;
166                    this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
167                }
168            }
169    
170            public static boolean mayBeEqual(@NotNull KotlinType type, @NotNull KotlinType other) {
171                return unify(type, other);
172            }
173    
174            private static boolean unify(KotlinType withParameters, KotlinType expected) {
175                // T -> how T is used
176                final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
177                Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
178                    @Override
179                    public Unit invoke(TypeParameterUsage parameterUsage) {
180                        Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
181                        if (howTheTypeIsUsedBefore == null) {
182                            howTheTypeIsUsedBefore = Variance.INVARIANT;
183                        }
184                        parameters.put(parameterUsage.typeParameterDescriptor,
185                                       parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
186                        return Unit.INSTANCE;
187                    }
188                };
189                processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
190                processAllTypeParameters(expected, Variance.INVARIANT, processor);
191                ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
192                registerTypeVariables(constraintSystem, parameters);
193                constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
194    
195                return constraintSystem.getStatus().isSuccessful();
196            }
197    
198            private static void processAllTypeParameters(KotlinType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
199                ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
200                if (descriptor instanceof TypeParameterDescriptor) {
201                    result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
202                }
203                for (TypeProjection projection : type.getArguments()) {
204                    if (projection.isStarProjection()) continue;
205                    processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
206                }
207            }
208        }
209    }