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.
/*
* Copyright 2010-2015 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.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl;
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import java.util.*;
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.registerTypeVariables;
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
public class TypeIntersector {
private final KotlinBuiltIns builtIns;
public TypeIntersector(@NotNull KotlinBuiltIns builtIns) {
this.builtIns = builtIns;
}
public boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) {
return intersect(JetTypeChecker.DEFAULT, new LinkedHashSet(Arrays.asList(typeA, typeB))) == null;
}
//TODO: usages of this method should be removed
@Nullable
public static JetType intersectTypes(
@NotNull KotlinBuiltIns builtIns,
@NotNull JetTypeChecker typeChecker,
@NotNull Set types
) {
return new TypeIntersector(builtIns).intersect(typeChecker, types);
}
@Nullable
public JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set types) {
if (types.isEmpty()) {
return builtIns.getNullableAnyType();
}
if (types.size() == 1) {
return types.iterator().next();
}
// Intersection of T1..Tn is an intersection of their non-null versions,
// made nullable is they all were nullable
boolean allNullable = true;
boolean nothingTypePresent = false;
List nullabilityStripped = new ArrayList(types.size());
for (JetType type : types) {
if (type.isError()) continue;
nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type);
allNullable &= type.isMarkedNullable();
nullabilityStripped.add(TypeUtils.makeNotNullable(type));
}
if (nothingTypePresent) {
return allNullable ? builtIns.getNullableNothingType() : builtIns.getNothingType();
}
if (nullabilityStripped.isEmpty()) {
// All types were errors
return ErrorUtils.createErrorType("Intersection of errors types: " + types);
}
// Now we remove types that have subtypes in the list
List resultingTypes = new ArrayList();
outer:
for (JetType type : nullabilityStripped) {
if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
for (JetType other : nullabilityStripped) {
// It makes sense to check for subtyping (other <: type), despite that
// type is not supposed to be open, for there're enums
if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
return null;
}
}
return TypeUtils.makeNullableAsSpecified(type, allNullable);
}
else {
for (JetType other : nullabilityStripped) {
if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
continue outer;
}
}
}
// Don't add type if it is already present, to avoid trivial type intersections in result
for (JetType other : resultingTypes) {
if (typeChecker.equalTypes(other, type)) {
continue outer;
}
}
resultingTypes.add(type);
}
if (resultingTypes.isEmpty()) {
// If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
// most likely, this is because they are all semantically interchangeable (e.g. List! and List),
// in that case, we can safely select the best representative out of that set and return it
// TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
// TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
JetType bestRepresentative = TypesPackage.singleBestRepresentative(nullabilityStripped);
if (bestRepresentative == null) {
throw new AssertionError("Empty intersection for types " + types);
}
return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
}
if (resultingTypes.size() == 1) {
return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
}
TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.EMPTY, resultingTypes);
JetScope[] scopes = new JetScope[resultingTypes.size()];
int i = 0;
for (JetType type : resultingTypes) {
scopes[i] = type.getMemberScope();
i++;
}
return JetTypeImpl.create(
Annotations.EMPTY,
constructor,
allNullable,
Collections.emptyList(),
new IntersectionScope(constructor, scopes)
);
}
// TODO : check intersectibility, don't use a chanied scope
private static class IntersectionScope extends ChainedScope {
public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull JetScope[] scopes) {
super(null, "member scope for intersection type " + constructor, scopes);
}
@NotNull
@Override
public DeclarationDescriptor getContainingDeclaration() {
throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
}
}
private static class TypeUnifier {
private static class TypeParameterUsage {
private final TypeParameterDescriptor typeParameterDescriptor;
private final Variance howTheTypeParameterIsUsed;
public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
this.typeParameterDescriptor = typeParameterDescriptor;
this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
}
}
public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) {
return unify(type, other);
}
private static boolean unify(JetType withParameters, JetType expected) {
// T -> how T is used
final Map parameters = new HashMap();
Function1 processor = new Function1() {
@Override
public Unit invoke(TypeParameterUsage parameterUsage) {
Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
if (howTheTypeIsUsedBefore == null) {
howTheTypeIsUsedBefore = Variance.INVARIANT;
}
parameters.put(parameterUsage.typeParameterDescriptor,
parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
return Unit.INSTANCE$;
}
};
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
processAllTypeParameters(expected, Variance.INVARIANT, processor);
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
registerTypeVariables(constraintSystem, parameters);
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
return constraintSystem.getStatus().isSuccessful();
}
private static void processAllTypeParameters(JetType type, Variance howThisTypeIsUsed, Function1 result) {
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof TypeParameterDescriptor) {
result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
}
for (TypeProjection projection : type.getArguments()) {
if (projection.isStarProjection()) continue;
processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
}
}
}
}