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 org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
027    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
028    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
029    import org.jetbrains.jet.lang.types.*;
030    import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
031    import org.jetbrains.jet.lang.types.checker.TypingConstraints;
032    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033    
034    import java.util.List;
035    import java.util.Map;
036    import java.util.Set;
037    
038    import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.*;
039    import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL;
040    import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE;
041    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind;
042    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind.*;
043    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
044    
045    public class ConstraintSystemImpl implements ConstraintSystem {
046    
047        public enum ConstraintKind {
048            SUB_TYPE, EQUAL
049        }
050    
051        private final Map<TypeParameterDescriptor, TypeConstraintsImpl> typeParameterConstraints = Maps.newLinkedHashMap();
052        private final Set<ConstraintPosition> errorConstraintPositions = Sets.newHashSet();
053        private final TypeSubstitutor resultingSubstitutor;
054        private final TypeSubstitutor currentSubstitutor;
055        private boolean hasErrorInConstrainingTypes;
056    
057        @Nullable
058        private ConstraintSystem systemWithoutExpectedTypeConstraint;
059    
060        public ConstraintSystemImpl() {
061            this.resultingSubstitutor = createTypeSubstitutorWithDefaultForUnknownTypeParameter(new TypeProjection(CANT_INFER_TYPE_PARAMETER));
062            this.currentSubstitutor = createTypeSubstitutorWithDefaultForUnknownTypeParameter(new TypeProjection(DONT_CARE));
063        }
064    
065        private TypeSubstitutor createTypeSubstitutorWithDefaultForUnknownTypeParameter(@Nullable final TypeProjection defaultTypeProjection) {
066            return TypeSubstitutor.create(new TypeSubstitution() {
067                @Override
068                public TypeProjection get(TypeConstructor key) {
069                    DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor();
070                    if (declarationDescriptor instanceof TypeParameterDescriptor) {
071                        TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
072    
073                        JetType value = ConstraintsUtil.getValue(getTypeConstraints(descriptor));
074                        if (value != null && !TypeUtils.equalsOrContainsAsArgument(value, DONT_CARE)) {
075                            return new TypeProjection(value);
076                        }
077                        if (typeParameterConstraints.containsKey(descriptor)) {
078                            return defaultTypeProjection;
079                        }
080                    }
081                    return null;
082                }
083    
084                @Override
085                public boolean isEmpty() {
086                    return false;
087                }
088    
089                @Override
090                public String toString() {
091                    return typeParameterConstraints.toString();
092                }
093            });
094        }
095    
096        @Override
097        public boolean hasTypeConstructorMismatch() {
098            return !errorConstraintPositions.isEmpty();
099        }
100    
101        @Override
102        public boolean hasTypeConstructorMismatchAt(@NotNull ConstraintPosition constraintPosition) {
103            return errorConstraintPositions.contains(constraintPosition);
104        }
105    
106        @Override
107        public boolean hasOnlyExpectedTypeMismatch() {
108            if (systemWithoutExpectedTypeConstraint == null) {
109                // the expected type constraint isn't added, there can't be an error with it
110                return false;
111            }
112            if (!isSuccessful() && systemWithoutExpectedTypeConstraint.isSuccessful()) {
113                return true;
114            }
115            if (errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(ConstraintPosition.EXPECTED_TYPE_POSITION)) {
116                // if systemWithoutExpectedTypeConstraint has unknown type parameters, it's not successful,
117                // but there can be expected type mismatch after expected type is added
118                return true;
119            }
120            return false;
121        }
122    
123        @Override
124        public boolean hasErrorInConstrainingTypes() {
125            return hasErrorInConstrainingTypes;
126        }
127    
128        @Override
129        public void registerTypeVariable(@NotNull TypeParameterDescriptor typeVariable, @NotNull Variance positionVariance) {
130            typeParameterConstraints.put(typeVariable, new TypeConstraintsImpl(positionVariance));
131        }
132    
133        @Override
134        @NotNull
135        public ConstraintSystem copy() {
136            return replaceTypeVariables(Functions.<TypeParameterDescriptor>identity(), true);
137        }
138    
139        @NotNull
140        public ConstraintSystem replaceTypeVariables(@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap) {
141            return replaceTypeVariables(typeVariablesMap, false);
142        }
143    
144        @NotNull
145        private ConstraintSystem replaceTypeVariables(
146                @NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap,
147                boolean recreateTypeConstraints
148        ) {
149            ConstraintSystemImpl newConstraintSystem = new ConstraintSystemImpl();
150            for (Map.Entry<TypeParameterDescriptor, TypeConstraintsImpl> entry : typeParameterConstraints.entrySet()) {
151                TypeParameterDescriptor typeParameter = entry.getKey();
152                TypeConstraintsImpl typeConstraints = entry.getValue();
153    
154                TypeParameterDescriptor newTypeParameter = typeVariablesMap.apply(typeParameter);
155                assert newTypeParameter != null;
156                newConstraintSystem.typeParameterConstraints.put(newTypeParameter, recreateTypeConstraints ? typeConstraints.copy() : typeConstraints);
157            }
158            newConstraintSystem.errorConstraintPositions.addAll(errorConstraintPositions);
159            newConstraintSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes;
160            return newConstraintSystem;
161        }
162    
163        @Override
164        public void addSupertypeConstraint(
165                @Nullable JetType constrainingType,
166                @NotNull JetType subjectType,
167                @NotNull ConstraintPosition constraintPosition
168        ) {
169            if (constrainingType != null && noExpectedType(constrainingType)) return;
170    
171            if (constraintPosition == ConstraintPosition.EXPECTED_TYPE_POSITION) {
172                systemWithoutExpectedTypeConstraint = copy();
173            }
174            addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
175        }
176    
177        @Override
178        public void addSubtypeConstraint(
179                @Nullable JetType constrainingType,
180                @NotNull JetType subjectType,
181                @NotNull ConstraintPosition constraintPosition
182        ) {
183            addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition);
184        }
185    
186        private void addConstraint(
187                @NotNull ConstraintKind constraintKind,
188                @Nullable JetType subType,
189                @Nullable JetType superType,
190                @NotNull final ConstraintPosition constraintPosition
191        ) {
192            TypeCheckingProcedure typeCheckingProcedure = new TypeCheckingProcedure(new TypingConstraints() {
193                @Override
194                public boolean assertEqualTypes(
195                        @NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure
196                ) {
197                    doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure);
198                    return true;
199    
200                }
201    
202                @Override
203                public boolean assertEqualTypeConstructors(
204                        @NotNull TypeConstructor a, @NotNull TypeConstructor b
205                ) {
206                    throw new IllegalStateException("'assertEqualTypeConstructors' shouldn't be invoked inside 'isSubtypeOf'");
207                }
208    
209                @Override
210                public boolean assertSubtype(
211                        @NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure
212                ) {
213                    doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure);
214                    return true;
215                }
216    
217                @Override
218                public boolean noCorrespondingSupertype(
219                        @NotNull JetType subtype, @NotNull JetType supertype
220                ) {
221                    errorConstraintPositions.add(constraintPosition);
222                    return true;
223                }
224            });
225            doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure);
226        }
227    
228        private boolean isErrorOrSpecialType(@Nullable JetType type) {
229            if (type == DONT_CARE || type == CANT_INFER_TYPE_PARAMETER) {
230                return true;
231            }
232    
233            if (type == null || ((ErrorUtils.isErrorType(type) && type != PLACEHOLDER_FUNCTION_TYPE))) {
234                hasErrorInConstrainingTypes = true;
235                return true;
236            }
237            return false;
238        }
239    
240        private void doAddConstraint(
241                @NotNull ConstraintKind constraintKind,
242                @Nullable JetType subType,
243                @Nullable JetType superType,
244                @NotNull ConstraintPosition constraintPosition,
245                @NotNull TypeCheckingProcedure typeCheckingProcedure
246        ) {
247    
248            if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return;
249            assert subType != null && superType != null;
250    
251            assert superType != PLACEHOLDER_FUNCTION_TYPE : "The type for " + constraintPosition + " shouldn't be a placeholder for function type";
252    
253            KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance();
254            if (subType == PLACEHOLDER_FUNCTION_TYPE) {
255                if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) {
256                    if (isMyTypeVariable(superType)) {
257                        // a constraint binds type parameter and any function type, so there is no new info and no error
258                        return;
259                    }
260                    errorConstraintPositions.add(constraintPosition);
261                }
262                return;
263            }
264    
265            // todo temporary hack
266            // function literal without declaring receiver type { x -> ... }
267            // can be considered as extension function if one is expected
268            // (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
269            if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
270                subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
271            }
272    
273            // can be equal for the recursive invocations:
274            // fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
275            if (subType.equals(superType)) return;
276    
277            assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) :
278                    "The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType;
279    
280    
281            if (isMyTypeVariable(subType)) {
282                generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND);
283                return;
284            }
285            if (isMyTypeVariable(superType)) {
286                generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND);
287                return;
288            }
289            // if superType is nullable and subType is not nullable, unsafe call error will be generated later,
290            // but constraint system should be solved anyway
291            typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType));
292        }
293    
294        private void generateTypeParameterConstraint(
295                @NotNull JetType parameterType,
296                @NotNull JetType constrainingType,
297                @NotNull BoundKind boundKind
298        ) {
299            TypeConstraintsImpl typeConstraints = getTypeConstraints(parameterType);
300            assert typeConstraints != null : "constraint should be generated only for type variables";
301    
302            if (parameterType.isNullable()) {
303                // For parameter type T constraint T? <: Int? should transform to T <: Int
304                constrainingType = TypeUtils.makeNotNullable(constrainingType);
305            }
306            typeConstraints.addBound(boundKind, constrainingType);
307        }
308    
309        public void processDeclaredBoundConstraints() {
310            for (Map.Entry<TypeParameterDescriptor, TypeConstraintsImpl> entry : typeParameterConstraints.entrySet()) {
311                TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
312                TypeConstraintsImpl typeConstraints = entry.getValue();
313                for (JetType declaredUpperBound : typeParameterDescriptor.getUpperBounds()) {
314                    //todo order matters here
315                    for (JetType lowerOrExactBound : Sets.union(typeConstraints.getLowerBounds(), typeConstraints.getExactBounds())) {
316                        addSubtypeConstraint(lowerOrExactBound, declaredUpperBound, ConstraintPosition.BOUND_CONSTRAINT_POSITION);
317                    }
318                }
319            }
320        }
321    
322        @NotNull
323        @Override
324        public Set<TypeParameterDescriptor> getTypeVariables() {
325            return typeParameterConstraints.keySet();
326        }
327    
328        @Override
329        @Nullable
330        public TypeConstraints getTypeConstraints(@NotNull TypeParameterDescriptor typeVariable) {
331            return typeParameterConstraints.get(typeVariable);
332        }
333    
334        @Nullable
335        private TypeConstraintsImpl getTypeConstraints(@NotNull JetType type) {
336            ClassifierDescriptor parameterDescriptor = type.getConstructor().getDeclarationDescriptor();
337            if (parameterDescriptor instanceof TypeParameterDescriptor) {
338                return typeParameterConstraints.get(parameterDescriptor);
339            }
340            return null;
341        }
342    
343        private boolean isMyTypeVariable(@NotNull JetType type) {
344            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
345            return descriptor instanceof TypeParameterDescriptor && typeParameterConstraints.get(descriptor) != null;
346        }
347    
348        @Override
349        public boolean isSuccessful() {
350            return !hasContradiction() && !hasUnknownParameters();
351        }
352    
353        @Override
354        public boolean hasContradiction() {
355            return hasTypeConstructorMismatch() || hasConflictingConstraints();
356        }
357    
358        @Override
359        public boolean hasConflictingConstraints() {
360            for (TypeParameterDescriptor typeParameter : typeParameterConstraints.keySet()) {
361                TypeConstraints typeConstraints = getTypeConstraints(typeParameter);
362                if (typeConstraints != null && ConstraintsUtil.getValues(typeConstraints).size() > 1) return true;
363            }
364            return false;
365        }
366    
367        @Override
368        public boolean hasUnknownParameters() {
369            for (TypeConstraintsImpl constraints : typeParameterConstraints.values()) {
370                if (constraints.isEmpty()) {
371                    return true;
372                }
373            }
374            return false;
375        }
376    
377        @NotNull
378        @Override
379        public TypeSubstitutor getResultingSubstitutor() {
380            if (hasOnlyExpectedTypeMismatch()) {
381                assert systemWithoutExpectedTypeConstraint != null;
382                return systemWithoutExpectedTypeConstraint.getResultingSubstitutor();
383            }
384            return resultingSubstitutor;
385        }
386    
387        @NotNull
388        @Override
389        public TypeSubstitutor getCurrentSubstitutor() {
390            return currentSubstitutor;
391        }
392    
393        @NotNull
394        public static JetType createCorrespondingExtensionFunctionType(@NotNull JetType functionType, @NotNull JetType receiverType) {
395            assert KotlinBuiltIns.getInstance().isFunctionType(functionType);
396    
397            List<TypeProjection> typeArguments = functionType.getArguments();
398            assert !typeArguments.isEmpty();
399    
400            List<JetType> arguments = Lists.newArrayList();
401            // excluding the last type argument of the function type, which is the return type
402            int index = 0;
403            int lastIndex = typeArguments.size() - 1;
404            for (TypeProjection typeArgument : typeArguments) {
405                if (index < lastIndex) {
406                    arguments.add(typeArgument.getType());
407                }
408                index++;
409            }
410            JetType returnType = typeArguments.get(lastIndex).getType();
411            return KotlinBuiltIns.getInstance().getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType);
412        }
413    }