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.base.Function;
020    import com.google.common.base.Functions;
021    import com.google.common.collect.Lists;
022    import com.google.common.collect.Maps;
023    import com.google.common.collect.Sets;
024    import com.intellij.openapi.util.Condition;
025    import com.intellij.openapi.util.Conditions;
026    import com.intellij.util.containers.ContainerUtil;
027    import org.jetbrains.annotations.NotNull;
028    import org.jetbrains.annotations.Nullable;
029    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
030    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
031    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
032    import org.jetbrains.jet.lang.types.*;
033    import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
034    import org.jetbrains.jet.lang.types.checker.TypingConstraints;
035    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036    
037    import java.util.Collection;
038    import java.util.List;
039    import java.util.Map;
040    import java.util.Set;
041    
042    import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL;
043    import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE;
044    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBoundsImpl.BoundKind.*;
045    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBoundsImpl.Bound;
046    import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_TYPE_PARAMETER;
047    import static org.jetbrains.jet.lang.types.TypeUtils.DONT_CARE;
048    
049    public class ConstraintSystemImpl implements ConstraintSystem {
050    
051        public enum ConstraintKind {
052            SUB_TYPE, EQUAL
053        }
054    
055        private final Map<TypeParameterDescriptor, TypeBoundsImpl> typeParameterBounds = Maps.newLinkedHashMap();
056        private final Set<ConstraintPosition> errorConstraintPositions = Sets.newHashSet();
057        private final TypeSubstitutor resultingSubstitutor;
058        private final TypeSubstitutor currentSubstitutor;
059        private boolean hasErrorInConstrainingTypes;
060    
061        private final ConstraintSystemStatus constraintSystemStatus = new ConstraintSystemStatus() {
062            // for debug ConstraintsUtil.getDebugMessageForStatus might be used
063    
064            @Override
065            public boolean isSuccessful() {
066                return !hasContradiction() && !hasUnknownParameters();
067            }
068    
069            @Override
070            public boolean hasContradiction() {
071                return hasTypeConstructorMismatch() || hasConflictingConstraints();
072            }
073    
074            @Override
075            public boolean hasViolatedUpperBound() {
076                if (isSuccessful()) return false;
077                return getSystemWithoutWeakConstraints().getStatus().isSuccessful();
078            }
079    
080            @Override
081            public boolean hasConflictingConstraints() {
082                for (TypeBoundsImpl typeBounds : typeParameterBounds.values()) {
083                    if (typeBounds.getValues().size() > 1) return true;
084                }
085                return false;
086            }
087    
088            @Override
089            public boolean hasUnknownParameters() {
090                for (TypeBoundsImpl typeBounds : typeParameterBounds.values()) {
091                    if (typeBounds.isEmpty()) {
092                        return true;
093                    }
094                }
095                return false;
096            }
097    
098            @Override
099            public boolean hasTypeConstructorMismatch() {
100                return !errorConstraintPositions.isEmpty();
101            }
102    
103            @Override
104            public boolean hasTypeConstructorMismatchAt(@NotNull ConstraintPosition constraintPosition) {
105                return errorConstraintPositions.contains(constraintPosition);
106            }
107    
108            @Override
109            public boolean hasOnlyErrorsFromPosition(ConstraintPosition constraintPosition) {
110                if (isSuccessful()) return false;
111                ConstraintSystem systemWithoutConstraintsFromPosition = filterConstraintsOut(constraintPosition);
112                if (systemWithoutConstraintsFromPosition.getStatus().isSuccessful()) {
113                    return true;
114                }
115                if (errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(constraintPosition)) {
116                    // e.g. if systemWithoutConstraintsFromPosition has unknown type parameters, it's not successful
117                    return true;
118                }
119                return false;
120            }
121    
122            @Override
123            public boolean hasErrorInConstrainingTypes() {
124                return hasErrorInConstrainingTypes;
125            }
126        };
127    
128        public ConstraintSystemImpl() {
129            this.resultingSubstitutor = createTypeSubstitutorWithDefaultForUnknownTypeParameter(new TypeProjectionImpl(
130                    TypeUtils.CANT_INFER_TYPE_PARAMETER));
131            this.currentSubstitutor = createTypeSubstitutorWithDefaultForUnknownTypeParameter(new TypeProjectionImpl(TypeUtils.DONT_CARE));
132        }
133    
134        private TypeSubstitutor createTypeSubstitutorWithDefaultForUnknownTypeParameter(@Nullable final TypeProjection defaultTypeProjection) {
135            return TypeSubstitutor.create(new TypeSubstitution() {
136                @Override
137                public TypeProjection get(TypeConstructor key) {
138                    DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor();
139                    if (declarationDescriptor instanceof TypeParameterDescriptor) {
140                        TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
141    
142                        if (typeParameterBounds.containsKey(descriptor)) {
143                            JetType value = getTypeBounds(descriptor).getValue();
144                            if (value != null && !TypeUtils.equalsOrContainsAsArgument(value, TypeUtils.DONT_CARE)) {
145                                return new TypeProjectionImpl(value);
146                            }
147                            return defaultTypeProjection;
148                        }
149                    }
150                    return null;
151                }
152    
153                @Override
154                public boolean isEmpty() {
155                    return false;
156                }
157    
158                @Override
159                public String toString() {
160                    return typeParameterBounds.toString();
161                }
162            });
163        }
164    
165        @NotNull
166        @Override
167        public ConstraintSystemStatus getStatus() {
168            return constraintSystemStatus;
169        }
170    
171        @Override
172        public void registerTypeVariables(@NotNull Map<TypeParameterDescriptor, Variance> typeVariables) {
173            for (Map.Entry<TypeParameterDescriptor, Variance> entry : typeVariables.entrySet()) {
174                TypeParameterDescriptor typeVariable = entry.getKey();
175                Variance positionVariance = entry.getValue();
176                typeParameterBounds.put(typeVariable, new TypeBoundsImpl(typeVariable, positionVariance));
177            }
178            TypeSubstitutor constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterBounds.keySet(), DONT_CARE);
179            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
180                TypeParameterDescriptor typeVariable = entry.getKey();
181                TypeBoundsImpl typeBounds = entry.getValue();
182    
183                for (JetType declaredUpperBound : typeVariable.getUpperBounds()) {
184                    if (KotlinBuiltIns.getInstance().getNullableAnyType().equals(declaredUpperBound)) continue; //todo remove this line (?)
185                    JetType substitutedBound = constantSubstitutor.substitute(declaredUpperBound, Variance.INVARIANT);
186                    if (substitutedBound != null) {
187                        typeBounds.addBound(UPPER_BOUND, substitutedBound, ConstraintPosition.getTypeBoundPosition(typeVariable.getIndex()));
188                    }
189                }
190            }
191        }
192    
193        @Override
194        @NotNull
195        public ConstraintSystem copy() {
196            return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
197                                                     new Function<TypeBoundsImpl, TypeBoundsImpl>() {
198                                                         @Override
199                                                         public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
200                                                             return typeBounds.copy();
201                                                         }
202                                                     },
203                                                     Conditions.<ConstraintPosition>alwaysTrue());
204        }
205    
206        @NotNull
207        public ConstraintSystem substituteTypeVariables(@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap) {
208            return createNewConstraintSystemFromThis(typeVariablesMap,
209                                                     // type bounds are proper types and don't contain other variables
210                                                     Functions.<TypeBoundsImpl>identity(),
211                                                     Conditions.<ConstraintPosition>alwaysTrue());
212        }
213    
214        @NotNull
215        public ConstraintSystem filterConstraintsOut(@NotNull ConstraintPosition... excludePositions) {
216            final Set<ConstraintPosition> positions = Sets.newHashSet(excludePositions);
217            return filterConstraints(new Condition<ConstraintPosition>() {
218                @Override
219                public boolean value(ConstraintPosition constraintPosition) {
220                    return !positions.contains(constraintPosition);
221                }
222            });
223        }
224    
225        @NotNull
226        public ConstraintSystem filterConstraints(@NotNull final Condition<ConstraintPosition> condition) {
227            return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
228                                                     new Function<TypeBoundsImpl, TypeBoundsImpl>() {
229                                                         @Override
230                                                         public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
231                                                             return typeBounds.filter(condition);
232                                                         }
233                                                     },
234                                                     condition);
235        }
236    
237        @NotNull
238        public ConstraintSystem getSystemWithoutWeakConstraints() {
239            return filterConstraints(new Condition<ConstraintPosition>() {
240                @Override
241                public boolean value(ConstraintPosition constraintPosition) {
242                    // 'isStrong' for compound means 'has some strong constraints'
243                    // but for testing absence of weak constraints we need 'has only strong constraints' here
244                    if (constraintPosition instanceof ConstraintPosition.CompoundConstraintPosition) {
245                        ConstraintPosition.CompoundConstraintPosition position =
246                                (ConstraintPosition.CompoundConstraintPosition) constraintPosition;
247                        return position.consistsOfOnlyStrongConstraints();
248                    }
249                    return constraintPosition.isStrong();
250                }
251            });
252        }
253    
254        @NotNull
255        private ConstraintSystem createNewConstraintSystemFromThis(
256                @NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> substituteTypeVariable,
257                @NotNull Function<TypeBoundsImpl, TypeBoundsImpl> replaceTypeBounds,
258                @NotNull Condition<ConstraintPosition> filterConstraintPosition
259        ) {
260            ConstraintSystemImpl newSystem = new ConstraintSystemImpl();
261            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
262                TypeParameterDescriptor typeParameter = entry.getKey();
263                TypeBoundsImpl typeBounds = entry.getValue();
264    
265                TypeParameterDescriptor newTypeParameter = substituteTypeVariable.apply(typeParameter);
266                assert newTypeParameter != null;
267                newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds.apply(typeBounds));
268            }
269            newSystem.errorConstraintPositions.addAll(ContainerUtil.filter(errorConstraintPositions, filterConstraintPosition));
270            //todo if 'filterConstraintPosition' is not trivial, it's incorrect to just copy 'hasErrorInConstrainingTypes'
271            newSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes;
272            return newSystem;
273        }
274    
275        @Override
276        public void addSupertypeConstraint(
277                @Nullable JetType constrainingType,
278                @NotNull JetType subjectType,
279                @NotNull ConstraintPosition constraintPosition
280        ) {
281            if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return;
282    
283            addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
284        }
285    
286        @Override
287        public void addSubtypeConstraint(
288                @Nullable JetType constrainingType,
289                @NotNull JetType subjectType,
290                @NotNull ConstraintPosition constraintPosition
291        ) {
292            addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition);
293        }
294    
295        private void addConstraint(
296                @NotNull ConstraintKind constraintKind,
297                @Nullable JetType subType,
298                @Nullable JetType superType,
299                @NotNull final ConstraintPosition constraintPosition
300        ) {
301            TypeCheckingProcedure typeCheckingProcedure = new TypeCheckingProcedure(new TypingConstraints() {
302                @Override
303                public boolean assertEqualTypes(
304                        @NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure
305                ) {
306                    doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure);
307                    return true;
308    
309                }
310    
311                @Override
312                public boolean assertEqualTypeConstructors(
313                        @NotNull TypeConstructor a, @NotNull TypeConstructor b
314                ) {
315                    throw new IllegalStateException("'assertEqualTypeConstructors' shouldn't be invoked inside 'isSubtypeOf'");
316                }
317    
318                @Override
319                public boolean assertSubtype(
320                        @NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure
321                ) {
322                    doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure);
323                    return true;
324                }
325    
326                @Override
327                public boolean noCorrespondingSupertype(
328                        @NotNull JetType subtype, @NotNull JetType supertype
329                ) {
330                    errorConstraintPositions.add(constraintPosition);
331                    return true;
332                }
333            });
334            doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure);
335        }
336    
337        private boolean isErrorOrSpecialType(@Nullable JetType type) {
338            if (type == DONT_CARE || type == CANT_INFER_TYPE_PARAMETER) {
339                return true;
340            }
341    
342            if (type == null || (type.isError() && type != TypeUtils.PLACEHOLDER_FUNCTION_TYPE)) {
343                hasErrorInConstrainingTypes = true;
344                return true;
345            }
346            return false;
347        }
348    
349        private void doAddConstraint(
350                @NotNull ConstraintKind constraintKind,
351                @Nullable JetType subType,
352                @Nullable JetType superType,
353                @NotNull ConstraintPosition constraintPosition,
354                @NotNull TypeCheckingProcedure typeCheckingProcedure
355        ) {
356    
357            if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return;
358            assert subType != null && superType != null;
359    
360            assert superType != TypeUtils.PLACEHOLDER_FUNCTION_TYPE : "The type for " + constraintPosition + " shouldn't be a placeholder for function type";
361    
362            KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance();
363            if (subType == TypeUtils.PLACEHOLDER_FUNCTION_TYPE) {
364                if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) {
365                    if (isMyTypeVariable(superType)) {
366                        // a constraint binds type parameter and any function type, so there is no new info and no error
367                        return;
368                    }
369                    errorConstraintPositions.add(constraintPosition);
370                }
371                return;
372            }
373    
374            // todo temporary hack
375            // function literal without declaring receiver type { x -> ... }
376            // can be considered as extension function if one is expected
377            // (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
378            if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
379                subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
380            }
381    
382            // can be equal for the recursive invocations:
383            // fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
384            if (subType.equals(superType)) return;
385    
386            assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) :
387                    "The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType;
388    
389    
390            if (isMyTypeVariable(subType)) {
391                generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND, constraintPosition);
392                return;
393            }
394            if (isMyTypeVariable(superType)) {
395                generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND, constraintPosition);
396                return;
397            }
398            // if superType is nullable and subType is not nullable, unsafe call error will be generated later,
399            // but constraint system should be solved anyway
400            typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType));
401        }
402    
403        private void generateTypeParameterConstraint(
404                @NotNull JetType parameterType,
405                @NotNull JetType constrainingType,
406                @NotNull TypeBoundsImpl.BoundKind boundKind,
407                @NotNull ConstraintPosition constraintPosition
408        ) {
409            TypeBoundsImpl typeBounds = getTypeBounds(parameterType);
410            assert typeBounds != null : "constraint should be generated only for type variables";
411    
412            if (!parameterType.isNullable() || !constrainingType.isNullable()) {
413                typeBounds.addBound(boundKind, constrainingType, constraintPosition);
414                return;
415            }
416            // For parameter type T:
417            // constraint T? =  Int? should transform to T >: Int and T <: Int?
418            // constraint T? >: Int? should transform to T >: Int
419            JetType notNullConstrainingType = TypeUtils.makeNotNullable(constrainingType);
420            if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
421                typeBounds.addBound(LOWER_BOUND, notNullConstrainingType, constraintPosition);
422            }
423            // constraint T? <: Int? should transform to T <: Int?
424            if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
425                typeBounds.addBound(UPPER_BOUND, constrainingType, constraintPosition);
426            }
427        }
428    
429        public void processDeclaredBoundConstraints() {
430            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
431                TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
432                TypeBoundsImpl typeBounds = entry.getValue();
433                for (JetType declaredUpperBound : typeParameterDescriptor.getUpperBounds()) {
434                    //todo order matters here
435                    Collection<Bound> bounds = Lists.newArrayList(typeBounds.getBounds());
436                    for (Bound bound : bounds) {
437                        if (bound.kind == LOWER_BOUND || bound.kind == EXACT_BOUND) {
438                            ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
439                                    ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
440                            addSubtypeConstraint(bound.type, declaredUpperBound, position);
441                        }
442                    }
443                    ClassifierDescriptor declarationDescriptor = declaredUpperBound.getConstructor().getDeclarationDescriptor();
444                    if (declarationDescriptor instanceof TypeParameterDescriptor && typeParameterBounds.containsKey(declarationDescriptor)) {
445                        TypeBoundsImpl typeBoundsForUpperBound = typeParameterBounds.get(declarationDescriptor);
446                        for (Bound bound : typeBoundsForUpperBound.getBounds()) {
447                            if (bound.kind == UPPER_BOUND || bound.kind == EXACT_BOUND) {
448                                ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
449                                        ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
450                                typeBounds.addBound(UPPER_BOUND, bound.type, position);
451                            }
452                        }
453                    }
454                }
455            }
456        }
457    
458        @NotNull
459        @Override
460        public Set<TypeParameterDescriptor> getTypeVariables() {
461            return typeParameterBounds.keySet();
462        }
463    
464        @Override
465        @NotNull
466        public TypeBounds getTypeBounds(@NotNull TypeParameterDescriptor typeVariable) {
467            TypeBoundsImpl typeBounds = typeParameterBounds.get(typeVariable);
468            assert typeBounds != null : "TypeParameterDescriptor is not a type variable for constraint system: " + typeVariable;
469            return typeBounds;
470        }
471    
472        @Nullable
473        private TypeBoundsImpl getTypeBounds(@NotNull JetType type) {
474            ClassifierDescriptor parameterDescriptor = type.getConstructor().getDeclarationDescriptor();
475            if (parameterDescriptor instanceof TypeParameterDescriptor) {
476                return typeParameterBounds.get(parameterDescriptor);
477            }
478            return null;
479        }
480    
481        private boolean isMyTypeVariable(@NotNull JetType type) {
482            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
483            return descriptor instanceof TypeParameterDescriptor && typeParameterBounds.get(descriptor) != null;
484        }
485    
486        @NotNull
487        @Override
488        public TypeSubstitutor getResultingSubstitutor() {
489            return resultingSubstitutor;
490        }
491    
492        @NotNull
493        @Override
494        public TypeSubstitutor getCurrentSubstitutor() {
495            return currentSubstitutor;
496        }
497    
498        @NotNull
499        public static JetType createCorrespondingExtensionFunctionType(@NotNull JetType functionType, @NotNull JetType receiverType) {
500            assert KotlinBuiltIns.getInstance().isFunctionType(functionType);
501    
502            List<TypeProjection> typeArguments = functionType.getArguments();
503            assert !typeArguments.isEmpty();
504    
505            List<JetType> arguments = Lists.newArrayList();
506            // excluding the last type argument of the function type, which is the return type
507            int index = 0;
508            int lastIndex = typeArguments.size() - 1;
509            for (TypeProjection typeArgument : typeArguments) {
510                if (index < lastIndex) {
511                    arguments.add(typeArgument.getType());
512                }
513                index++;
514            }
515            JetType returnType = typeArguments.get(lastIndex).getType();
516            return KotlinBuiltIns.getInstance().getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType);
517        }
518    }