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 }