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