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    }