
org.jetbrains.jet.lang.resolve.OverridingUtil Maven / Gradle / Ivy
/*
* Copyright 2010-2014 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.jet.lang.resolve;
import com.google.common.base.Predicate;
import com.google.common.collect.*;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Function1;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.PropertyAccessorDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.PropertyDescriptorImpl;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.CONFLICT;
import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
public class OverridingUtil {
private static final List EXTERNAL_CONDITIONS =
ContainerUtil.collect(ServiceLoader.load(
ExternalOverridabilityCondition.class,
ExternalOverridabilityCondition.class.getClassLoader()).iterator()
);
private OverridingUtil() {
}
@NotNull
public static OverrideCompatibilityInfo isOverridableBy(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
if (superDescriptor instanceof FunctionDescriptor) {
if (!(subDescriptor instanceof FunctionDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch();
}
else if (superDescriptor instanceof PropertyDescriptor) {
if (!(subDescriptor instanceof PropertyDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch();
}
else {
throw new IllegalArgumentException("This type of CallableDescriptor cannot be checked for overridability: " + superDescriptor);
}
// TODO: check outside of this method
if (!superDescriptor.getName().equals(subDescriptor.getName())) {
return OverrideCompatibilityInfo.nameMismatch();
}
return isOverridableByImpl(superDescriptor, subDescriptor, true);
}
private static List compiledValueParameters(CallableDescriptor callableDescriptor) {
ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
ArrayList parameters = new ArrayList();
if (receiverParameter != null) {
parameters.add(receiverParameter.getType());
}
for (ValueParameterDescriptor valueParameterDescriptor : callableDescriptor.getValueParameters()) {
parameters.add(valueParameterDescriptor.getType());
}
return parameters;
}
/**
* @param forOverride true for override, false for overload
*/
static OverrideCompatibilityInfo isOverridableByImpl(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor, boolean forOverride) {
// TODO : Visibility
if ((superDescriptor.getReceiverParameter() == null) != (subDescriptor.getReceiverParameter() == null)) {
return OverrideCompatibilityInfo.receiverPresenceMismatch();
}
if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
return OverrideCompatibilityInfo.valueParameterNumberMismatch();
}
List superValueParameters = compiledValueParameters(superDescriptor);
List subValueParameters = compiledValueParameters(subDescriptor);
if (forOverride) {
if (superDescriptor.getTypeParameters().size() != subDescriptor.getTypeParameters().size()) {
for (int i = 0; i < superValueParameters.size(); ++i) {
JetType superValueParameterType = getUpperBound(superValueParameters.get(i));
JetType subValueParameterType = getUpperBound(subValueParameters.get(i));
// TODO: compare erasure
if (!JetTypeChecker.INSTANCE.equalTypes(superValueParameterType, subValueParameterType)) {
return OverrideCompatibilityInfo.typeParameterNumberMismatch();
}
}
return OverrideCompatibilityInfo.valueParameterTypeMismatch(null, null, OverrideCompatibilityInfo.Result.CONFLICT);
}
}
if (forOverride) {
List superTypeParameters = superDescriptor.getTypeParameters();
List subTypeParameters = subDescriptor.getTypeParameters();
BiMap axioms = HashBiMap.create();
for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
axioms.put(superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor());
}
for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
if (!JetTypeChecker.INSTANCE.equalTypes(superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), axioms)) {
return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter);
}
}
for (int i = 0, unsubstitutedValueParametersSize = superValueParameters.size(); i < unsubstitutedValueParametersSize; i++) {
JetType superValueParameter = superValueParameters.get(i);
JetType subValueParameter = subValueParameters.get(i);
boolean bothErrors = superValueParameter.isError() && subValueParameter.isError();
if (!bothErrors && !JetTypeChecker.INSTANCE.equalTypes(superValueParameter, subValueParameter, axioms)) {
return OverrideCompatibilityInfo.valueParameterTypeMismatch(superValueParameter, subValueParameter, OverrideCompatibilityInfo.Result.INCOMPATIBLE);
}
}
for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
if (!externalCondition.isOverridable(superDescriptor, subDescriptor)) {
return OverrideCompatibilityInfo.externalConditionFailed(externalCondition.getClass());
}
}
}
else {
for (int i = 0; i < superValueParameters.size(); ++i) {
JetType superValueParameterType = getUpperBound(superValueParameters.get(i));
JetType subValueParameterType = getUpperBound(subValueParameters.get(i));
// TODO: compare erasure
if (!JetTypeChecker.INSTANCE.equalTypes(superValueParameterType, subValueParameterType)) {
return OverrideCompatibilityInfo.valueParameterTypeMismatch(superValueParameterType, subValueParameterType, OverrideCompatibilityInfo.Result.INCOMPATIBLE);
}
}
return OverrideCompatibilityInfo.success();
}
// TODO : Default values, varargs etc
return OverrideCompatibilityInfo.success();
}
private static JetType getUpperBound(JetType type) {
if (type.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor) {
return type;
}
else if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
return ((TypeParameterDescriptor) type.getConstructor().getDeclarationDescriptor()).getUpperBoundsAsType();
}
else {
throw new IllegalStateException("unknown type constructor: " + type.getConstructor().getClass().getName());
}
}
public static void bindOverride(CallableMemberDescriptor fromCurrent, CallableMemberDescriptor fromSupertype) {
fromCurrent.addOverriddenDescriptor(fromSupertype);
for (ValueParameterDescriptor parameterFromCurrent : fromCurrent.getValueParameters()) {
assert parameterFromCurrent.getIndex() < fromSupertype.getValueParameters().size()
: "An override relation between functions implies that they have the same number of value parameters";
ValueParameterDescriptor parameterFromSupertype = fromSupertype.getValueParameters().get(parameterFromCurrent.getIndex());
parameterFromCurrent.addOverriddenDescriptor(parameterFromSupertype);
}
}
public static void generateOverridesInFunctionGroup(
@SuppressWarnings("UnusedParameters")
@NotNull Name name, //DO NOT DELETE THIS PARAMETER: needed to make sure all descriptors have the same name
@NotNull Collection extends CallableMemberDescriptor> membersFromSupertypes,
@NotNull Collection extends CallableMemberDescriptor> membersFromCurrent,
@NotNull ClassDescriptor current,
@NotNull DescriptorSink sink
) {
Collection notOverridden = Sets.newLinkedHashSet(membersFromSupertypes);
for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
Collection bound =
extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink);
notOverridden.removeAll(bound);
}
createAndBindFakeOverrides(current, notOverridden, sink);
}
private static Collection extractAndBindOverridesForMember(
@NotNull CallableMemberDescriptor fromCurrent,
@NotNull Collection extends CallableMemberDescriptor> descriptorsFromSuper,
@NotNull ClassDescriptor current,
@NotNull DescriptorSink sink
) {
Collection bound = Lists.newArrayList();
for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
OverrideCompatibilityInfo.Result result = isOverridableBy(fromSupertype, fromCurrent).getResult();
boolean isVisible = Visibilities.isVisible(fromSupertype, current);
switch (result) {
case OVERRIDABLE:
if (isVisible) {
bindOverride(fromCurrent, fromSupertype);
}
bound.add(fromSupertype);
break;
case CONFLICT:
if (isVisible) {
sink.conflict(fromSupertype, fromCurrent);
}
bound.add(fromSupertype);
break;
case INCOMPATIBLE:
break;
}
}
return bound;
}
private static void createAndBindFakeOverrides(
@NotNull ClassDescriptor current,
@NotNull Collection notOverridden,
@NotNull DescriptorSink sink
) {
Queue fromSuperQueue = new LinkedList(notOverridden);
while (!fromSuperQueue.isEmpty()) {
CallableMemberDescriptor notOverriddenFromSuper = VisibilityUtil.findMemberWithMaxVisibility(fromSuperQueue);
Collection overridables =
extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, sink);
createAndBindFakeOverride(overridables, current, sink);
}
}
private static boolean isMoreSpecific(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) {
if (a instanceof SimpleFunctionDescriptor) {
assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass();
JetType aReturnType = a.getReturnType();
assert aReturnType != null;
JetType bReturnType = b.getReturnType();
assert bReturnType != null;
return JetTypeChecker.INSTANCE.isSubtypeOf(aReturnType, bReturnType);
}
if (a instanceof PropertyDescriptor) {
assert b instanceof PropertyDescriptor : "b is " + b.getClass();
if (((PropertyDescriptor) a).isVar() || ((PropertyDescriptor) b).isVar()) {
return ((PropertyDescriptor) a).isVar();
}
// both vals
return JetTypeChecker.INSTANCE.isSubtypeOf(((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType());
}
throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
}
private static CallableMemberDescriptor selectMostSpecificMemberFromSuper(@NotNull Collection overridables) {
CallableMemberDescriptor result = null;
for (CallableMemberDescriptor overridable : overridables) {
if (result == null || isMoreSpecific(overridable, result)) {
result = overridable;
}
}
return result;
}
private static void createAndBindFakeOverride(
@NotNull Collection overridables,
@NotNull ClassDescriptor current,
@NotNull DescriptorSink sink
) {
Collection visibleOverridables = filterVisibleFakeOverrides(current, overridables);
Modality modality = getMinimalModality(visibleOverridables);
boolean allInvisible = visibleOverridables.isEmpty();
Collection effectiveOverridden = allInvisible ? overridables : visibleOverridables;
Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
CallableMemberDescriptor mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden);
CallableMemberDescriptor fakeOverride =
mostSpecific.copy(current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
for (CallableMemberDescriptor descriptor : effectiveOverridden) {
bindOverride(fakeOverride, descriptor);
}
sink.addToScope(fakeOverride);
}
@NotNull
private static Modality getMinimalModality(@NotNull Collection descriptors) {
Modality modality = Modality.ABSTRACT;
for (CallableMemberDescriptor descriptor : descriptors) {
if (descriptor.getModality().compareTo(modality) < 0) {
modality = descriptor.getModality();
}
}
return modality;
}
@NotNull
private static Collection filterVisibleFakeOverrides(
@NotNull final ClassDescriptor current,
@NotNull Collection toFilter
) {
return Collections2.filter(toFilter, new Predicate() {
@Override
public boolean apply(@Nullable CallableMemberDescriptor descriptor) {
//nested class could capture private member, so check for private visibility added
return descriptor != null &&
descriptor.getVisibility() != Visibilities.PRIVATE &&
Visibilities.isVisible(descriptor, current);
}
});
}
@NotNull
private static Collection extractMembersOverridableInBothWays(
@NotNull CallableMemberDescriptor overrider,
@NotNull Queue extractFrom,
@NotNull DescriptorSink sink
) {
Collection overridable = Lists.newArrayList();
overridable.add(overrider);
for (Iterator iterator = extractFrom.iterator(); iterator.hasNext(); ) {
CallableMemberDescriptor candidate = iterator.next();
if (overrider == candidate) {
iterator.remove();
continue;
}
OverrideCompatibilityInfo.Result result1 = isOverridableBy(candidate, overrider).getResult();
OverrideCompatibilityInfo.Result result2 = isOverridableBy(overrider, candidate).getResult();
if (result1 == OVERRIDABLE && result2 == OVERRIDABLE) {
overridable.add(candidate);
iterator.remove();
}
else if (result1 == CONFLICT || result2 == CONFLICT) {
sink.conflict(overrider, candidate);
iterator.remove();
}
}
return overridable;
}
public static void resolveUnknownVisibilityForMember(
@NotNull CallableMemberDescriptor memberDescriptor,
@NotNull Function1 cannotInferVisibility
) {
for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
if (descriptor.getVisibility() == Visibilities.INHERITED) {
resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
}
}
if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
return;
}
Visibility visibilityToInherit = computeVisibilityToInherit(memberDescriptor, cannotInferVisibility);
if (memberDescriptor instanceof PropertyDescriptorImpl) {
((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
for (PropertyAccessorDescriptor accessor : ((PropertyDescriptor) memberDescriptor).getAccessors()) {
resolveUnknownVisibilityForMember(accessor, cannotInferVisibility);
}
}
else if (memberDescriptor instanceof FunctionDescriptorImpl) {
((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
}
else {
assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
}
}
@NotNull
private static Visibility computeVisibilityToInherit(
@NotNull CallableMemberDescriptor memberDescriptor,
@NotNull Function1 cannotInferVisibility
) {
Visibility maxVisibility = findMaxVisibility(memberDescriptor.getOverriddenDescriptors());
if (maxVisibility == null) {
cannotInferVisibility.invoke(memberDescriptor);
return Visibilities.PUBLIC;
}
if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
return maxVisibility;
}
return maxVisibility.normalize();
}
@Nullable
private static Visibility findMaxVisibility(@NotNull Collection extends CallableMemberDescriptor> descriptors) {
if (descriptors.isEmpty()) {
return Visibilities.INTERNAL;
}
Visibility maxVisibility = null;
for (CallableMemberDescriptor descriptor : descriptors) {
Visibility visibility = descriptor.getVisibility();
assert visibility != Visibilities.INHERITED;
if (maxVisibility == null) {
maxVisibility = visibility;
continue;
}
Integer compareResult = Visibilities.compare(visibility, maxVisibility);
if (compareResult == null) {
maxVisibility = null;
}
else if (compareResult > 0) {
maxVisibility = visibility;
}
}
// TODO: IDEA seems to issue an incorrect warning here
//noinspection ConstantConditions
if (maxVisibility == null) {
return null;
}
for (CallableMemberDescriptor descriptor : descriptors) {
Integer compareResult = Visibilities.compare(maxVisibility, descriptor.getVisibility());
if (compareResult == null || compareResult < 0) {
return null;
}
}
return maxVisibility;
}
public interface DescriptorSink {
void addToScope(@NotNull CallableMemberDescriptor fakeOverride);
void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent);
}
public static class OverrideCompatibilityInfo {
public enum Result {
OVERRIDABLE,
INCOMPATIBLE,
CONFLICT,
}
private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(Result.OVERRIDABLE, "SUCCESS");
@NotNull
public static OverrideCompatibilityInfo success() {
return SUCCESS;
}
@NotNull
public static OverrideCompatibilityInfo nameMismatch() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "nameMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo typeParameterNumberMismatch() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "typeParameterNumberMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo receiverPresenceMismatch() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "receiverPresenceMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo valueParameterNumberMismatch() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "valueParameterNumberMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo boundsMismatch(TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "boundsMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo valueParameterTypeMismatch(JetType superValueParameter, JetType subValueParameter, Result result) {
return new OverrideCompatibilityInfo(result, "valueParameterTypeMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo memberKindMismatch() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "memberKindMismatch"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo returnTypeMismatch(JetType substitutedSuperReturnType, JetType unsubstitutedSubReturnType) {
return new OverrideCompatibilityInfo(Result.CONFLICT, "returnTypeMismatch: " + unsubstitutedSubReturnType + " >< " + substitutedSuperReturnType); // TODO
}
@NotNull
public static OverrideCompatibilityInfo varOverriddenByVal() {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "varOverriddenByVal"); // TODO
}
@NotNull
public static OverrideCompatibilityInfo externalConditionFailed(Class extends ExternalOverridabilityCondition> conditionClass) {
return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "externalConditionFailed: " + conditionClass.getName()); // TODO
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private final Result overridable;
private final String message;
public OverrideCompatibilityInfo(Result success, String message) {
this.overridable = success;
this.message = message;
}
public Result getResult() {
return overridable;
}
public String getMessage() {
return message;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy