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 kotlin.Function1;
028    import org.jetbrains.annotations.NotNull;
029    import org.jetbrains.annotations.Nullable;
030    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
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.TypeBounds.Bound;
045    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.*;
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                @NotNull Function1<TypeParameterDescriptor, TypeProjection> getDefaultTypeProjection
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.containsSpecialType(value, TypeUtils.DONT_CARE)) {
138                    typeProjection = new TypeProjectionImpl(value);
139                }
140                else {
141                    typeProjection = getDefaultTypeProjection.invoke(typeParameter);
142                }
143                substitutionContext.put(typeParameter, typeProjection);
144            }
145            return substitutionContext;
146        }
147    
148        private TypeSubstitutor replaceUninferredBy(@NotNull Function1<TypeParameterDescriptor, TypeProjection> getDefaultValue) {
149            return TypeUtils.makeSubstitutorForTypeParametersMap(getParameterToInferredValueMap(typeParameterBounds, getDefaultValue));
150        }
151    
152        private TypeSubstitutor replaceUninferredBy(@NotNull final JetType defaultValue) {
153            return replaceUninferredBy(
154                    new Function1<TypeParameterDescriptor, TypeProjection>() {
155                        @Override
156                        public TypeProjection invoke(TypeParameterDescriptor descriptor) {
157                            return new TypeProjectionImpl(defaultValue);
158                        }
159                    }
160            );
161        }
162    
163        private TypeSubstitutor replaceUninferredBySpecialErrorType() {
164            return replaceUninferredBy(
165                    new Function1<TypeParameterDescriptor, TypeProjection>() {
166                        @Override
167                        public TypeProjection invoke(TypeParameterDescriptor descriptor) {
168                            return new TypeProjectionImpl(ErrorUtils.createUninferredParameterType(descriptor));
169                        }
170                    }
171            );
172        }
173    
174        @NotNull
175        @Override
176        public ConstraintSystemStatus getStatus() {
177            return constraintSystemStatus;
178        }
179    
180        @Override
181        public void registerTypeVariables(@NotNull Map<TypeParameterDescriptor, Variance> typeVariables) {
182            for (Map.Entry<TypeParameterDescriptor, Variance> entry : typeVariables.entrySet()) {
183                TypeParameterDescriptor typeVariable = entry.getKey();
184                Variance positionVariance = entry.getValue();
185                typeParameterBounds.put(typeVariable, new TypeBoundsImpl(typeVariable, positionVariance));
186            }
187            TypeSubstitutor constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterBounds.keySet(), DONT_CARE);
188            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
189                TypeParameterDescriptor typeVariable = entry.getKey();
190                TypeBoundsImpl typeBounds = entry.getValue();
191    
192                for (JetType declaredUpperBound : typeVariable.getUpperBounds()) {
193                    if (KotlinBuiltIns.getInstance().getNullableAnyType().equals(declaredUpperBound)) continue; //todo remove this line (?)
194                    JetType substitutedBound = constantSubstitutor.substitute(declaredUpperBound, Variance.INVARIANT);
195                    if (substitutedBound != null) {
196                        typeBounds.addBound(UPPER_BOUND, substitutedBound, ConstraintPosition.getTypeBoundPosition(typeVariable.getIndex()));
197                    }
198                }
199            }
200        }
201    
202        @Override
203        @NotNull
204        public ConstraintSystem copy() {
205            return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
206                                                     new Function<TypeBoundsImpl, TypeBoundsImpl>() {
207                                                         @Override
208                                                         public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
209                                                             return typeBounds.copy();
210                                                         }
211                                                     },
212                                                     Conditions.<ConstraintPosition>alwaysTrue());
213        }
214    
215        @NotNull
216        public ConstraintSystem substituteTypeVariables(@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap) {
217            return createNewConstraintSystemFromThis(typeVariablesMap,
218                                                     // type bounds are proper types and don't contain other variables
219                                                     Functions.<TypeBoundsImpl>identity(),
220                                                     Conditions.<ConstraintPosition>alwaysTrue());
221        }
222    
223        @NotNull
224        public ConstraintSystem filterConstraintsOut(@NotNull ConstraintPosition... excludePositions) {
225            final Set<ConstraintPosition> positions = Sets.newHashSet(excludePositions);
226            return filterConstraints(new Condition<ConstraintPosition>() {
227                @Override
228                public boolean value(ConstraintPosition constraintPosition) {
229                    return !positions.contains(constraintPosition);
230                }
231            });
232        }
233    
234        @NotNull
235        public ConstraintSystem filterConstraints(@NotNull final Condition<ConstraintPosition> condition) {
236            return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
237                                                     new Function<TypeBoundsImpl, TypeBoundsImpl>() {
238                                                         @Override
239                                                         public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
240                                                             return typeBounds.filter(condition);
241                                                         }
242                                                     },
243                                                     condition);
244        }
245    
246        @NotNull
247        public ConstraintSystem getSystemWithoutWeakConstraints() {
248            return filterConstraints(new Condition<ConstraintPosition>() {
249                @Override
250                public boolean value(ConstraintPosition constraintPosition) {
251                    // 'isStrong' for compound means 'has some strong constraints'
252                    // but for testing absence of weak constraints we need 'has only strong constraints' here
253                    if (constraintPosition instanceof ConstraintPosition.CompoundConstraintPosition) {
254                        ConstraintPosition.CompoundConstraintPosition position =
255                                (ConstraintPosition.CompoundConstraintPosition) constraintPosition;
256                        return position.consistsOfOnlyStrongConstraints();
257                    }
258                    return constraintPosition.isStrong();
259                }
260            });
261        }
262    
263        @NotNull
264        private ConstraintSystem createNewConstraintSystemFromThis(
265                @NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> substituteTypeVariable,
266                @NotNull Function<TypeBoundsImpl, TypeBoundsImpl> replaceTypeBounds,
267                @NotNull Condition<ConstraintPosition> filterConstraintPosition
268        ) {
269            ConstraintSystemImpl newSystem = new ConstraintSystemImpl();
270            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
271                TypeParameterDescriptor typeParameter = entry.getKey();
272                TypeBoundsImpl typeBounds = entry.getValue();
273    
274                TypeParameterDescriptor newTypeParameter = substituteTypeVariable.apply(typeParameter);
275                assert newTypeParameter != null;
276                newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds.apply(typeBounds));
277            }
278            newSystem.errorConstraintPositions.addAll(ContainerUtil.filter(errorConstraintPositions, filterConstraintPosition));
279            //todo if 'filterConstraintPosition' is not trivial, it's incorrect to just copy 'hasErrorInConstrainingTypes'
280            newSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes;
281            return newSystem;
282        }
283    
284        @Override
285        public void addSupertypeConstraint(
286                @Nullable JetType constrainingType,
287                @NotNull JetType subjectType,
288                @NotNull ConstraintPosition constraintPosition
289        ) {
290            if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return;
291    
292            addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
293        }
294    
295        @Override
296        public void addSubtypeConstraint(
297                @Nullable JetType constrainingType,
298                @NotNull JetType subjectType,
299                @NotNull ConstraintPosition constraintPosition
300        ) {
301            addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition);
302        }
303    
304        private void addConstraint(
305                @NotNull ConstraintKind constraintKind,
306                @Nullable JetType subType,
307                @Nullable JetType superType,
308                @NotNull final ConstraintPosition constraintPosition
309        ) {
310            TypeCheckingProcedure typeCheckingProcedure = new TypeCheckingProcedure(new TypingConstraints() {
311                @Override
312                public boolean assertEqualTypes(
313                        @NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure
314                ) {
315                    doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure);
316                    return true;
317    
318                }
319    
320                @Override
321                public boolean assertEqualTypeConstructors(
322                        @NotNull TypeConstructor a, @NotNull TypeConstructor b
323                ) {
324                    return a.equals(b);
325                }
326    
327                @Override
328                public boolean assertSubtype(
329                        @NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure
330                ) {
331                    doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure);
332                    return true;
333                }
334    
335                @Override
336                public boolean noCorrespondingSupertype(
337                        @NotNull JetType subtype, @NotNull JetType supertype
338                ) {
339                    errorConstraintPositions.add(constraintPosition);
340                    return true;
341                }
342            });
343            doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure);
344        }
345    
346        private boolean isErrorOrSpecialType(@Nullable JetType type) {
347            if (type == DONT_CARE || ErrorUtils.isUninferredParameter(type)) {
348                return true;
349            }
350    
351            if (type == null || (type.isError() && type != TypeUtils.PLACEHOLDER_FUNCTION_TYPE)) {
352                hasErrorInConstrainingTypes = true;
353                return true;
354            }
355            return false;
356        }
357    
358        private void doAddConstraint(
359                @NotNull ConstraintKind constraintKind,
360                @Nullable JetType subType,
361                @Nullable JetType superType,
362                @NotNull ConstraintPosition constraintPosition,
363                @NotNull TypeCheckingProcedure typeCheckingProcedure
364        ) {
365    
366            if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return;
367            assert subType != null && superType != null;
368    
369            assert superType != TypeUtils.PLACEHOLDER_FUNCTION_TYPE : "The type for " + constraintPosition + " shouldn't be a placeholder for function type";
370    
371            KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance();
372            if (subType == TypeUtils.PLACEHOLDER_FUNCTION_TYPE) {
373                if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) {
374                    if (isMyTypeVariable(superType)) {
375                        // a constraint binds type parameter and any function type, so there is no new info and no error
376                        return;
377                    }
378                    errorConstraintPositions.add(constraintPosition);
379                }
380                return;
381            }
382    
383            // todo temporary hack
384            // function literal without declaring receiver type { x -> ... }
385            // can be considered as extension function if one is expected
386            // (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
387            if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
388                subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
389            }
390    
391            // can be equal for the recursive invocations:
392            // fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
393            if (subType.equals(superType)) return;
394    
395            assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) :
396                    "The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType;
397    
398    
399            if (isMyTypeVariable(subType)) {
400                generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND, constraintPosition);
401                return;
402            }
403            if (isMyTypeVariable(superType)) {
404                generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND, constraintPosition);
405                return;
406            }
407            // if superType is nullable and subType is not nullable, unsafe call error will be generated later,
408            // but constraint system should be solved anyway
409            typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType));
410        }
411    
412        private void generateTypeParameterConstraint(
413                @NotNull JetType parameterType,
414                @NotNull JetType constrainingType,
415                @NotNull TypeBoundsImpl.BoundKind boundKind,
416                @NotNull ConstraintPosition constraintPosition
417        ) {
418            TypeBoundsImpl typeBounds = getTypeBounds(parameterType);
419            assert typeBounds != null : "constraint should be generated only for type variables";
420    
421            if (!parameterType.isNullable() || !constrainingType.isNullable()) {
422                typeBounds.addBound(boundKind, constrainingType, constraintPosition);
423                return;
424            }
425            // For parameter type T:
426            // constraint T? =  Int? should transform to T >: Int and T <: Int?
427            // constraint T? >: Int? should transform to T >: Int
428            JetType notNullConstrainingType = TypeUtils.makeNotNullable(constrainingType);
429            if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
430                typeBounds.addBound(LOWER_BOUND, notNullConstrainingType, constraintPosition);
431            }
432            // constraint T? <: Int? should transform to T <: Int?
433            if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
434                typeBounds.addBound(UPPER_BOUND, constrainingType, constraintPosition);
435            }
436        }
437    
438        public void processDeclaredBoundConstraints() {
439            for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
440                TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
441                TypeBoundsImpl typeBounds = entry.getValue();
442                for (JetType declaredUpperBound : typeParameterDescriptor.getUpperBounds()) {
443                    //todo order matters here
444                    Collection<Bound> bounds = Lists.newArrayList(typeBounds.getBounds());
445                    for (Bound bound : bounds) {
446                        if (bound.kind == LOWER_BOUND || bound.kind == EXACT_BOUND) {
447                            ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
448                                    ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
449                            addSubtypeConstraint(bound.type, declaredUpperBound, position);
450                        }
451                    }
452                    ClassifierDescriptor declarationDescriptor = declaredUpperBound.getConstructor().getDeclarationDescriptor();
453                    if (declarationDescriptor instanceof TypeParameterDescriptor && typeParameterBounds.containsKey(declarationDescriptor)) {
454                        TypeBoundsImpl typeBoundsForUpperBound = typeParameterBounds.get(declarationDescriptor);
455                        for (Bound bound : typeBoundsForUpperBound.getBounds()) {
456                            if (bound.kind == UPPER_BOUND || bound.kind == EXACT_BOUND) {
457                                ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
458                                        ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
459                                typeBounds.addBound(UPPER_BOUND, bound.type, position);
460                            }
461                        }
462                    }
463                }
464            }
465        }
466    
467        @NotNull
468        @Override
469        public Set<TypeParameterDescriptor> getTypeVariables() {
470            return typeParameterBounds.keySet();
471        }
472    
473        @Override
474        @NotNull
475        public TypeBounds getTypeBounds(@NotNull TypeParameterDescriptor typeVariable) {
476            TypeBoundsImpl typeBounds = typeParameterBounds.get(typeVariable);
477            assert typeBounds != null : "TypeParameterDescriptor is not a type variable for constraint system: " + typeVariable;
478            return typeBounds;
479        }
480    
481        @Nullable
482        private TypeBoundsImpl getTypeBounds(@NotNull JetType type) {
483            ClassifierDescriptor parameterDescriptor = type.getConstructor().getDeclarationDescriptor();
484            if (parameterDescriptor instanceof TypeParameterDescriptor) {
485                return typeParameterBounds.get(parameterDescriptor);
486            }
487            return null;
488        }
489    
490        private boolean isMyTypeVariable(@NotNull JetType type) {
491            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
492            return descriptor instanceof TypeParameterDescriptor && typeParameterBounds.get(descriptor) != null;
493        }
494    
495        @NotNull
496        @Override
497        public TypeSubstitutor getResultingSubstitutor() {
498            return replaceUninferredBySpecialErrorType();
499        }
500    
501        @NotNull
502        @Override
503        public TypeSubstitutor getCurrentSubstitutor() {
504            return replaceUninferredBy(TypeUtils.DONT_CARE);
505        }
506    
507        @NotNull
508        public static JetType createCorrespondingExtensionFunctionType(@NotNull JetType functionType, @NotNull JetType receiverType) {
509            assert KotlinBuiltIns.getInstance().isFunctionType(functionType);
510    
511            List<TypeProjection> typeArguments = functionType.getArguments();
512            assert !typeArguments.isEmpty();
513    
514            List<JetType> arguments = Lists.newArrayList();
515            // excluding the last type argument of the function type, which is the return type
516            int index = 0;
517            int lastIndex = typeArguments.size() - 1;
518            for (TypeProjection typeArgument : typeArguments) {
519                if (index < lastIndex) {
520                    arguments.add(typeArgument.getType());
521                }
522                index++;
523            }
524            JetType returnType = typeArguments.get(lastIndex).getType();
525            return KotlinBuiltIns.getInstance().getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType);
526        }
527    }