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.JetScope;
031    import org.jetbrains.kotlin.types.checker.JetTypeChecker;
032    
033    import java.util.*;
034    
035    import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.registerTypeVariables;
036    import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
037    
038    public class TypeIntersector {
039    
040        private final KotlinBuiltIns builtIns;
041    
042        public TypeIntersector(@NotNull KotlinBuiltIns builtIns) {
043            this.builtIns = builtIns;
044        }
045    
046        public boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
047            return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet<JetType>(Arrays.asList(typeA, typeB))) == null;
048        }
049    
050        //TODO: usages of this method should be removed
051        @Nullable
052        public static JetType intersectTypes(
053                @NotNull KotlinBuiltIns builtIns,
054                @NotNull JetTypeChecker typeChecker,
055                @NotNull Set<JetType> types
056        ) {
057            return new TypeIntersector(builtIns).intersect(typeChecker, types);
058        }
059    
060        @Nullable
061        public JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
062            if (types.isEmpty()) {
063                return builtIns.getNullableAnyType();
064            }
065    
066            if (types.size() == 1) {
067                return types.iterator().next();
068            }
069    
070            // Intersection of T1..Tn is an intersection of their non-null versions,
071            //   made nullable is they all were nullable
072            boolean allNullable = true;
073            boolean nothingTypePresent = false;
074            List<JetType> nullabilityStripped = new ArrayList<JetType>(types.size());
075            for (JetType type : types) {
076                if (type.isError()) continue;
077    
078                nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type);
079                allNullable &= type.isMarkedNullable();
080                nullabilityStripped.add(TypeUtils.makeNotNullable(type));
081            }
082    
083            if (nothingTypePresent) {
084                return allNullable ? builtIns.getNullableNothingType() : builtIns.getNothingType();
085            }
086    
087            if (nullabilityStripped.isEmpty()) {
088                // All types were errors
089                return ErrorUtils.createErrorType("Intersection of errors types: " + types);
090            }
091    
092            // Now we remove types that have subtypes in the list
093            List<JetType> resultingTypes = new ArrayList<JetType>();
094            outer:
095            for (JetType type : nullabilityStripped) {
096                if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
097                    for (JetType other : nullabilityStripped) {
098                        // It makes sense to check for subtyping (other <: type), despite that
099                        // type is not supposed to be open, for there're enums
100                        if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
101                            return null;
102                        }
103                    }
104                    return TypeUtils.makeNullableAsSpecified(type, allNullable);
105                }
106                else {
107                    for (JetType other : nullabilityStripped) {
108                        if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
109                            continue outer;
110                        }
111    
112                    }
113                }
114    
115                // Don't add type if it is already present, to avoid trivial type intersections in result
116                for (JetType other : resultingTypes) {
117                    if (typeChecker.equalTypes(other, type)) {
118                        continue outer;
119                    }
120                }
121                resultingTypes.add(type);
122            }
123    
124            if (resultingTypes.isEmpty()) {
125                // If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
126                // most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
127                // in that case, we can safely select the best representative out of that set and return it
128                // TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
129                // TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
130                JetType bestRepresentative = TypesPackage.singleBestRepresentative(nullabilityStripped);
131                if (bestRepresentative == null) {
132                    throw new AssertionError("Empty intersection for types " + types);
133                }
134                return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
135            }
136    
137            if (resultingTypes.size() == 1) {
138                return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
139            }
140    
141            TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.EMPTY, resultingTypes);
142    
143            JetScope[] scopes = new JetScope[resultingTypes.size()];
144            int i = 0;
145            for (JetType type : resultingTypes) {
146                scopes[i] = type.getMemberScope();
147                i++;
148            }
149    
150            return JetTypeImpl.create(
151                    Annotations.EMPTY,
152                    constructor,
153                    allNullable,
154                    Collections.<TypeProjection>emptyList(),
155                    new IntersectionScope(constructor, scopes)
156            );
157        }
158    
159        // TODO : check intersectibility, don't use a chanied scope
160        private static class IntersectionScope extends ChainedScope {
161            public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull JetScope[] scopes) {
162                super(null, "member scope for intersection type " + constructor, scopes);
163            }
164    
165            @NotNull
166            @Override
167            public DeclarationDescriptor getContainingDeclaration() {
168                throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
169            }
170        }
171    
172        private static class TypeUnifier {
173            private static class TypeParameterUsage {
174                private final TypeParameterDescriptor typeParameterDescriptor;
175                private final Variance howTheTypeParameterIsUsed;
176    
177                public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
178                    this.typeParameterDescriptor = typeParameterDescriptor;
179                    this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
180                }
181            }
182    
183            public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
184                return unify(type, other);
185            }
186    
187            private static boolean unify(JetType withParameters, JetType expected) {
188                // T -> how T is used
189                final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
190                Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
191                    @Override
192                    public Unit invoke(TypeParameterUsage parameterUsage) {
193                        Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
194                        if (howTheTypeIsUsedBefore == null) {
195                            howTheTypeIsUsedBefore = Variance.INVARIANT;
196                        }
197                        parameters.put(parameterUsage.typeParameterDescriptor,
198                                       parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
199                        return Unit.INSTANCE$;
200                    }
201                };
202                processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
203                processAllTypeParameters(expected, Variance.INVARIANT, processor);
204                ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
205                registerTypeVariables(constraintSystem, parameters);
206                constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
207    
208                return constraintSystem.getStatus().isSuccessful();
209            }
210    
211            private static void processAllTypeParameters(JetType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
212                ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
213                if (descriptor instanceof TypeParameterDescriptor) {
214                    result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
215                }
216                for (TypeProjection projection : type.getArguments()) {
217                    if (projection.isStarProjection()) continue;
218                    processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
219                }
220            }
221        }
222    }