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.types;
018    
019    import com.google.common.base.Function;
020    import com.google.common.collect.Collections2;
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.util.Processor;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintResolutionListener;
030    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution;
031    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities;
032    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType;
033    import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
036    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037    
038    import java.util.*;
039    
040    public 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    }