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.resolve.calls.inference;
018
019 import com.google.common.collect.Maps;
020 import com.google.common.collect.Sets;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
024 import org.jetbrains.jet.lang.types.*;
025 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
026 import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
027 import org.jetbrains.jet.lang.types.checker.TypingConstraints;
028
029 import java.util.*;
030
031 import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.PARAMETER_BOUND;
032
033 public 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 }