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