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 }