Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.types.CommonSupertypes Maven / Gradle / Ivy
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import org.jetbrains.kotlin.utils.DFS;
import java.util.*;
import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE;
import static org.jetbrains.kotlin.types.Variance.OUT_VARIANCE;
public class CommonSupertypes {
@Nullable
public static KotlinType commonSupertypeForNonDenotableTypes(@NotNull Collection types) {
if (types.isEmpty()) return null;
if (types.size() == 1) {
KotlinType type = types.iterator().next();
if (type.getConstructor() instanceof IntersectionTypeConstructor) {
return commonSupertypeForNonDenotableTypes(type.getConstructor().getSupertypes());
}
}
return commonSupertype(types);
}
@NotNull
public static KotlinType commonSupertype(@NotNull Collection types) {
if (types.size() == 1) return types.iterator().next();
// Recursion should not be significantly deeper than the deepest type in question
// It can be slightly deeper, though: e.g. when initial types are simple, but their supertypes are complex
return findCommonSupertype(types, 0, maxDepth(types) + 3);
}
private static int maxDepth(@NotNull Collection types) {
int max = 0;
for (KotlinType type : types) {
int depth = depth(type);
if (max < depth) {
max = depth;
}
}
return max;
}
private static int depth(@NotNull KotlinType type) {
return 1 + maxDepth(CollectionsKt.map(type.getArguments(), projection -> {
if (projection.isStarProjection()) {
// any type is good enough for depth here
return type.getConstructor().getBuiltIns().getAnyType();
}
return projection.getType();
}));
}
@NotNull
private static KotlinType findCommonSupertype(@NotNull Collection types, int recursionDepth, int maxDepth) {
assert recursionDepth <= maxDepth : "Recursion depth exceeded: " + recursionDepth + " > " + maxDepth + " for types " + types;
boolean hasFlexible = false;
List upper = new ArrayList<>(types.size());
List lower = new ArrayList<>(types.size());
for (KotlinType type : types) {
UnwrappedType unwrappedType = type.unwrap();
if (unwrappedType instanceof FlexibleType) {
if (DynamicTypesKt.isDynamic(unwrappedType)) {
return unwrappedType;
}
hasFlexible = true;
FlexibleType flexibleType = (FlexibleType) unwrappedType;
upper.add(flexibleType.getUpperBound());
lower.add(flexibleType.getLowerBound());
}
else {
SimpleType simpleType = (SimpleType) unwrappedType;
upper.add(simpleType);
lower.add(simpleType);
}
}
if (!hasFlexible) return commonSuperTypeForInflexible(upper, recursionDepth, maxDepth);
return KotlinTypeFactory.flexibleType( // mixing different factories is not supported
commonSuperTypeForInflexible(lower, recursionDepth, maxDepth),
commonSuperTypeForInflexible(upper, recursionDepth, maxDepth)
);
}
@NotNull
private static SimpleType commonSuperTypeForInflexible(@NotNull Collection types, int recursionDepth, int maxDepth) {
assert !types.isEmpty();
Collection typeSet = new HashSet<>(types);
// If any of the types is nullable, the result must be nullable
// This also removed Nothing and Nothing? because they are subtypes of everything else
boolean nullable = false;
for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) {
KotlinType type = iterator.next();
assert type != null;
assert !FlexibleTypesKt.isFlexible(type) : "Flexible type " + type + " passed to commonSuperTypeForInflexible";
if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
iterator.remove();
}
if (KotlinTypeKt.isError(type)) {
return ErrorUtils.createErrorType("Supertype of error type " + type);
}
nullable |= type.isMarkedNullable();
}
// Everything deleted => it's Nothing or Nothing?
if (typeSet.isEmpty()) {
// TODO : attributes
KotlinBuiltIns builtIns = types.iterator().next().getConstructor().getBuiltIns();
return nullable ? builtIns.getNullableNothingType() : builtIns.getNothingType();
}
if (typeSet.size() == 1) {
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
}
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map> commonSupertypes = computeCommonRawSupertypes(typeSet);
while (commonSupertypes.size() > 1) {
Set merge = new HashSet<>();
for (Set supertypes : commonSupertypes.values()) {
merge.addAll(supertypes);
}
commonSupertypes = computeCommonRawSupertypes(merge);
}
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map.Entry> entry = commonSupertypes.entrySet().iterator().next();
// Reconstructing type arguments if possible
SimpleType result = computeSupertypeProjections(entry.getKey(), entry.getValue(), recursionDepth, maxDepth);
return TypeUtils.makeNullableIfNeeded(result, nullable);
}
// Raw supertypes are superclasses w/o type arguments
// @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
@NotNull
private static Map> computeCommonRawSupertypes(@NotNull Collection types) {
assert !types.isEmpty();
Map> constructorToAllInstances = new HashMap<>();
Set commonSuperclasses = null;
List order = null;
for (SimpleType type : types) {
Set visited = new HashSet<>();
order = topologicallySortSuperclassesAndRecordAllInstances(type, constructorToAllInstances, visited);
if (commonSuperclasses == null) {
commonSuperclasses = visited;
}
else {
commonSuperclasses.retainAll(visited);
}
}
assert order != null;
Set notSource = new HashSet<>();
Map> result = new HashMap<>();
for (TypeConstructor superConstructor : order) {
if (!commonSuperclasses.contains(superConstructor)) {
continue;
}
if (!notSource.contains(superConstructor)) {
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
markAll(superConstructor, notSource);
}
}
return result;
}
// constructor - type constructor of a supertype to be instantiated
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
@NotNull
private static SimpleType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types, int recursionDepth, int maxDepth) {
// we assume that all the given types are applications of the same type constructor
assert !types.isEmpty();
if (types.size() == 1) {
return types.iterator().next();
}
List parameters = constructor.getParameters();
List newProjections = new ArrayList<>(parameters.size());
for (TypeParameterDescriptor parameterDescriptor : parameters) {
Set typeProjections = new HashSet<>();
for (KotlinType type : types) {
typeProjections.add(type.getArguments().get(parameterDescriptor.getIndex()));
}
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections, recursionDepth, maxDepth));
}
boolean nullable = false;
for (KotlinType type : types) {
nullable |= type.isMarkedNullable();
}
ClassifierDescriptor classifier = constructor.getDeclarationDescriptor();
MemberScope newScope;
if (classifier instanceof ClassDescriptor) {
newScope = ((ClassDescriptor) classifier).getMemberScope(newProjections);
}
else if (classifier instanceof TypeParameterDescriptor) {
newScope = classifier.getDefaultType().getMemberScope();
}
else {
newScope = ErrorUtils.createErrorScope("A scope for common supertype which is not a normal classifier", true);
}
return KotlinTypeFactory.simpleType(Annotations.Companion.getEMPTY(), constructor, newProjections, nullable, newScope);
}
@NotNull
private static TypeProjection computeSupertypeProjection(
@NotNull TypeParameterDescriptor parameterDescriptor,
@NotNull Set typeProjections,
int recursionDepth, int maxDepth
) {
TypeProjection singleBestProjection = FlexibleTypesKt.singleBestRepresentative(typeProjections);
if (singleBestProjection != null) {
return singleBestProjection;
}
if (recursionDepth >= maxDepth) {
// If recursion is too deep, we cut it by taking as an ultimate supertype argument
// Example: class A : Base; class B : Base, commonSuperType(A, B) = Base<*>
return TypeUtils.makeStarProjection(parameterDescriptor);
}
Set ins = new HashSet<>();
Set outs = new HashSet<>();
Variance variance = parameterDescriptor.getVariance();
switch (variance) {
case INVARIANT:
// Nothing
break;
case IN_VARIANCE:
outs = null;
break;
case OUT_VARIANCE:
ins = null;
break;
}
for (TypeProjection projection : typeProjections) {
Variance projectionKind = projection.getProjectionKind();
if (projectionKind.getAllowsInPosition()) {
if (ins != null) {
ins.add(projection.getType());
}
}
else {
ins = null;
}
if (projectionKind.getAllowsOutPosition()) {
if (outs != null) {
outs.add(projection.getType());
}
}
else {
outs = null;
}
}
if (outs != null) {
assert !outs.isEmpty() : "Out projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
KotlinType superType = findCommonSupertype(outs, recursionDepth + 1, maxDepth);
for (KotlinType upperBound: parameterDescriptor.getUpperBounds()) {
if (!TypeUtilsKt.isSubtypeOf(superType, upperBound)) {
return new StarProjectionImpl(parameterDescriptor);
}
}
return new TypeProjectionImpl(projectionKind, superType);
}
if (ins != null) {
assert !ins.isEmpty() : "In projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, ins);
if (intersection == null) {
return TypeUtils.makeStarProjection(parameterDescriptor);
}
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
return new TypeProjectionImpl(projectionKind, intersection);
}
else {
return TypeUtils.makeStarProjection(parameterDescriptor);
}
}
private static void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) {
markerSet.add(typeConstructor);
for (KotlinType type : typeConstructor.getSupertypes()) {
markAll(type.getConstructor(), markerSet);
}
}
@NotNull
public static List topologicallySortSuperclassesAndRecordAllInstances(
@NotNull SimpleType type,
@NotNull Map> constructorToAllInstances,
@NotNull Set visited
) {
return DFS.dfs(
Collections.singletonList(type),
current -> {
TypeSubstitutor substitutor = TypeSubstitutor.create(current);
Collection supertypes = current.getConstructor().getSupertypes();
List result = new ArrayList<>(supertypes.size());
for (KotlinType supertype : supertypes) {
if (visited.contains(supertype.getConstructor())) {
continue;
}
result.add(FlexibleTypesKt.lowerIfFlexible(substitutor.safeSubstitute(supertype, Variance.INVARIANT)));
}
return result;
},
current -> visited.add(current.getConstructor()),
new DFS.NodeHandlerWithListResult() {
@Override
public boolean beforeChildren(SimpleType current) {
Set instances =
constructorToAllInstances.computeIfAbsent(current.getConstructor(), k -> new HashSet<>());
instances.add(current);
return true;
}
@Override
public void afterChildren(SimpleType current) {
result.addFirst(current.getConstructor());
}
}
);
}
}