001 /*
002 * Copyright 2010-2013 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.jet.lang.types;
018
019 import com.google.common.base.Function;
020 import com.google.common.collect.Collections2;
021 import com.google.common.collect.Lists;
022 import com.google.common.collect.Maps;
023 import com.google.common.collect.Sets;
024 import com.intellij.util.Processor;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.lang.descriptors.*;
028 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintResolutionListener;
030 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution;
031 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities;
032 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType;
033 import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
034 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
036 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037
038 import java.util.*;
039
040 public class TypeUtils {
041 public static final JetType NO_EXPECTED_TYPE = new JetType() {
042 @NotNull
043 @Override
044 public TypeConstructor getConstructor() {
045 throw new IllegalStateException();
046 }
047
048 @NotNull
049 @Override
050 public List<TypeProjection> getArguments() {
051 throw new IllegalStateException();
052 }
053
054 @Override
055 public boolean isNullable() {
056 throw new IllegalStateException();
057 }
058
059 @NotNull
060 @Override
061 public JetScope getMemberScope() {
062 throw new IllegalStateException();
063 }
064
065 @Override
066 public List<AnnotationDescriptor> getAnnotations() {
067 throw new IllegalStateException();
068 }
069
070 @Override
071 public String toString() {
072 return "NO_EXPECTED_TYPE";
073 }
074 };
075
076 @NotNull
077 public static JetType makeNullable(@NotNull JetType type) {
078 return makeNullableAsSpecified(type, true);
079 }
080
081 @NotNull
082 public static JetType makeNotNullable(@NotNull JetType type) {
083 return makeNullableAsSpecified(type, false);
084 }
085
086 @NotNull
087 public static JetType makeNullableAsSpecified(@NotNull JetType type, boolean nullable) {
088 if (type.isNullable() == nullable) {
089 return type;
090 }
091 if (ErrorUtils.isErrorType(type)) {
092 return type;
093 }
094 return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), nullable, type.getArguments(), type.getMemberScope());
095 }
096
097 public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
098 return intersect(JetTypeChecker.INSTANCE, Sets.newLinkedHashSet(Lists.newArrayList(typeA, typeB))) == null;
099 }
100
101 @Nullable
102 public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
103 if (types.isEmpty()) {
104 return KotlinBuiltIns.getInstance().getNullableAnyType();
105 }
106
107 if (types.size() == 1) {
108 return types.iterator().next();
109 }
110
111 // Intersection of T1..Tn is an intersection of their non-null versions,
112 // made nullable is they all were nullable
113 boolean allNullable = true;
114 boolean nothingTypePresent = false;
115 List<JetType> nullabilityStripped = Lists.newArrayList();
116 for (JetType type : types) {
117 nothingTypePresent |= KotlinBuiltIns.getInstance().isNothingOrNullableNothing(type);
118 allNullable &= type.isNullable();
119 nullabilityStripped.add(makeNotNullable(type));
120 }
121
122 if (nothingTypePresent) {
123 return allNullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType();
124 }
125
126 // Now we remove types that have subtypes in the list
127 List<JetType> resultingTypes = Lists.newArrayList();
128 outer:
129 for (JetType type : nullabilityStripped) {
130 if (!canHaveSubtypes(typeChecker, type)) {
131 for (JetType other : nullabilityStripped) {
132 // It makes sense to check for subtyping (other <: type), despite that
133 // type is not supposed to be open, for there're enums
134 if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
135 return null;
136 }
137 }
138 return makeNullableAsSpecified(type, allNullable);
139 }
140 else {
141 for (JetType other : nullabilityStripped) {
142 if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
143 continue outer;
144 }
145
146 }
147 }
148
149 // Don't add type if it is already present, to avoid trivial type intersections in result
150 for (JetType other : resultingTypes) {
151 if (typeChecker.equalTypes(other, type)) {
152 continue outer;
153 }
154 }
155 resultingTypes.add(type);
156 }
157
158 if (resultingTypes.size() == 1) {
159 return makeNullableAsSpecified(resultingTypes.get(0), allNullable);
160 }
161
162
163 List<AnnotationDescriptor> noAnnotations = Collections.<AnnotationDescriptor>emptyList();
164 TypeConstructor constructor = new IntersectionTypeConstructor(
165 noAnnotations,
166 resultingTypes);
167
168 JetScope[] scopes = new JetScope[resultingTypes.size()];
169 int i = 0;
170 for (JetType type : resultingTypes) {
171 scopes[i] = type.getMemberScope();
172 i++;
173 }
174
175 return new JetTypeImpl(
176 noAnnotations,
177 constructor,
178 allNullable,
179 Collections.<TypeProjection>emptyList(),
180 new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope
181 }
182
183 private static class TypeUnifier {
184 private static class TypeParameterUsage {
185 private final TypeParameterDescriptor typeParameterDescriptor;
186 private final Variance howTheTypeParameterIsUsed;
187
188 public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
189 this.typeParameterDescriptor = typeParameterDescriptor;
190 this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
191 }
192 }
193
194 public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
195 return unify(type, other);
196 }
197
198 private static boolean unify(JetType withParameters, JetType expected) {
199 ConstraintSystemWithPriorities constraintSystem = new ConstraintSystemWithPriorities(ConstraintResolutionListener.DO_NOTHING);
200 // T -> how T is used
201 final Map<TypeParameterDescriptor, Variance> parameters = Maps.newHashMap();
202 Processor<TypeParameterUsage> processor = new Processor<TypeParameterUsage>() {
203 @Override
204 public boolean process(TypeParameterUsage parameterUsage) {
205 Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
206 if (howTheTypeIsUsedBefore == null) {
207 howTheTypeIsUsedBefore = Variance.INVARIANT;
208 }
209 parameters.put(parameterUsage.typeParameterDescriptor,
210 parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
211 return true;
212 }
213 };
214 processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
215 processAllTypeParameters(expected, Variance.INVARIANT, processor);
216 for (Map.Entry<TypeParameterDescriptor, Variance> entry : parameters.entrySet()) {
217 constraintSystem.registerTypeVariable(entry.getKey(), entry.getValue());
218 }
219 constraintSystem.addSubtypingConstraint(ConstraintType.VALUE_ARGUMENT.assertSubtyping(withParameters, expected));
220
221 ConstraintSystemSolution solution = constraintSystem.solve();
222 return solution.getStatus().isSuccessful();
223 }
224
225 private static void processAllTypeParameters(JetType type, Variance howThiTypeIsUsed, Processor<TypeParameterUsage> result) {
226 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
227 if (descriptor instanceof TypeParameterDescriptor) {
228 result.process(new TypeParameterUsage((TypeParameterDescriptor)descriptor, howThiTypeIsUsed));
229 }
230 for (TypeProjection projection : type.getArguments()) {
231 processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
232 }
233 }
234 }
235
236 public static boolean canHaveSubtypes(JetTypeChecker typeChecker, JetType type) {
237 if (type.isNullable()) {
238 return true;
239 }
240 if (!type.getConstructor().isSealed()) {
241 return true;
242 }
243
244 List<TypeParameterDescriptor> parameters = type.getConstructor().getParameters();
245 List<TypeProjection> arguments = type.getArguments();
246 for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
247 TypeParameterDescriptor parameterDescriptor = parameters.get(i);
248 TypeProjection typeProjection = arguments.get(i);
249 Variance projectionKind = typeProjection.getProjectionKind();
250 JetType argument = typeProjection.getType();
251
252 switch (parameterDescriptor.getVariance()) {
253 case INVARIANT:
254 switch (projectionKind) {
255 case INVARIANT:
256 if (lowerThanBound(typeChecker, argument, parameterDescriptor) || canHaveSubtypes(typeChecker, argument)) {
257 return true;
258 }
259 break;
260 case IN_VARIANCE:
261 if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
262 return true;
263 }
264 break;
265 case OUT_VARIANCE:
266 if (canHaveSubtypes(typeChecker, argument)) {
267 return true;
268 }
269 break;
270 }
271 break;
272 case IN_VARIANCE:
273 if (projectionKind != Variance.OUT_VARIANCE) {
274 if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
275 return true;
276 }
277 }
278 else {
279 if (canHaveSubtypes(typeChecker, argument)) {
280 return true;
281 }
282 }
283 break;
284 case OUT_VARIANCE:
285 if (projectionKind != Variance.IN_VARIANCE) {
286 if (canHaveSubtypes(typeChecker, argument)) {
287 return true;
288 }
289 }
290 else {
291 if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
292 return true;
293 }
294 }
295 break;
296 }
297 }
298 return false;
299 }
300
301 private static boolean lowerThanBound(JetTypeChecker typeChecker, JetType argument, TypeParameterDescriptor parameterDescriptor) {
302 for (JetType bound : parameterDescriptor.getUpperBounds()) {
303 if (typeChecker.isSubtypeOf(argument, bound)) {
304 if (!argument.getConstructor().equals(bound.getConstructor())) {
305 return true;
306 }
307 }
308 }
309 return false;
310 }
311
312 public static JetType makeNullableIfNeeded(JetType type, boolean nullable) {
313 if (nullable) {
314 return makeNullable(type);
315 }
316 return type;
317 }
318
319 @NotNull
320 public static JetType makeUnsubstitutedType(ClassDescriptor classDescriptor, JetScope unsubstitutedMemberScope) {
321 if (ErrorUtils.isError(classDescriptor)) {
322 return ErrorUtils.createErrorType("Unsubstituted type for " + classDescriptor);
323 }
324 List<TypeProjection> arguments = getDefaultTypeProjections(classDescriptor.getTypeConstructor().getParameters());
325 return new JetTypeImpl(
326 Collections.<AnnotationDescriptor>emptyList(),
327 classDescriptor.getTypeConstructor(),
328 false,
329 arguments,
330 unsubstitutedMemberScope
331 );
332 }
333
334 @NotNull
335 public static List<TypeProjection> getDefaultTypeProjections(List<TypeParameterDescriptor> parameters) {
336 List<TypeProjection> result = new ArrayList<TypeProjection>();
337 for (TypeParameterDescriptor parameterDescriptor : parameters) {
338 result.add(new TypeProjection(parameterDescriptor.getDefaultType()));
339 }
340 return result;
341 }
342
343 @NotNull
344 public static List<JetType> getDefaultTypes(List<TypeParameterDescriptor> parameters) {
345 List<JetType> result = Lists.newArrayList();
346 for (TypeParameterDescriptor parameterDescriptor : parameters) {
347 result.add(parameterDescriptor.getDefaultType());
348 }
349 return result;
350 }
351
352 private static void collectImmediateSupertypes(@NotNull JetType type, @NotNull Collection<JetType> result) {
353 TypeSubstitutor substitutor = TypeSubstitutor.create(type);
354 for (JetType supertype : type.getConstructor().getSupertypes()) {
355 result.add(substitutor.substitute(supertype, Variance.INVARIANT));
356 }
357 }
358
359 @NotNull
360 public static List<JetType> getImmediateSupertypes(@NotNull JetType type) {
361 List<JetType> result = Lists.newArrayList();
362 collectImmediateSupertypes(type, result);
363 return result;
364 }
365
366 private static void collectAllSupertypes(@NotNull JetType type, @NotNull Set<JetType> result) {
367 List<JetType> immediateSupertypes = getImmediateSupertypes(type);
368 result.addAll(immediateSupertypes);
369 for (JetType supertype : immediateSupertypes) {
370 collectAllSupertypes(supertype, result);
371 }
372 }
373
374
375 @NotNull
376 public static Set<JetType> getAllSupertypes(@NotNull JetType type) {
377 // 15 is obtained by experimentation: JDK classes like ArrayList tend to have so many supertypes,
378 // the average number is lower
379 Set<JetType> result = new LinkedHashSet<JetType>(15);
380 collectAllSupertypes(type, result);
381 return result;
382 }
383
384 public static boolean hasNullableLowerBound(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
385 for (JetType bound : typeParameterDescriptor.getLowerBounds()) {
386 if (bound.isNullable()) {
387 return true;
388 }
389 }
390 return false;
391 }
392
393 public static boolean hasNullableSuperType(@NotNull JetType type) {
394 if (type.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor) {
395 // A class/trait cannot have a nullable supertype
396 return false;
397 }
398
399 for (JetType supertype : getImmediateSupertypes(type)) {
400 if (supertype.isNullable()) return true;
401 if (hasNullableSuperType(supertype)) return true;
402 }
403
404 return false;
405 }
406
407 public static boolean equalClasses(@NotNull JetType type1, @NotNull JetType type2) {
408 DeclarationDescriptor declarationDescriptor1 = type1.getConstructor().getDeclarationDescriptor();
409 if (declarationDescriptor1 == null) return false; // No class, classes are not equal
410 DeclarationDescriptor declarationDescriptor2 = type2.getConstructor().getDeclarationDescriptor();
411 if (declarationDescriptor2 == null) return false; // Class of type1 is not null
412 return declarationDescriptor1.getOriginal().equals(declarationDescriptor2.getOriginal());
413 }
414
415 @Nullable
416 public static ClassDescriptor getClassDescriptor(@NotNull JetType type) {
417 DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
418 if (declarationDescriptor instanceof ClassDescriptor) {
419 return (ClassDescriptor) declarationDescriptor;
420 }
421 return null;
422 }
423
424 @NotNull
425 public static JetType substituteParameters(@NotNull ClassDescriptor clazz, @NotNull List<JetType> actualTypeParameters) {
426 List<TypeParameterDescriptor> clazzTypeParameters = clazz.getTypeConstructor().getParameters();
427
428 if (clazzTypeParameters.size() != actualTypeParameters.size()) {
429 throw new IllegalArgumentException("type parameter counts do not match: " + clazz + ", " + actualTypeParameters);
430 }
431
432 Map<TypeConstructor, TypeProjection> substitutions = Maps.newHashMap();
433
434 for (int i = 0; i < clazzTypeParameters.size(); ++i) {
435 TypeConstructor typeConstructor = clazzTypeParameters.get(i).getTypeConstructor();
436 TypeProjection typeProjection = new TypeProjection(actualTypeParameters.get(i));
437 substitutions.put(typeConstructor, typeProjection);
438 }
439
440 return TypeSubstitutor.create(substitutions).substitute(clazz.getDefaultType(), Variance.INVARIANT);
441 }
442
443 private static void addAllClassDescriptors(@NotNull JetType type, @NotNull Set<ClassDescriptor> set) {
444 ClassDescriptor cd = getClassDescriptor(type);
445 if (cd != null) {
446 set.add(cd);
447 }
448 for (TypeProjection projection : type.getArguments()) {
449 addAllClassDescriptors(projection.getType(), set);
450 }
451 }
452
453 @NotNull
454 public static List<ClassDescriptor> getAllClassDescriptors(@NotNull JetType type) {
455 Set<ClassDescriptor> classDescriptors = new HashSet<ClassDescriptor>();
456 addAllClassDescriptors(type, classDescriptors);
457 return new ArrayList<ClassDescriptor>(classDescriptors);
458 }
459
460 public static boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
461 return JetTypeChecker.INSTANCE.isSubtypeOf(a, b) && JetTypeChecker.INSTANCE.isSubtypeOf(b, a);
462 }
463
464 public static boolean typeConstructorUsedInType(@NotNull TypeConstructor key, @NotNull JetType value) {
465 if (value.getConstructor() == key) return true;
466 for (TypeProjection projection : value.getArguments()) {
467 if (typeConstructorUsedInType(key, projection.getType())) {
468 return true;
469 }
470 }
471 return false;
472 }
473
474 public static boolean dependsOnTypeParameters(@NotNull JetType type, @NotNull Collection<TypeParameterDescriptor> typeParameters) {
475 return dependsOnTypeConstructors(type, Collections2
476 .transform(typeParameters, new Function<TypeParameterDescriptor, TypeConstructor>() {
477 @Override
478 public TypeConstructor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) {
479 assert typeParameterDescriptor != null;
480 return typeParameterDescriptor.getTypeConstructor();
481 }
482 }));
483 }
484
485 public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
486 if (typeParameterConstructors.contains(type.getConstructor())) return true;
487 for (TypeProjection typeProjection : type.getArguments()) {
488 if (dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
489 return true;
490 }
491 }
492 return false;
493 }
494
495 public static boolean equalsOrContainsAsArgument(@Nullable JetType type, @NotNull JetType... possibleArgumentTypes) {
496 return equalsOrContainsAsArgument(type, Sets.newHashSet(possibleArgumentTypes));
497 }
498
499 private static boolean equalsOrContainsAsArgument(@Nullable JetType type, @NotNull Set<JetType> possibleArgumentTypes) {
500 if (type == null) return false;
501 if (possibleArgumentTypes.contains(type)) return true;
502 if (type instanceof NamespaceType) return false;
503 for (TypeProjection projection : type.getArguments()) {
504 if (equalsOrContainsAsArgument(projection.getType(), possibleArgumentTypes)) return true;
505 }
506 return false;
507 }
508
509 @NotNull
510 public static String getTypeNameAndStarProjectionsString(@NotNull String name, int size) {
511 StringBuilder builder = new StringBuilder(name);
512 builder.append("<");
513 for (int i = 0; i < size; i++) {
514 builder.append("*");
515 if (i == size - 1) break;
516 builder.append(", ");
517 }
518 builder.append(">");
519
520 return builder.toString();
521 }
522 }