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.TypeParameterDescriptor;
026 import org.jetbrains.kotlin.descriptors.annotations.Annotations;
027 import org.jetbrains.kotlin.resolve.calls.inference.CallHandle;
028 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
029 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl;
030 import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
031 import org.jetbrains.kotlin.resolve.scopes.MemberScope;
032 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
033
034 import java.util.*;
035
036 import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
037 import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
038
039 public class TypeIntersector {
040
041 public static boolean isIntersectionEmpty(@NotNull KotlinType typeA, @NotNull KotlinType typeB) {
042 return intersectTypes(KotlinTypeChecker.DEFAULT, new LinkedHashSet<KotlinType>(Arrays.asList(typeA, typeB))) == null;
043 }
044
045 @Nullable
046 public static KotlinType intersectTypes(@NotNull KotlinTypeChecker typeChecker, @NotNull Collection<KotlinType> types) {
047 assert !types.isEmpty() : "Attempting to intersect empty collection of types, this case should be dealt with on the call site.";
048
049 if (types.size() == 1) {
050 return types.iterator().next();
051 }
052
053 // Intersection of T1..Tn is an intersection of their non-null versions,
054 // made nullable is they all were nullable
055 KotlinType nothingOrNullableNothing = null;
056 boolean allNullable = true;
057 List<KotlinType> nullabilityStripped = new ArrayList<KotlinType>(types.size());
058 for (KotlinType type : types) {
059 if (type.isError()) continue;
060
061 if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
062 nothingOrNullableNothing = type;
063 }
064 allNullable &= type.isMarkedNullable();
065 nullabilityStripped.add(TypeUtils.makeNotNullable(type));
066 }
067
068 if (nothingOrNullableNothing != null) {
069 return TypeUtils.makeNullableAsSpecified(nothingOrNullableNothing, allNullable);
070 }
071
072 if (nullabilityStripped.isEmpty()) {
073 // All types were errors
074 return ErrorUtils.createErrorType("Intersection of error types: " + types);
075 }
076
077 // Now we remove types that have subtypes in the list
078 List<KotlinType> resultingTypes = new ArrayList<KotlinType>();
079 outer:
080 for (KotlinType type : nullabilityStripped) {
081 if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
082 for (KotlinType other : nullabilityStripped) {
083 // It makes sense to check for subtyping (other <: type), despite that
084 // type is not supposed to be open, for there're enums
085 if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
086 return null;
087 }
088 }
089 return TypeUtils.makeNullableAsSpecified(type, allNullable);
090 }
091 else {
092 for (KotlinType other : nullabilityStripped) {
093 if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
094 continue outer;
095 }
096 }
097 }
098
099 // Don't add type if it is already present, to avoid trivial type intersections in result
100 for (KotlinType other : resultingTypes) {
101 if (typeChecker.equalTypes(other, type)) {
102 continue outer;
103 }
104 }
105 resultingTypes.add(type);
106 }
107
108 if (resultingTypes.isEmpty()) {
109 // If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
110 // most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
111 // in that case, we can safely select the best representative out of that set and return it
112 // TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
113 // TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
114 KotlinType bestRepresentative = FlexibleTypesKt.singleBestRepresentative(nullabilityStripped);
115 if (bestRepresentative == null) {
116 throw new AssertionError("Empty intersection for types " + types);
117 }
118 return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
119 }
120
121 if (resultingTypes.size() == 1) {
122 return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
123 }
124
125 TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.Companion.getEMPTY(), resultingTypes);
126
127 MemberScope[] scopes = new MemberScope[resultingTypes.size()];
128 int i = 0;
129 for (KotlinType type : resultingTypes) {
130 scopes[i] = type.getMemberScope();
131 i++;
132 }
133
134 return KotlinTypeImpl.create(
135 Annotations.Companion.getEMPTY(),
136 constructor,
137 allNullable,
138 Collections.<TypeProjection>emptyList(),
139 new ChainedScope("member scope for intersection type " + constructor, scopes)
140 );
141 }
142
143 /**
144 * Note: this method was used in overload and override bindings to approximate type parameters with several bounds,
145 * but as it turned out at some point, that logic was inconsistent with Java rules, so it was simplified.
146 * Most of the other usages of this method are left untouched but probably should be investigated closely if they're still valid.
147 */
148 @NotNull
149 public static KotlinType getUpperBoundsAsType(@NotNull TypeParameterDescriptor descriptor) {
150 List<KotlinType> upperBounds = descriptor.getUpperBounds();
151 assert !upperBounds.isEmpty() : "Upper bound list is empty: " + descriptor;
152 KotlinType upperBoundsAsType = intersectTypes(KotlinTypeChecker.DEFAULT, upperBounds);
153 return upperBoundsAsType != null ? upperBoundsAsType : getBuiltIns(descriptor).getNothingType();
154 }
155
156 private static class TypeUnifier {
157 private static class TypeParameterUsage {
158 private final TypeParameterDescriptor typeParameterDescriptor;
159 private final Variance howTheTypeParameterIsUsed;
160
161 public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
162 this.typeParameterDescriptor = typeParameterDescriptor;
163 this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
164 }
165 }
166
167 public static boolean mayBeEqual(@NotNull KotlinType type, @NotNull KotlinType other) {
168 return unify(type, other);
169 }
170
171 private static boolean unify(KotlinType withParameters, KotlinType expected) {
172 // T -> how T is used
173 final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
174 Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
175 @Override
176 public Unit invoke(TypeParameterUsage parameterUsage) {
177 Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
178 if (howTheTypeIsUsedBefore == null) {
179 howTheTypeIsUsedBefore = Variance.INVARIANT;
180 }
181 parameters.put(parameterUsage.typeParameterDescriptor,
182 parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
183 return Unit.INSTANCE;
184 }
185 };
186 processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
187 processAllTypeParameters(expected, Variance.INVARIANT, processor);
188 ConstraintSystem.Builder constraintSystem = new ConstraintSystemBuilderImpl();
189 TypeSubstitutor substitutor = constraintSystem.registerTypeVariables(CallHandle.NONE.INSTANCE, parameters.keySet(), false);
190 constraintSystem.addSubtypeConstraint(withParameters, substitutor.substitute(expected, Variance.INVARIANT), SPECIAL.position());
191
192 return constraintSystem.build().getStatus().isSuccessful();
193 }
194
195 private static void processAllTypeParameters(KotlinType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
196 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
197 if (descriptor instanceof TypeParameterDescriptor) {
198 result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
199 }
200 for (TypeProjection projection : type.getArguments()) {
201 if (projection.isStarProjection()) continue;
202 processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
203 }
204 }
205 }
206 }