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}