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.resolve.calls.inference; 018 019import com.google.common.collect.Maps; 020import com.google.common.collect.Sets; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 023import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 024import org.jetbrains.jet.lang.types.*; 025import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 026import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure; 027import org.jetbrains.jet.lang.types.checker.TypingConstraints; 028 029import java.util.*; 030 031import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.PARAMETER_BOUND; 032 033public class ConstraintSystemWithPriorities { 034 035 public static final Comparator<SubtypingConstraint> SUBTYPING_CONSTRAINT_ORDER = new Comparator<SubtypingConstraint>() { 036 @Override 037 public int compare(SubtypingConstraint o1, SubtypingConstraint o2) { 038 return o1.getType().compareTo(o2.getType()); 039 } 040 }; 041 042 public static TypeSubstitutor makeConstantSubstitutor(Collection<TypeParameterDescriptor> typeParameterDescriptors, JetType type) { 043 final Set<TypeConstructor> constructors = Sets.newHashSet(); 044 for (TypeParameterDescriptor typeParameterDescriptor : typeParameterDescriptors) { 045 constructors.add(typeParameterDescriptor.getTypeConstructor()); 046 } 047 final TypeProjection projection = new TypeProjection(type); 048 049 return TypeSubstitutor.create(new TypeSubstitution() { 050 @Override 051 public TypeProjection get(TypeConstructor key) { 052 if (constructors.contains(key)) { 053 return projection; 054 } 055 return null; 056 } 057 058 @Override 059 public boolean isEmpty() { 060 return false; 061 } 062 }); 063 } 064 065 private static class LoopInTypeVariableConstraintsException extends RuntimeException { 066 public LoopInTypeVariableConstraintsException() {} 067 } 068 069 //========================================================================================================================================================== 070 071 private final Map<JetType, TypeValue> knownTypes = Maps.newLinkedHashMap(); // linked - for easier debugging 072 private final Map<TypeParameterDescriptor, TypeValue> unknownTypes = Maps.newLinkedHashMap(); // linked - for easier debugging 073 private final Set<TypeValue> unsolvedUnknowns = Sets.newLinkedHashSet(); // linked - for easier debugging 074 private final PriorityQueue<SubtypingConstraint> constraintQueue = new PriorityQueue<SubtypingConstraint>(10, SUBTYPING_CONSTRAINT_ORDER); 075 076 private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; 077 private final TypeCheckingProcedure constraintExpander; 078 private final ConstraintResolutionListener listener; 079 080 public ConstraintSystemWithPriorities(ConstraintResolutionListener listener) { 081 this.listener = listener; 082 this.constraintExpander = createConstraintExpander(); 083 } 084 085 @NotNull 086 private TypeValue getTypeValueFor(@NotNull JetType type) { 087 DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); 088 if (declarationDescriptor instanceof TypeParameterDescriptor) { 089 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor; 090 // Checking that this is not a T?, but exactly T 091 if (typeParameterDescriptor.getDefaultType().isNullable() == type.isNullable()) { 092 TypeValue unknownType = unknownTypes.get(typeParameterDescriptor); 093 if (unknownType != null) { 094 return unknownType; 095 } 096 } 097 } 098 099 TypeValue typeValue = knownTypes.get(type); 100 if (typeValue == null) { 101 typeValue = new TypeValue(type); 102 knownTypes.put(type, typeValue); 103 } 104 return typeValue; 105 } 106 107 public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { 108 assert !unknownTypes.containsKey(typeParameterDescriptor); 109 TypeValue typeValue = new TypeValue(typeParameterDescriptor, positionVariance); 110 unknownTypes.put(typeParameterDescriptor, typeValue); 111 unsolvedUnknowns.add(typeValue); 112 } 113 114 @NotNull 115 private TypeValue getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) { 116 TypeValue unknownType = unknownTypes.get(typeParameterDescriptor); 117 if (unknownType == null) { 118 throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor); 119 } 120 return unknownType; 121 } 122 123 public void addSubtypingConstraint(@NotNull SubtypingConstraint constraint) { 124 constraintQueue.add(constraint); 125 } 126 127 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 128 // Constraint expansion 129 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 130 131 private TypeCheckingProcedure createConstraintExpander() { 132 return new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() { 133 @Override 134 public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { 135 TypeValue aValue = getTypeValueFor(a); 136 TypeValue bValue = getTypeValueFor(b); 137 138 return expandEqualityConstraint(aValue, bValue); 139 } 140 141 @SuppressWarnings("SuspiciousMethodCalls") @Override 142 public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { 143 return a.equals(b) 144 || unknownTypes.containsKey(a.getDeclarationDescriptor()) 145 || unknownTypes.containsKey(b.getDeclarationDescriptor()); 146 } 147 148 @Override 149 public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { 150 TypeValue subtypeValue = getTypeValueFor(subtype); 151 TypeValue supertypeValue = getTypeValueFor(supertype); 152 153 if (someUnknown(subtypeValue, supertypeValue)) { 154 expandSubtypingConstraint(subtypeValue, supertypeValue); 155 } 156 return true; // For known types further expansion happens automatically 157 } 158 159 @Override 160 public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { 161 // If some of the types is an unknown, the constraint must be generated, and we should carry on 162 // otherwise there can be no solution, and we should fail 163 TypeValue subTypeValue = getTypeValueFor(subtype); 164 TypeValue superTypeValue = getTypeValueFor(supertype); 165 boolean someUnknown = someUnknown(subTypeValue, superTypeValue); 166 if (someUnknown) { 167 expandSubtypingConstraint(subTypeValue, superTypeValue); 168 } 169 return someUnknown; 170 } 171 172 private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { 173 return !subtypeValue.isKnown() || !supertypeValue.isKnown(); 174 } 175 176 }, listener)); 177 } 178 179 private boolean assignValueTo(TypeValue unknown, JetType value) { 180 if (unknown.hasValue()) { 181 // If we have already assigned a value to this unknown, 182 // it is a conflict to assign another one, unless this new one is equal to the previous 183 return TypeUtils.equalTypes(unknown.getType(), value); 184 } 185 unsolvedUnknowns.remove(unknown); 186 unknown.setValue(value); 187 return true; 188 } 189 190 private boolean mergeUnknowns(@NotNull TypeValue a, @NotNull TypeValue b) { 191 assert !a.isKnown() && !b.isKnown(); 192 listener.error("!!!mergeUnknowns() is not implemented!!!"); 193 return false; 194 } 195 196 public boolean expandEqualityConstraint(TypeValue a, TypeValue b) { 197 if (a.isKnown() && b.isKnown()) { 198 return constraintExpander.equalTypes(a.getType(), b.getType()); 199 } 200 201 // At least one of them is unknown 202 if (a.isKnown()) { 203 TypeValue tmp = a; 204 a = b; 205 b = tmp; 206 } 207 208 // Now a is definitely unknown 209 if (b.isKnown()) { 210 return assignValueTo(a, b.getType()); 211 } 212 213 // They are both unknown 214 return mergeUnknowns(a, b); 215 } 216 217 private boolean expandSubtypingConstraint(TypeValue lower, TypeValue upper) { 218 listener.log("Constraint added: ", lower, " :< ", upper); 219 220 if (lower == upper) return true; 221 222 // Remember for a later check 223 lower.addUpperBound(upper); 224 upper.addLowerBound(lower); 225 226 if (lower.isKnown() && upper.isKnown()) { 227 // Two known types: expand constraints 228 return constraintExpander.isSubtypeOf(lower.getType(), upper.getType()); 229 } 230 else if (!lower.isKnown() && !upper.isKnown()) { 231 // Two unknown types: merge them into one variable 232 return mergeUnknowns(lower, upper); 233 } 234 else { 235 // One unknown and one known 236 if (upper.isKnown()) { 237 if (!TypeUtils.canHaveSubtypes(typeChecker, upper.getType())) { 238 // Upper bound is final -> we have to equate the lower bounds to it 239 return expandEqualityConstraint(lower, upper); 240 } 241 if (lower.getLowerBounds().contains(upper)) { 242 // upper :< lower :< upper 243 return expandEqualityConstraint(lower, upper); 244 } 245 } 246 else { 247 if (upper.getUpperBounds().contains(lower)) { 248 // lower :< upper :< lower 249 return expandEqualityConstraint(lower, upper); 250 } 251 } 252 } 253 return true; 254 } 255 256 @NotNull 257 public ConstraintSystemSolution solve() { 258 Solution solution = new Solution(); 259 // At this point we only have type values, no bounds added for them, no values computed for unknown types 260 261 // After the parameters are inferred we will make sure the initial constraints are satisfied 262 PriorityQueue<SubtypingConstraint> constraintsToEnsureAfterInference = new PriorityQueue<SubtypingConstraint>(constraintQueue); 263 264 // Expand and solve constraints 265 while (!constraintQueue.isEmpty()) { 266 SubtypingConstraint constraint = constraintQueue.poll(); 267 268 // Apply constraint 269 TypeValue lower = getTypeValueFor(constraint.getSubtype()); 270 TypeValue upper = getTypeValueFor(constraint.getSupertype()); 271 boolean success = expandSubtypingConstraint(lower, upper); 272 if (!success) { 273 solution.registerError(constraint.getErrorMessage()); 274// break; 275 } 276 277 // (???) Propagate info 278 279 // Any unknowns left? 280 if (unsolvedUnknowns.isEmpty()) break; 281 } 282 283 284 // effective bounds for each node 285// Set<TypeValue> visited = Sets.newHashSet(); 286// for (TypeValue unknownType : unknownTypes.values()) { 287// transitiveClosure(unknownType, visited); 288// } 289 290 assert constraintQueue.isEmpty() || unsolvedUnknowns.isEmpty() : constraintQueue + " " + unsolvedUnknowns; 291 292 for (TypeValue unknown : Sets.newLinkedHashSet(unsolvedUnknowns)) { 293 if (!computeValueFor(unknown)) { 294 listener.error("Not enough data to compute value for ", unknown); 295 solution.registerError("Not enough data to compute value for " + unknown + ". Please, specify type arguments explicitly"); 296 } 297 } 298 299 // Logging 300 for (TypeValue unknownType : unknownTypes.values()) { 301 listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType); 302 } 303 for (TypeValue knownType : knownTypes.values()) { 304 listener.constraintsForKnownType(knownType.getType(), knownType); 305 } 306 307 // Now, let's check the rest of the constraints and re-check the initial ones 308 309 // Add constraints for the declared bounds for parameters 310 // Maybe these bounds could reconcile some resolution earlier? Then, move them up 311 for (Map.Entry<TypeParameterDescriptor, TypeValue> entry : Sets.newHashSet(unknownTypes.entrySet())) { 312 TypeParameterDescriptor typeParameterDescriptor = entry.getKey(); 313 TypeValue unknown = entry.getValue(); 314 for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) { 315 constraintsToEnsureAfterInference.add(PARAMETER_BOUND.assertSubtyping(unknown.getOriginalType(), getTypeValueFor(upperBound).getOriginalType())); 316// unknown.addUpperBound(new TypeValue(upperBound)); 317 } 318 for (JetType lowerBound : typeParameterDescriptor.getLowerBounds()) { 319 constraintsToEnsureAfterInference.add(PARAMETER_BOUND.assertSubtyping(getTypeValueFor(lowerBound).getOriginalType(), unknown.getOriginalType())); 320// unknown.addLowerBound(new TypeValue(lowerBound)); 321 } 322 } 323 324 325 // Find inconsistencies 326 327 // Check that all bounds are respected by solutions: 328 // we have set some of them from equality constraints with known types 329 // and thus the bounds may be violated if some of the constraints conflict 330 331 332 for (SubtypingConstraint constraint : constraintsToEnsureAfterInference) { 333 JetType substitutedSubtype = solution.getSubstitutor().substitute(constraint.getSubtype(), Variance.INVARIANT); // TODO 334 if (substitutedSubtype == null) continue; 335 JetType substitutedSupertype = solution.getSubstitutor().substitute(constraint.getSupertype(), Variance.INVARIANT); // TODO 336 if (substitutedSupertype == null) continue; 337 338 if (!typeChecker.isSubtypeOf(substitutedSubtype, substitutedSupertype)) { 339 solution.registerError(constraint.getErrorMessage()); 340 listener.error("Constraint violation: ", substitutedSubtype, " :< ", substitutedSupertype, " message: ", constraint.getErrorMessage()); 341 } 342 } 343 344 345// for (TypeValue unknownType : unknownTypes.values()) { 346// check(unknownType, solution); 347// } 348// 349// for (TypeValue knownType : knownTypes.values()) { 350// check(knownType, solution); 351// } 352 353 354 listener.done(solution, unknownTypes.keySet()); 355 356 return solution; 357 } 358 359 private final Set<TypeValue> beingComputed = Sets.newHashSet(); 360 361 public boolean computeValueFor(TypeValue unknown) { 362 assert !unknown.isKnown(); 363 if (beingComputed.contains(unknown)) { 364 throw new LoopInTypeVariableConstraintsException(); 365 } 366 if (!unknown.hasValue()) { 367 beingComputed.add(unknown); 368 try { 369 if (unknown.getPositionVariance() == Variance.IN_VARIANCE) { 370 // maximal solution 371 throw new UnsupportedOperationException(); 372 } 373 else { 374 // minimal solution 375 376 Set<TypeValue> lowerBounds = unknown.getLowerBounds(); 377 Set<TypeValue> upperBounds = unknown.getUpperBounds(); 378 if (!lowerBounds.isEmpty()) { 379 Set<JetType> types = getTypes(lowerBounds); 380 381 JetType commonSupertype = CommonSupertypes.commonSupertype(types); 382 for (TypeValue upperBound : upperBounds) { 383 if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getType())) { 384 return false; 385 } 386 } 387 388 listener.log("minimal solution from lower bounds for ", this, " is ", commonSupertype); 389 assignValueTo(unknown, commonSupertype); 390 } 391 else if (!upperBounds.isEmpty()) { 392 Set<JetType> types = getTypes(upperBounds); 393 JetType intersect = TypeUtils.intersect(typeChecker, types); 394 395 if (intersect == null) return false; 396 397 assignValueTo(unknown, intersect); 398 } 399 else { 400 return false; // No bounds to compute the value from 401 } 402 } 403 } finally { 404 beingComputed.remove(unknown); 405 } 406 } 407 return true; 408 } 409 410 private static Set<JetType> getTypes(Set<TypeValue> lowerBounds) { 411 Set<JetType> types = Sets.newHashSet(); 412 for (TypeValue lowerBound : lowerBounds) { 413 types.add(lowerBound.getType()); 414 } 415 return types; 416 } 417 418 private void transitiveClosure(TypeValue current, Set<TypeValue> visited) { 419 if (!visited.add(current)) { 420 return; 421 } 422 423 for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) { 424 if (upperBound.isKnown()) { 425 continue; 426 } 427 transitiveClosure(upperBound, visited); 428 Set<TypeValue> upperBounds = upperBound.getUpperBounds(); 429 for (TypeValue transitiveBound : upperBounds) { 430 expandSubtypingConstraint(current, transitiveBound); 431 } 432 } 433 } 434 435 private void check(TypeValue typeValue, Solution solution) { 436 if (!typeValue.hasValue()) return; 437 try { 438 JetType resultingType = typeValue.getType(); 439 JetType type = solution.getSubstitutor().substitute(resultingType, Variance.INVARIANT); // TODO 440 for (TypeValue upperBound : typeValue.getUpperBounds()) { 441 JetType boundingType = solution.getSubstitutor().substitute(upperBound.getType(), Variance.INVARIANT); 442 if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO 443 solution.registerError("Constraint violation: " + type + " is not a subtype of " + boundingType); 444 listener.error("Constraint violation: ", type, " :< ", boundingType); 445 } 446 } 447 for (TypeValue lowerBound : typeValue.getLowerBounds()) { 448 JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getType(), Variance.INVARIANT); 449 if (!typeChecker.isSubtypeOf(boundingType, type)) { 450 solution.registerError("Constraint violation: " + boundingType + " is not a subtype of " + type); 451 listener.error("Constraint violation: ", boundingType, " :< ", type); 452 } 453 } 454 } 455 catch (LoopInTypeVariableConstraintsException e) { 456 listener.error("Loop detected"); 457 solution.registerError("[TODO] Loop in constraints"); 458 } 459 } 460 461 private static class Error implements SolutionStatus { 462 463 private final String message; 464 465 private Error(String message) { 466 this.message = message; 467 } 468 469 @Override 470 public boolean isSuccessful() { 471 return false; 472 } 473 474 @Override 475 public String toString() { 476 return message; 477 } 478 } 479 480 public class Solution implements ConstraintSystemSolution { 481 private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitution() { 482 @Override 483 public TypeProjection get(TypeConstructor key) { 484 DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); 485 if (declarationDescriptor instanceof TypeParameterDescriptor) { 486 TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; 487 488 if (!unknownTypes.containsKey(descriptor)) return null; 489 490 JetType value = getValue(descriptor); 491 if (value == null) { 492 return null; 493 } 494 TypeProjection typeProjection = new TypeProjection(value); 495 496 listener.log(descriptor, " |-> ", typeProjection); 497 498 return typeProjection; 499 } 500 return null; 501 } 502 503 @Override 504 public boolean isEmpty() { 505 return false; 506 } 507 508 @Override 509 public String toString() { 510 return unknownTypes.toString(); 511 } 512 }); 513 514 private SolutionStatus status; 515 516 public Solution() { 517 this.status = SolutionStatus.SUCCESS; 518 } 519 520 private Solution registerError(String message) { 521 status = new Error(message); 522 return this; 523 } 524 525 @NotNull 526 @Override 527 public SolutionStatus getStatus() { 528 return status; 529 } 530 531 @Override 532 public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) { 533 TypeValue typeVariable = getTypeVariable(typeParameterDescriptor); 534 return typeVariable.hasValue() ? typeVariable.getType() : null; 535 } 536 537 @NotNull 538 @Override 539 public TypeSubstitutor getSubstitutor() { 540 return typeSubstitutor; 541 } 542 543 } 544 private static final class TypeConstraintBuilderAdapter implements TypingConstraints { 545 private final TypingConstraints delegate; 546 private final ConstraintResolutionListener listener; 547 548 private TypeConstraintBuilderAdapter(TypingConstraints delegate, ConstraintResolutionListener listener) { 549 this.delegate = delegate; 550 this.listener = listener; 551 } 552 553 @Override 554 public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { 555 boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure); 556 if (!result) { 557 listener.error("-- Failed to equate ", a, " and ", b); 558 } 559 return result; 560 } 561 562 @Override 563 public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { 564 boolean result = delegate.assertEqualTypeConstructors(a, b); 565 if (!result) { 566 listener.error("-- Type constructors are not equal: ", a, " and ", b); 567 } 568 return result; 569 } 570 571 @Override 572 public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { 573 boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure); 574 if (!result) { 575 listener.error("-- " + subtype + " can't be a subtype of " + supertype); 576 } 577 return result; 578 } 579 580 @Override 581 public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { 582 boolean result = delegate.noCorrespondingSupertype(subtype, supertype); 583 if (!result) { 584 listener.error("-- " + subtype + " has no supertype corresponding to " + supertype); 585 } 586 return result; 587 } 588 } 589 590}