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