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.collect.Lists;
020import com.google.common.collect.Sets;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
024import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
025import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026import org.jetbrains.jet.lang.resolve.scopes.JetScope;
027import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
028import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
029import org.jetbrains.jet.utils.DFS;
030
031import java.util.*;
032
033import static org.jetbrains.jet.lang.types.Variance.*;
034
035public class CommonSupertypes {
036    @NotNull
037    public static JetType commonSupertype(@NotNull Collection<JetType> types) {
038        Collection<JetType> typeSet = new HashSet<JetType>(types);
039        assert !typeSet.isEmpty();
040
041        // If any of the types is nullable, the result must be nullable
042        // This also removed Nothing and Nothing? because they are subtypes of everything else
043        boolean nullable = false;
044        for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
045            JetType type = iterator.next();
046            assert type != null;
047            if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(type)) {
048                iterator.remove();
049            }
050            if (ErrorUtils.isErrorType(type)) {
051                return ErrorUtils.createErrorType("Supertype of error type " + type);
052            }
053            nullable |= type.isNullable();
054        }
055
056        // Everything deleted => it's Nothing or Nothing?
057        if (typeSet.isEmpty()) {
058            // TODO : attributes
059            return nullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType();
060        }
061
062        if (typeSet.size() == 1) {
063            return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
064        }
065
066        // constructor of the supertype -> all of its instantiations occurring as supertypes
067        Map<TypeConstructor, Set<JetType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
068        while (commonSupertypes.size() > 1) {
069            Set<JetType> merge = new HashSet<JetType>();
070            for (Set<JetType> supertypes : commonSupertypes.values()) {
071                merge.addAll(supertypes);
072            }
073            commonSupertypes = computeCommonRawSupertypes(merge);
074        }
075        assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
076
077        // constructor of the supertype -> all of its instantiations occurring as supertypes
078        Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
079
080        // Reconstructing type arguments if possible
081        JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
082        return TypeUtils.makeNullableIfNeeded(result, nullable);
083    }
084
085    // Raw supertypes are superclasses w/o type arguments
086    // @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
087    @NotNull
088    private static Map<TypeConstructor, Set<JetType>> computeCommonRawSupertypes(@NotNull Collection<JetType> types) {
089        assert !types.isEmpty();
090
091        Map<TypeConstructor, Set<JetType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<JetType>>();
092        Set<TypeConstructor> commonSuperclasses = null;
093
094        List<TypeConstructor> order = null;
095        for (JetType type : types) {
096            Set<TypeConstructor> visited = Sets.newHashSet();
097            order = topologicallySortSuperclassesAndRecordAllInstances(type, constructorToAllInstances, visited);
098
099            if (commonSuperclasses == null) {
100                commonSuperclasses = visited;
101            }
102            else {
103                commonSuperclasses.retainAll(visited);
104            }
105        }
106        assert order != null;
107
108        Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
109        Map<TypeConstructor, Set<JetType>> result = new HashMap<TypeConstructor, Set<JetType>>();
110        for (TypeConstructor superConstructor : order) {
111            if (!commonSuperclasses.contains(superConstructor)) {
112                continue;
113            }
114
115            if (!notSource.contains(superConstructor)) {
116                result.put(superConstructor, constructorToAllInstances.get(superConstructor));
117                markAll(superConstructor, notSource);
118            }
119        }
120
121        return result;
122    }
123
124    private static List<TypeConstructor> topologicallySortSuperclassesAndRecordAllInstances(
125            @NotNull JetType type,
126            @NotNull final Map<TypeConstructor, Set<JetType>> constructorToAllInstances,
127            @NotNull final Set<TypeConstructor> visited
128    ) {
129        return DFS.dfs(
130                Collections.singletonList(type),
131                new DFS.Neighbors<JetType>() {
132                    @NotNull
133                    @Override
134                    public Iterable<JetType> getNeighbors(JetType current) {
135                        TypeSubstitutor substitutor = TypeSubstitutor.create(current);
136                        List<JetType> result = Lists.newArrayList();
137                        for (JetType supertype : current.getConstructor().getSupertypes()) {
138                            if (visited.contains(supertype.getConstructor())) {
139                                continue;
140                            }
141                            result.add(substitutor.safeSubstitute(supertype, INVARIANT));
142                        }
143                        return result;
144                    }
145                },
146                new DFS.Visited<JetType>() {
147                    @Override
148                    public boolean checkAndMarkVisited(JetType current) {
149                        return visited.add(current.getConstructor());
150                    }
151                },
152                new DFS.NodeHandlerWithListResult<JetType, TypeConstructor>() {
153                    @Override
154                    public void beforeChildren(JetType current) {
155                        TypeConstructor constructor = current.getConstructor();
156
157                        Set<JetType> instances = constructorToAllInstances.get(constructor);
158                        if (instances == null) {
159                            instances = new HashSet<JetType>();
160                            constructorToAllInstances.put(constructor, instances);
161                        }
162                        instances.add(current);
163                    }
164
165                    @Override
166                    public void afterChildren(JetType current) {
167                        result.addFirst(current.getConstructor());
168                    }
169                }
170        );
171    }
172
173    // constructor - type constructor of a supertype to be instantiated
174    // types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
175    @NotNull
176    private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
177        // we assume that all the given types are applications of the same type constructor
178
179        assert !types.isEmpty();
180
181        if (types.size() == 1) {
182            return types.iterator().next();
183        }
184
185        List<TypeParameterDescriptor> parameters = constructor.getParameters();
186        List<TypeProjection> newProjections = new ArrayList<TypeProjection>();
187        for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
188            TypeParameterDescriptor parameterDescriptor = parameters.get(i);
189            Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
190            for (JetType type : types) {
191                typeProjections.add(type.getArguments().get(i));
192            }
193            newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
194        }
195
196        boolean nullable = false;
197        for (JetType type : types) {
198            nullable |= type.isNullable();
199        }
200
201        // TODO : attributes?
202        JetScope newScope = KotlinBuiltIns.getInstance().STUB;
203        DeclarationDescriptor declarationDescriptor = constructor.getDeclarationDescriptor();
204        if (declarationDescriptor instanceof ClassDescriptor) {
205            newScope = ((ClassDescriptor) declarationDescriptor).getMemberScope(newProjections);
206        }
207        return new JetTypeImpl(Collections.<AnnotationDescriptor>emptyList(), constructor, nullable, newProjections, newScope);
208    }
209
210    @NotNull
211    private static TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set<TypeProjection> typeProjections) {
212        if (typeProjections.size() == 1) {
213            return typeProjections.iterator().next();
214        }
215
216        Set<JetType> ins = new HashSet<JetType>();
217        Set<JetType> outs = new HashSet<JetType>();
218
219        Variance variance = parameterDescriptor.getVariance();
220        switch (variance) {
221            case INVARIANT:
222                // Nothing
223                break;
224            case IN_VARIANCE:
225                outs = null;
226                break;
227            case OUT_VARIANCE:
228                ins = null;
229                break;
230        }
231
232        for (TypeProjection projection : typeProjections) {
233            Variance projectionKind = projection.getProjectionKind();
234            if (projectionKind.allowsInPosition()) {
235                if (ins != null) {
236                    ins.add(projection.getType());
237                }
238            }
239            else {
240                ins = null;
241            }
242
243            if (projectionKind.allowsOutPosition()) {
244                if (outs != null) {
245                    outs.add(projection.getType());
246                }
247            }
248            else {
249                outs = null;
250            }
251        }
252
253        if (ins != null) {
254            JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, ins);
255            if (intersection == null) {
256                if (outs != null) {
257                    return new TypeProjection(OUT_VARIANCE, commonSupertype(outs));
258                }
259                return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
260            }
261            Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
262            return new TypeProjection(projectionKind, intersection);
263        }
264        else if (outs != null) {
265            Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
266            return new TypeProjection(projectionKind, commonSupertype(outs));
267        }
268        else {
269            Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
270            return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
271        }
272    }
273
274    private static void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
275        markerSet.add(typeConstructor);
276        for (JetType type : typeConstructor.getSupertypes()) {
277            markAll(type.getConstructor(), markerSet);
278        }
279    }
280}