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.types;
018
019import com.google.common.base.Function;
020import com.google.common.collect.Collections2;
021import com.google.common.collect.Lists;
022import com.google.common.collect.Maps;
023import com.google.common.collect.Sets;
024import com.intellij.util.Processor;
025import org.jetbrains.annotations.NotNull;
026import org.jetbrains.annotations.Nullable;
027import org.jetbrains.jet.lang.descriptors.*;
028import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintResolutionListener;
030import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution;
031import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities;
032import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType;
033import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
034import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
036import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037
038import java.util.*;
039
040public class TypeUtils {
041    public static final JetType NO_EXPECTED_TYPE = new JetType() {
042        @NotNull
043        @Override
044        public TypeConstructor getConstructor() {
045            throw new IllegalStateException();
046        }
047
048        @NotNull
049        @Override
050        public List<TypeProjection> getArguments() {
051            throw new IllegalStateException();
052        }
053
054        @Override
055        public boolean isNullable() {
056            throw new IllegalStateException();
057        }
058
059        @NotNull
060        @Override
061        public JetScope getMemberScope() {
062            throw new IllegalStateException();
063        }
064
065        @Override
066        public List<AnnotationDescriptor> getAnnotations() {
067            throw new IllegalStateException();
068        }
069
070        @Override
071        public String toString() {
072            return "NO_EXPECTED_TYPE";
073        }
074    };
075
076    @NotNull
077    public static JetType makeNullable(@NotNull JetType type) {
078        return makeNullableAsSpecified(type, true);
079    }
080
081    @NotNull
082    public static JetType makeNotNullable(@NotNull JetType type) {
083        return makeNullableAsSpecified(type, false);
084    }
085
086    @NotNull
087    public static JetType makeNullableAsSpecified(@NotNull JetType type, boolean nullable) {
088        if (type.isNullable() == nullable) {
089            return type;
090        }
091        if (ErrorUtils.isErrorType(type)) {
092            return type;
093        }
094        return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), nullable, type.getArguments(), type.getMemberScope());
095    }
096
097    public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
098        return intersect(JetTypeChecker.INSTANCE, Sets.newLinkedHashSet(Lists.newArrayList(typeA, typeB))) == null;
099    }
100
101    @Nullable
102    public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
103        if (types.isEmpty()) {
104            return KotlinBuiltIns.getInstance().getNullableAnyType();
105        }
106
107        if (types.size() == 1) {
108            return types.iterator().next();
109        }
110
111        // Intersection of T1..Tn is an intersection of their non-null versions,
112        //   made nullable is they all were nullable
113        boolean allNullable = true;
114        boolean nothingTypePresent = false;
115        List<JetType> nullabilityStripped = Lists.newArrayList();
116        for (JetType type : types) {
117            nothingTypePresent |= KotlinBuiltIns.getInstance().isNothingOrNullableNothing(type);
118            allNullable &= type.isNullable();
119            nullabilityStripped.add(makeNotNullable(type));
120        }
121        
122        if (nothingTypePresent) {
123            return allNullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType();
124        }
125
126        // Now we remove types that have subtypes in the list
127        List<JetType> resultingTypes = Lists.newArrayList();
128        outer:
129        for (JetType type : nullabilityStripped) {
130            if (!canHaveSubtypes(typeChecker, type)) {
131                for (JetType other : nullabilityStripped) {
132                    // It makes sense to check for subtyping (other <: type), despite that
133                    // type is not supposed to be open, for there're enums
134                    if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
135                        return null;
136                    }
137                }
138                return makeNullableAsSpecified(type, allNullable);
139            }
140            else {
141                for (JetType other : nullabilityStripped) {
142                    if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
143                        continue outer;
144                    }
145
146                }
147            }
148
149            // Don't add type if it is already present, to avoid trivial type intersections in result
150            for (JetType other : resultingTypes) {
151                if (typeChecker.equalTypes(other, type)) {
152                    continue outer;
153                }
154            }
155            resultingTypes.add(type);
156        }
157        
158        if (resultingTypes.size() == 1) {
159            return makeNullableAsSpecified(resultingTypes.get(0), allNullable);
160        }
161
162
163        List<AnnotationDescriptor> noAnnotations = Collections.<AnnotationDescriptor>emptyList();
164        TypeConstructor constructor = new IntersectionTypeConstructor(
165                noAnnotations,
166                resultingTypes);
167
168        JetScope[] scopes = new JetScope[resultingTypes.size()];
169        int i = 0;
170        for (JetType type : resultingTypes) {
171            scopes[i] = type.getMemberScope();
172            i++;
173        }
174
175        return new JetTypeImpl(
176                noAnnotations,
177                constructor,
178                allNullable,
179                Collections.<TypeProjection>emptyList(),
180                new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope
181    }
182
183    private static class TypeUnifier {
184        private static class TypeParameterUsage {
185            private final TypeParameterDescriptor typeParameterDescriptor;
186            private final Variance howTheTypeParameterIsUsed;
187
188            public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
189                this.typeParameterDescriptor = typeParameterDescriptor;
190                this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
191            }
192        }
193
194        public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
195            return unify(type, other);
196        }
197
198        private static boolean unify(JetType withParameters, JetType expected) {
199            ConstraintSystemWithPriorities constraintSystem = new ConstraintSystemWithPriorities(ConstraintResolutionListener.DO_NOTHING);
200            // T -> how T is used
201            final Map<TypeParameterDescriptor, Variance> parameters = Maps.newHashMap();
202            Processor<TypeParameterUsage> processor = new Processor<TypeParameterUsage>() {
203                @Override
204                public boolean process(TypeParameterUsage parameterUsage) {
205                    Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
206                    if (howTheTypeIsUsedBefore == null) {
207                        howTheTypeIsUsedBefore = Variance.INVARIANT;
208                    }
209                    parameters.put(parameterUsage.typeParameterDescriptor,
210                                   parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
211                    return true;
212                }
213            };
214            processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
215            processAllTypeParameters(expected, Variance.INVARIANT, processor);
216            for (Map.Entry<TypeParameterDescriptor, Variance> entry : parameters.entrySet()) {
217                constraintSystem.registerTypeVariable(entry.getKey(), entry.getValue());
218            }
219            constraintSystem.addSubtypingConstraint(ConstraintType.VALUE_ARGUMENT.assertSubtyping(withParameters, expected));
220
221            ConstraintSystemSolution solution = constraintSystem.solve();
222            return solution.getStatus().isSuccessful();
223        }
224
225        private static void processAllTypeParameters(JetType type, Variance howThiTypeIsUsed, Processor<TypeParameterUsage> result) {
226            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
227            if (descriptor instanceof TypeParameterDescriptor) {
228                result.process(new TypeParameterUsage((TypeParameterDescriptor)descriptor, howThiTypeIsUsed));
229            }
230            for (TypeProjection projection : type.getArguments()) {
231                processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
232            }
233        }
234    }
235
236    public static boolean canHaveSubtypes(JetTypeChecker typeChecker, JetType type) {
237        if (type.isNullable()) {
238            return true;
239        }
240        if (!type.getConstructor().isSealed()) {
241            return true;
242        }
243
244        List<TypeParameterDescriptor> parameters = type.getConstructor().getParameters();
245        List<TypeProjection> arguments = type.getArguments();
246        for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
247            TypeParameterDescriptor parameterDescriptor = parameters.get(i);
248            TypeProjection typeProjection = arguments.get(i);
249            Variance projectionKind = typeProjection.getProjectionKind();
250            JetType argument = typeProjection.getType();
251
252            switch (parameterDescriptor.getVariance()) {
253                case INVARIANT:
254                    switch (projectionKind) {
255                        case INVARIANT:
256                            if (lowerThanBound(typeChecker, argument, parameterDescriptor) || canHaveSubtypes(typeChecker, argument)) {
257                                return true;
258                            }
259                            break;
260                        case IN_VARIANCE:
261                            if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
262                                return true;
263                            }
264                            break;
265                        case OUT_VARIANCE:
266                            if (canHaveSubtypes(typeChecker, argument)) {
267                                return true;
268                            }
269                            break;
270                    }
271                    break;
272                case IN_VARIANCE:
273                    if (projectionKind != Variance.OUT_VARIANCE) {
274                        if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
275                            return true;
276                        }
277                    }
278                    else {
279                        if (canHaveSubtypes(typeChecker, argument)) {
280                            return true;
281                        }
282                    }
283                    break;
284                case OUT_VARIANCE:
285                    if (projectionKind != Variance.IN_VARIANCE) {
286                        if (canHaveSubtypes(typeChecker, argument)) {
287                            return true;
288                        }
289                    }
290                    else {
291                        if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
292                            return true;
293                        }
294                    }
295                    break;
296            }
297        }
298        return false;
299    }
300
301    private static boolean lowerThanBound(JetTypeChecker typeChecker, JetType argument, TypeParameterDescriptor parameterDescriptor) {
302        for (JetType bound : parameterDescriptor.getUpperBounds()) {
303            if (typeChecker.isSubtypeOf(argument, bound)) {
304                if (!argument.getConstructor().equals(bound.getConstructor())) {
305                    return true;
306                }
307            }
308        }
309        return false;
310    }
311
312    public static JetType makeNullableIfNeeded(JetType type, boolean nullable) {
313        if (nullable) {
314            return makeNullable(type);
315        }
316        return type;
317    }
318
319    @NotNull
320    public static JetType makeUnsubstitutedType(ClassDescriptor classDescriptor, JetScope unsubstitutedMemberScope) {
321        if (ErrorUtils.isError(classDescriptor)) {
322            return ErrorUtils.createErrorType("Unsubstituted type for " + classDescriptor);
323        }
324        List<TypeProjection> arguments = getDefaultTypeProjections(classDescriptor.getTypeConstructor().getParameters());
325        return new JetTypeImpl(
326                Collections.<AnnotationDescriptor>emptyList(),
327                classDescriptor.getTypeConstructor(),
328                false,
329                arguments,
330                unsubstitutedMemberScope
331        );
332    }
333
334    @NotNull
335    public static List<TypeProjection> getDefaultTypeProjections(List<TypeParameterDescriptor> parameters) {
336        List<TypeProjection> result = new ArrayList<TypeProjection>();
337        for (TypeParameterDescriptor parameterDescriptor : parameters) {
338            result.add(new TypeProjection(parameterDescriptor.getDefaultType()));
339        }
340        return result;
341    }
342
343    @NotNull
344    public static List<JetType> getDefaultTypes(List<TypeParameterDescriptor> parameters) {
345        List<JetType> result = Lists.newArrayList();
346        for (TypeParameterDescriptor parameterDescriptor : parameters) {
347            result.add(parameterDescriptor.getDefaultType());
348        }
349        return result;
350    }
351
352    private static void collectImmediateSupertypes(@NotNull JetType type, @NotNull Collection<JetType> result) {
353        TypeSubstitutor substitutor = TypeSubstitutor.create(type);
354        for (JetType supertype : type.getConstructor().getSupertypes()) {
355            result.add(substitutor.substitute(supertype, Variance.INVARIANT));
356        }
357    }
358
359    @NotNull
360    public static List<JetType> getImmediateSupertypes(@NotNull JetType type) {
361        List<JetType> result = Lists.newArrayList();
362        collectImmediateSupertypes(type, result);
363        return result;
364    }
365
366    private static void collectAllSupertypes(@NotNull JetType type, @NotNull Set<JetType> result) {
367        List<JetType> immediateSupertypes = getImmediateSupertypes(type);
368        result.addAll(immediateSupertypes);
369        for (JetType supertype : immediateSupertypes) {
370            collectAllSupertypes(supertype, result);
371        }
372    }
373
374
375    @NotNull
376    public static Set<JetType> getAllSupertypes(@NotNull JetType type) {
377        // 15 is obtained by experimentation: JDK classes like ArrayList tend to have so many supertypes,
378        // the average number is lower
379        Set<JetType> result = new LinkedHashSet<JetType>(15);
380        collectAllSupertypes(type, result);
381        return result;
382    }
383
384    public static boolean hasNullableLowerBound(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
385        for (JetType bound : typeParameterDescriptor.getLowerBounds()) {
386            if (bound.isNullable()) {
387                return true;
388            }
389        }
390        return false;
391    }
392
393    public static boolean hasNullableSuperType(@NotNull JetType type) {
394        if (type.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor) {
395            // A class/trait cannot have a nullable supertype
396            return false;
397        }
398
399        for (JetType supertype : getImmediateSupertypes(type)) {
400            if (supertype.isNullable()) return true;
401            if (hasNullableSuperType(supertype)) return true;
402        }
403        
404        return false;
405    }
406
407    public static boolean equalClasses(@NotNull JetType type1, @NotNull JetType type2) {
408        DeclarationDescriptor declarationDescriptor1 = type1.getConstructor().getDeclarationDescriptor();
409        if (declarationDescriptor1 == null) return false; // No class, classes are not equal
410        DeclarationDescriptor declarationDescriptor2 = type2.getConstructor().getDeclarationDescriptor();
411        if (declarationDescriptor2 == null) return false; // Class of type1 is not null
412        return declarationDescriptor1.getOriginal().equals(declarationDescriptor2.getOriginal());
413    }
414
415    @Nullable
416    public static ClassDescriptor getClassDescriptor(@NotNull JetType type) {
417        DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
418        if (declarationDescriptor instanceof ClassDescriptor) {
419            return (ClassDescriptor) declarationDescriptor;
420        }
421        return null;
422    }
423
424    @NotNull
425    public static JetType substituteParameters(@NotNull ClassDescriptor clazz, @NotNull List<JetType> actualTypeParameters) {
426        List<TypeParameterDescriptor> clazzTypeParameters = clazz.getTypeConstructor().getParameters();
427
428        if (clazzTypeParameters.size() != actualTypeParameters.size()) {
429            throw new IllegalArgumentException("type parameter counts do not match: " + clazz + ", " + actualTypeParameters);
430        }
431        
432        Map<TypeConstructor, TypeProjection> substitutions = Maps.newHashMap();
433        
434        for (int i = 0; i < clazzTypeParameters.size(); ++i) {
435            TypeConstructor typeConstructor = clazzTypeParameters.get(i).getTypeConstructor();
436            TypeProjection typeProjection = new TypeProjection(actualTypeParameters.get(i));
437            substitutions.put(typeConstructor, typeProjection);
438        }
439        
440        return TypeSubstitutor.create(substitutions).substitute(clazz.getDefaultType(), Variance.INVARIANT);
441    }
442
443    private static void addAllClassDescriptors(@NotNull JetType type, @NotNull Set<ClassDescriptor> set) {
444        ClassDescriptor cd = getClassDescriptor(type);
445        if (cd != null) {
446            set.add(cd);
447        }
448        for (TypeProjection projection : type.getArguments()) {
449            addAllClassDescriptors(projection.getType(), set);
450        }
451    }
452
453    @NotNull
454    public static List<ClassDescriptor> getAllClassDescriptors(@NotNull JetType type) {
455        Set<ClassDescriptor> classDescriptors = new HashSet<ClassDescriptor>();
456        addAllClassDescriptors(type, classDescriptors);
457        return new ArrayList<ClassDescriptor>(classDescriptors);
458    }
459
460    public static boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
461        return JetTypeChecker.INSTANCE.isSubtypeOf(a, b) && JetTypeChecker.INSTANCE.isSubtypeOf(b, a);
462    }
463
464    public static boolean typeConstructorUsedInType(@NotNull TypeConstructor key, @NotNull JetType value) {
465        if (value.getConstructor() == key) return true;
466        for (TypeProjection projection : value.getArguments()) {
467            if (typeConstructorUsedInType(key, projection.getType())) {
468                return true;
469            }
470        }
471        return false;
472    }
473
474    public static boolean dependsOnTypeParameters(@NotNull JetType type, @NotNull Collection<TypeParameterDescriptor> typeParameters) {
475        return dependsOnTypeConstructors(type, Collections2
476                .transform(typeParameters, new Function<TypeParameterDescriptor, TypeConstructor>() {
477                    @Override
478                    public TypeConstructor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) {
479                        assert typeParameterDescriptor != null;
480                        return typeParameterDescriptor.getTypeConstructor();
481                    }
482                }));
483    }
484
485    public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
486        if (typeParameterConstructors.contains(type.getConstructor())) return true;
487        for (TypeProjection typeProjection : type.getArguments()) {
488            if (dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
489                return true;
490            }
491        }
492        return false;
493    }
494
495    public static boolean equalsOrContainsAsArgument(@Nullable JetType type, @NotNull JetType... possibleArgumentTypes) {
496        return equalsOrContainsAsArgument(type, Sets.newHashSet(possibleArgumentTypes));
497    }
498
499    private static boolean equalsOrContainsAsArgument(@Nullable JetType type, @NotNull Set<JetType> possibleArgumentTypes) {
500        if (type == null) return false;
501        if (possibleArgumentTypes.contains(type)) return true;
502        if (type instanceof NamespaceType) return false;
503        for (TypeProjection projection : type.getArguments()) {
504            if (equalsOrContainsAsArgument(projection.getType(), possibleArgumentTypes)) return true;
505        }
506        return false;
507    }
508
509    @NotNull
510    public static String getTypeNameAndStarProjectionsString(@NotNull String name, int size) {
511        StringBuilder builder = new StringBuilder(name);
512        builder.append("<");
513        for (int i = 0; i < size; i++) {
514            builder.append("*");
515            if (i == size - 1) break;
516            builder.append(", ");
517        }
518        builder.append(">");
519
520        return builder.toString();
521    }
522}