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 017package org.jetbrains.jet.lang.types; 018 019import com.google.common.base.Function; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.Lists; 022import com.google.common.collect.Maps; 023import com.google.common.collect.Sets; 024import com.intellij.util.Processor; 025import org.jetbrains.annotations.NotNull; 026import org.jetbrains.annotations.Nullable; 027import org.jetbrains.jet.lang.descriptors.*; 028import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; 029import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintResolutionListener; 030import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; 031import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities; 032import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType; 033import org.jetbrains.jet.lang.resolve.scopes.ChainedScope; 034import org.jetbrains.jet.lang.resolve.scopes.JetScope; 035import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 036import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 037 038import java.util.*; 039 040public 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}