コード例 #1
0
ファイル: OverridingUtil.java プロジェクト: anujk3/kotlin
public class OverridingUtil {

  private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS =
      KotlinPackage.toList(
          ServiceLoader.load(
              ExternalOverridabilityCondition.class,
              ExternalOverridabilityCondition.class.getClassLoader()));

  public static final OverridingUtil DEFAULT =
      new OverridingUtil(
          new JetTypeChecker.TypeConstructorEquality() {
            @Override
            public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
              return a.equals(b);
            }
          });

  @NotNull
  public static OverridingUtil createWithEqualityAxioms(
      @NotNull JetTypeChecker.TypeConstructorEquality equalityAxioms) {
    return new OverridingUtil(equalityAxioms);
  }

  private final JetTypeChecker.TypeConstructorEquality equalityAxioms;

  private OverridingUtil(JetTypeChecker.TypeConstructorEquality axioms) {
    equalityAxioms = axioms;
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, false);
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableByIncludingReturnType(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, true);
  }

  @NotNull
  private OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      boolean checkReturnType) {
    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();
    }

    OverrideCompatibilityInfo receiverAndParameterResult =
        checkReceiverAndParameterCount(superDescriptor, subDescriptor);
    if (receiverAndParameterResult != null) {
      return receiverAndParameterResult;
    }

    List<JetType> superValueParameters = compiledValueParameters(superDescriptor);
    List<JetType> subValueParameters = compiledValueParameters(subDescriptor);

    List<TypeParameterDescriptor> superTypeParameters = superDescriptor.getTypeParameters();
    List<TypeParameterDescriptor> subTypeParameters = subDescriptor.getTypeParameters();

    if (superTypeParameters.size() != subTypeParameters.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.DEFAULT.equalTypes(superValueParameterType, subValueParameterType)) {
          return OverrideCompatibilityInfo.typeParameterNumberMismatch();
        }
      }
      return OverrideCompatibilityInfo.valueParameterTypeMismatch(
          null, null, OverrideCompatibilityInfo.Result.CONFLICT);
    }

    final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors =
        new HashMap<TypeConstructor, TypeConstructor>();
    for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
      TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
      TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
      matchingTypeConstructors.put(
          superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor());
    }

    JetTypeChecker.TypeConstructorEquality localEqualityAxioms =
        new JetTypeChecker.TypeConstructorEquality() {
          @Override
          public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
            if (equalityAxioms.equals(a, b)) return true;
            TypeConstructor img1 = matchingTypeConstructors.get(a);
            TypeConstructor img2 = matchingTypeConstructors.get(b);
            if (!(img1 != null && img1.equals(b)) && !(img2 != null && img2.equals(a))) {
              return false;
            }
            return true;
          }
        };

    for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
      TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
      TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);

      if (!areTypesEquivalent(
          superTypeParameter.getUpperBoundsAsType(),
          subTypeParameter.getUpperBoundsAsType(),
          localEqualityAxioms)) {
        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);

      if (!areTypesEquivalent(superValueParameter, subValueParameter, localEqualityAxioms)) {
        return OverrideCompatibilityInfo.valueParameterTypeMismatch(
            superValueParameter, subValueParameter, INCOMPATIBLE);
      }
    }

    if (checkReturnType) {
      JetType superReturnType = superDescriptor.getReturnType();
      JetType subReturnType = subDescriptor.getReturnType();

      if (superReturnType != null && subReturnType != null) {
        boolean bothErrors = subReturnType.isError() && superReturnType.isError();
        if (!bothErrors
            && !JetTypeChecker.withAxioms(localEqualityAxioms)
                .isSubtypeOf(subReturnType, superReturnType)) {
          return OverrideCompatibilityInfo.returnTypeMismatch(superReturnType, subReturnType);
        }
      }
    }

    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      if (!externalCondition.isOverridable(superDescriptor, subDescriptor)) {
        return OverrideCompatibilityInfo.externalConditionFailed(externalCondition.getClass());
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @Nullable
  static OverrideCompatibilityInfo checkReceiverAndParameterCount(
      CallableDescriptor superDescriptor, CallableDescriptor subDescriptor) {
    if ((superDescriptor.getExtensionReceiverParameter() == null)
        != (subDescriptor.getExtensionReceiverParameter() == null)) {
      return OverrideCompatibilityInfo.receiverPresenceMismatch();
    }

    if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
      return OverrideCompatibilityInfo.valueParameterNumberMismatch();
    }

    return null;
  }

  private static boolean areTypesEquivalent(
      @NotNull JetType typeInSuper,
      @NotNull JetType typeInSub,
      @NotNull JetTypeChecker.TypeConstructorEquality axioms) {
    boolean bothErrors = typeInSuper.isError() && typeInSub.isError();
    if (!bothErrors && !JetTypeChecker.withAxioms(axioms).equalTypes(typeInSuper, typeInSub)) {
      return false;
    }
    return true;
  }

  static List<JetType> compiledValueParameters(CallableDescriptor callableDescriptor) {
    ReceiverParameterDescriptor receiverParameter =
        callableDescriptor.getExtensionReceiverParameter();
    ArrayList<JetType> parameters = new ArrayList<JetType>();
    if (receiverParameter != null) {
      parameters.add(receiverParameter.getType());
    }
    for (ValueParameterDescriptor valueParameterDescriptor :
        callableDescriptor.getValueParameters()) {
      parameters.add(valueParameterDescriptor.getType());
    }
    return parameters;
  }

  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<CallableMemberDescriptor> notOverridden =
        new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes);

    for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
      Collection<CallableMemberDescriptor> bound =
          extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink);
      notOverridden.removeAll(bound);
    }

    createAndBindFakeOverrides(current, notOverridden, sink);
  }

  private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember(
      @NotNull CallableMemberDescriptor fromCurrent,
      @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper,
      @NotNull ClassDescriptor current,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> bound =
        new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size());
    for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
      OverrideCompatibilityInfo.Result result =
          DEFAULT.isOverridableBy(fromSupertype, fromCurrent).getResult();

      boolean isVisible =
          Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, 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<CallableMemberDescriptor> notOverridden,
      @NotNull DescriptorSink sink) {
    Queue<CallableMemberDescriptor> fromSuperQueue =
        new LinkedList<CallableMemberDescriptor>(notOverridden);
    while (!fromSuperQueue.isEmpty()) {
      CallableMemberDescriptor notOverriddenFromSuper =
          VisibilityUtil.findMemberWithMaxVisibility(fromSuperQueue);
      Collection<CallableMemberDescriptor> 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.DEFAULT.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.DEFAULT.isSubtypeOf(
          ((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType());
    }
    throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
  }

  private static CallableMemberDescriptor selectMostSpecificMemberFromSuper(
      @NotNull Collection<CallableMemberDescriptor> overridables) {
    CallableMemberDescriptor result = null;
    for (CallableMemberDescriptor overridable : overridables) {
      if (result == null || isMoreSpecific(overridable, result)) {
        result = overridable;
      }
    }
    return result;
  }

  private static void createAndBindFakeOverride(
      @NotNull Collection<CallableMemberDescriptor> overridables,
      @NotNull ClassDescriptor current,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> visibleOverridables =
        filterVisibleFakeOverrides(current, overridables);
    boolean allInvisible = visibleOverridables.isEmpty();
    Collection<CallableMemberDescriptor> effectiveOverridden =
        allInvisible ? overridables : visibleOverridables;

    Modality modality = getMinimalModality(effectiveOverridden);
    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<CallableMemberDescriptor> descriptors) {
    Modality modality = Modality.ABSTRACT;
    for (CallableMemberDescriptor descriptor : descriptors) {
      if (descriptor.getModality().compareTo(modality) < 0) {
        modality = descriptor.getModality();
      }
    }
    return modality;
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
      @NotNull final ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> toFilter) {
    return KotlinPackage.filter(
        toFilter,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            // nested class could capture private member, so check for private visibility added
            return !Visibilities.isPrivate(descriptor.getVisibility())
                && Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, current);
          }
        });
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
      @NotNull CallableMemberDescriptor overrider,
      @NotNull Queue<CallableMemberDescriptor> extractFrom,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> overridable = new ArrayList<CallableMemberDescriptor>();
    overridable.add(overrider);
    for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator();
        iterator.hasNext(); ) {
      CallableMemberDescriptor candidate = iterator.next();
      if (overrider == candidate) {
        iterator.remove();
        continue;
      }

      OverrideCompatibilityInfo.Result result1 =
          DEFAULT.isOverridableBy(candidate, overrider).getResult();
      OverrideCompatibilityInfo.Result result2 =
          DEFAULT.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,
      @Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility) {
    for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
      if (descriptor.getVisibility() == Visibilities.INHERITED) {
        resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
      }
    }

    if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
      return;
    }

    Visibility maxVisibility = computeVisibilityToInherit(memberDescriptor);
    Visibility visibilityToInherit;
    if (maxVisibility == null) {
      if (cannotInferVisibility != null) {
        cannotInferVisibility.invoke(memberDescriptor);
      }
      visibilityToInherit = Visibilities.PUBLIC;
    } else {
      visibilityToInherit = maxVisibility;
    }

    if (memberDescriptor instanceof PropertyDescriptorImpl) {
      ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
      for (PropertyAccessorDescriptor accessor :
          ((PropertyDescriptor) memberDescriptor).getAccessors()) {
        // If we couldn't infer visibility for property, the diagnostic is already reported, no need
        // to report it again on accessors
        resolveUnknownVisibilityForMember(
            accessor, maxVisibility == null ? null : cannotInferVisibility);
      }
    } else if (memberDescriptor instanceof FunctionDescriptorImpl) {
      ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    } else {
      assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
      ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    }
  }

  @Nullable
  private static Visibility computeVisibilityToInherit(
      @NotNull CallableMemberDescriptor memberDescriptor) {
    Set<? extends CallableMemberDescriptor> overriddenDescriptors =
        memberDescriptor.getOverriddenDescriptors();
    Visibility maxVisibility = findMaxVisibility(overriddenDescriptors);
    if (maxVisibility == null) {
      return null;
    }
    if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
      for (CallableMemberDescriptor overridden : overriddenDescriptors) {
        // An implementation (a non-abstract overridden member) of a fake override should have the
        // maximum possible visibility
        if (overridden.getModality() != Modality.ABSTRACT
            && !overridden.getVisibility().equals(maxVisibility)) {
          return null;
        }
      }
      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
          : "Visibility should have been computed for " + descriptor;
      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;
  }

  @NotNull
  @KotlinSignature(
      "fun getTopmostOverridenDescriptors(originalDescriptor: CallableDescriptor): List<out CallableDescriptor>")
  public static List<? extends CallableDescriptor> getTopmostOverridenDescriptors(
      @NotNull CallableDescriptor originalDescriptor) {
    return DFS.dfs(
        Collections.singletonList(originalDescriptor),
        new DFS.Neighbors<CallableDescriptor>() {
          @NotNull
          @Override
          public Iterable<? extends CallableDescriptor> getNeighbors(CallableDescriptor current) {
            return current.getOverriddenDescriptors();
          }
        },
        new DFS.CollectingNodeHandler<
            CallableDescriptor, CallableDescriptor, ArrayList<CallableDescriptor>>(
            new ArrayList<CallableDescriptor>()) {
          @Override
          public void afterChildren(CallableDescriptor current) {
            if (current.getOverriddenDescriptors().isEmpty()) {
              result.add(current);
            }
          }
        });
  }

  public static boolean traverseOverridenDescriptors(
      @NotNull CallableDescriptor originalDescriptor,
      @NotNull final Function1<CallableDescriptor, Boolean> handler) {
    return DFS.dfs(
        Collections.singletonList(originalDescriptor),
        new DFS.Neighbors<CallableDescriptor>() {
          @NotNull
          @Override
          public Iterable<? extends CallableDescriptor> getNeighbors(CallableDescriptor current) {
            return current.getOverriddenDescriptors();
          }
        },
        new DFS.AbstractNodeHandler<CallableDescriptor, Boolean>() {
          private boolean result = true;

          @Override
          public boolean beforeChildren(CallableDescriptor current) {
            if (!handler.invoke(current)) {
              result = false;
            }
            return result;
          }

          @Override
          public Boolean result() {
            return result;
          }
        });
  }

  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(INCOMPATIBLE, "nameMismatch"); // TODO
    }

    @NotNull
    public static OverrideCompatibilityInfo typeParameterNumberMismatch() {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, "typeParameterNumberMismatch"); // TODO
    }

    @NotNull
    public static OverrideCompatibilityInfo receiverPresenceMismatch() {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, "receiverPresenceMismatch"); // TODO
    }

    @NotNull
    public static OverrideCompatibilityInfo valueParameterNumberMismatch() {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, "valueParameterNumberMismatch"); // TODO
    }

    @NotNull
    public static OverrideCompatibilityInfo boundsMismatch(
        TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) {
      return new OverrideCompatibilityInfo(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(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(INCOMPATIBLE, "varOverriddenByVal"); // TODO
    }

    @NotNull
    public static OverrideCompatibilityInfo externalConditionFailed(
        Class<? extends ExternalOverridabilityCondition> conditionClass) {
      return new OverrideCompatibilityInfo(
          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;
    }
  }
}
コード例 #2
0
ファイル: OverridingUtil.java プロジェクト: JiaHaoWen/kotlin
public class OverridingUtil {

  private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS =
      CollectionsKt.toList(
          ServiceLoader.load(
              ExternalOverridabilityCondition.class,
              ExternalOverridabilityCondition.class.getClassLoader()));

  public static final OverridingUtil DEFAULT =
      new OverridingUtil(
          new KotlinTypeChecker.TypeConstructorEquality() {
            @Override
            public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
              return a.equals(b);
            }
          });

  @NotNull
  public static OverridingUtil createWithEqualityAxioms(
      @NotNull KotlinTypeChecker.TypeConstructorEquality equalityAxioms) {
    return new OverridingUtil(equalityAxioms);
  }

  private final KotlinTypeChecker.TypeConstructorEquality equalityAxioms;

  private OverridingUtil(KotlinTypeChecker.TypeConstructorEquality axioms) {
    equalityAxioms = axioms;
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, false);
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableByIncludingReturnType(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, true);
  }

  @NotNull
  private OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      boolean checkReturnType) {
    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      ExternalOverridabilityCondition.Result result =
          externalCondition.isOverridable(superDescriptor, subDescriptor);
      switch (result) {
        case OVERRIDABLE:
          return OverrideCompatibilityInfo.success();
        case CONFLICT:
          return OverrideCompatibilityInfo.conflict("External condition failed");
        case INCOMPATIBLE:
          return OverrideCompatibilityInfo.incompatible("External condition");
        case UNKNOWN:
          // do nothing
          // go to the next external condition or default override check
      }
    }

    return isOverridableByWithoutExternalConditions(
        superDescriptor, subDescriptor, checkReturnType);
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableByWithoutExternalConditions(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      boolean checkReturnType) {
    if (superDescriptor instanceof FunctionDescriptor
            && !(subDescriptor instanceof FunctionDescriptor)
        || superDescriptor instanceof PropertyDescriptor
            && !(subDescriptor instanceof PropertyDescriptor)) {
      return OverrideCompatibilityInfo.incompatible("Member kind mismatch");
    }

    if (!(superDescriptor instanceof FunctionDescriptor)
        && !(superDescriptor instanceof PropertyDescriptor)) {
      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.incompatible("Name mismatch");
    }

    OverrideCompatibilityInfo receiverAndParameterResult =
        checkReceiverAndParameterCount(superDescriptor, subDescriptor);
    if (receiverAndParameterResult != null) {
      return receiverAndParameterResult;
    }

    List<KotlinType> superValueParameters = compiledValueParameters(superDescriptor);
    List<KotlinType> subValueParameters = compiledValueParameters(subDescriptor);

    List<TypeParameterDescriptor> superTypeParameters = superDescriptor.getTypeParameters();
    List<TypeParameterDescriptor> subTypeParameters = subDescriptor.getTypeParameters();

    if (superTypeParameters.size() != subTypeParameters.size()) {
      for (int i = 0; i < superValueParameters.size(); ++i) {
        // TODO: compare erasure
        if (!KotlinTypeChecker.DEFAULT.equalTypes(
            superValueParameters.get(i), subValueParameters.get(i))) {
          return OverrideCompatibilityInfo.incompatible("Type parameter number mismatch");
        }
      }
      return OverrideCompatibilityInfo.conflict("Type parameter number mismatch");
    }

    KotlinTypeChecker typeChecker = createTypeChecker(superTypeParameters, subTypeParameters);

    for (int i = 0; i < superTypeParameters.size(); i++) {
      if (!areTypeParametersEquivalent(
          superTypeParameters.get(i), subTypeParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch");
      }
    }

    for (int i = 0; i < superValueParameters.size(); i++) {
      if (!areTypesEquivalent(
          superValueParameters.get(i), subValueParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch");
      }
    }

    if (checkReturnType) {
      KotlinType superReturnType = superDescriptor.getReturnType();
      KotlinType subReturnType = subDescriptor.getReturnType();

      if (superReturnType != null && subReturnType != null) {
        boolean bothErrors = subReturnType.isError() && superReturnType.isError();
        if (!bothErrors && !typeChecker.isSubtypeOf(subReturnType, superReturnType)) {
          return OverrideCompatibilityInfo.conflict("Return type mismatch");
        }
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @NotNull
  private KotlinTypeChecker createTypeChecker(
      @NotNull List<TypeParameterDescriptor> firstParameters,
      @NotNull List<TypeParameterDescriptor> secondParameters) {
    assert firstParameters.size() == secondParameters.size()
        : "Should be the same number of type parameters: "
            + firstParameters
            + " vs "
            + secondParameters;
    if (firstParameters.isEmpty()) return KotlinTypeChecker.withAxioms(equalityAxioms);

    final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors =
        new HashMap<TypeConstructor, TypeConstructor>();
    for (int i = 0; i < firstParameters.size(); i++) {
      matchingTypeConstructors.put(
          firstParameters.get(i).getTypeConstructor(),
          secondParameters.get(i).getTypeConstructor());
    }

    return KotlinTypeChecker.withAxioms(
        new KotlinTypeChecker.TypeConstructorEquality() {
          @Override
          public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
            if (equalityAxioms.equals(a, b)) return true;
            TypeConstructor img1 = matchingTypeConstructors.get(a);
            TypeConstructor img2 = matchingTypeConstructors.get(b);
            return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
          }
        });
  }

  @Nullable
  static OverrideCompatibilityInfo checkReceiverAndParameterCount(
      CallableDescriptor superDescriptor, CallableDescriptor subDescriptor) {
    if ((superDescriptor.getExtensionReceiverParameter() == null)
        != (subDescriptor.getExtensionReceiverParameter() == null)) {
      return OverrideCompatibilityInfo.incompatible("Receiver presence mismatch");
    }

    if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
      return OverrideCompatibilityInfo.incompatible("Value parameter number mismatch");
    }

    return null;
  }

  private static boolean areTypesEquivalent(
      @NotNull KotlinType typeInSuper,
      @NotNull KotlinType typeInSub,
      @NotNull KotlinTypeChecker typeChecker) {
    boolean bothErrors = typeInSuper.isError() && typeInSub.isError();
    return bothErrors || typeChecker.equalTypes(typeInSuper, typeInSub);
  }

  // See JLS 8, 8.4.4 Generic Methods
  // TODO: use TypeSubstitutor instead
  private static boolean areTypeParametersEquivalent(
      @NotNull TypeParameterDescriptor superTypeParameter,
      @NotNull TypeParameterDescriptor subTypeParameter,
      @NotNull KotlinTypeChecker typeChecker) {
    List<KotlinType> superBounds = superTypeParameter.getUpperBounds();
    List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds());
    if (superBounds.size() != subBounds.size()) return false;

    outer:
    for (KotlinType superBound : superBounds) {
      ListIterator<KotlinType> it = subBounds.listIterator();
      while (it.hasNext()) {
        KotlinType subBound = it.next();
        if (areTypesEquivalent(superBound, subBound, typeChecker)) {
          it.remove();
          continue outer;
        }
      }
      return false;
    }

    return true;
  }

  static List<KotlinType> compiledValueParameters(CallableDescriptor callableDescriptor) {
    ReceiverParameterDescriptor receiverParameter =
        callableDescriptor.getExtensionReceiverParameter();
    List<KotlinType> parameters = new ArrayList<KotlinType>();
    if (receiverParameter != null) {
      parameters.add(receiverParameter.getType());
    }
    for (ValueParameterDescriptor valueParameterDescriptor :
        callableDescriptor.getValueParameters()) {
      parameters.add(valueParameterDescriptor.getType());
    }
    return parameters;
  }

  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<CallableMemberDescriptor> notOverridden =
        new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes);

    for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
      Collection<CallableMemberDescriptor> bound =
          extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink);
      notOverridden.removeAll(bound);
    }

    createAndBindFakeOverrides(current, notOverridden, sink);
  }

  private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember(
      @NotNull CallableMemberDescriptor fromCurrent,
      @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper,
      @NotNull ClassDescriptor current,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> bound =
        new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size());
    for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
      OverrideCompatibilityInfo.Result result =
          DEFAULT.isOverridableBy(fromSupertype, fromCurrent).getResult();

      boolean isVisible =
          Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, fromSupertype, current);
      switch (result) {
        case OVERRIDABLE:
          if (isVisible) {
            fromCurrent.addOverriddenDescriptor(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<CallableMemberDescriptor> notOverridden,
      @NotNull DescriptorSink sink) {
    Queue<CallableMemberDescriptor> fromSuperQueue =
        new LinkedList<CallableMemberDescriptor>(notOverridden);
    while (!fromSuperQueue.isEmpty()) {
      CallableMemberDescriptor notOverriddenFromSuper =
          VisibilityUtilKt.findMemberWithMaxVisibility(fromSuperQueue);
      Collection<CallableMemberDescriptor> 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();

      KotlinType aReturnType = a.getReturnType();
      assert aReturnType != null;
      KotlinType bReturnType = b.getReturnType();
      assert bReturnType != null;

      return KotlinTypeChecker.DEFAULT.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 KotlinTypeChecker.DEFAULT.isSubtypeOf(
          ((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType());
    }
    throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
  }

  private static CallableMemberDescriptor selectMostSpecificMemberFromSuper(
      @NotNull Collection<CallableMemberDescriptor> overridables) {
    CallableMemberDescriptor result = null;
    for (CallableMemberDescriptor overridable : overridables) {
      if (result == null || isMoreSpecific(overridable, result)) {
        result = overridable;
      }
    }
    return result;
  }

  private static void createAndBindFakeOverride(
      @NotNull Collection<CallableMemberDescriptor> overridables,
      @NotNull ClassDescriptor current,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> visibleOverridables =
        filterVisibleFakeOverrides(current, overridables);
    boolean allInvisible = visibleOverridables.isEmpty();
    Collection<CallableMemberDescriptor> effectiveOverridden =
        allInvisible ? overridables : visibleOverridables;

    Modality modality = getMinimalModality(effectiveOverridden);
    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) {
      fakeOverride.addOverriddenDescriptor(descriptor);
    }
    sink.addFakeOverride(fakeOverride);
  }

  @NotNull
  private static Modality getMinimalModality(
      @NotNull Collection<CallableMemberDescriptor> descriptors) {
    Modality modality = Modality.ABSTRACT;
    for (CallableMemberDescriptor descriptor : descriptors) {
      if (descriptor.getModality().compareTo(modality) < 0) {
        modality = descriptor.getModality();
      }
    }
    return modality;
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
      @NotNull final ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> toFilter) {
    return CollectionsKt.filter(
        toFilter,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            // nested class could capture private member, so check for private visibility added
            return !Visibilities.isPrivate(descriptor.getVisibility())
                && Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, current);
          }
        });
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
      @NotNull CallableMemberDescriptor overrider,
      @NotNull Queue<CallableMemberDescriptor> extractFrom,
      @NotNull DescriptorSink sink) {
    Collection<CallableMemberDescriptor> overridable = new ArrayList<CallableMemberDescriptor>();
    overridable.add(overrider);
    for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator();
        iterator.hasNext(); ) {
      CallableMemberDescriptor candidate = iterator.next();
      if (overrider == candidate) {
        iterator.remove();
        continue;
      }

      OverrideCompatibilityInfo.Result result1 =
          DEFAULT.isOverridableBy(candidate, overrider).getResult();
      OverrideCompatibilityInfo.Result result2 =
          DEFAULT.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,
      @Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility) {
    for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
      if (descriptor.getVisibility() == Visibilities.INHERITED) {
        resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
      }
    }

    if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
      return;
    }

    Visibility maxVisibility = computeVisibilityToInherit(memberDescriptor);
    Visibility visibilityToInherit;
    if (maxVisibility == null) {
      if (cannotInferVisibility != null) {
        cannotInferVisibility.invoke(memberDescriptor);
      }
      visibilityToInherit = Visibilities.PUBLIC;
    } else {
      visibilityToInherit = maxVisibility;
    }

    if (memberDescriptor instanceof PropertyDescriptorImpl) {
      ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
      for (PropertyAccessorDescriptor accessor :
          ((PropertyDescriptor) memberDescriptor).getAccessors()) {
        // If we couldn't infer visibility for property, the diagnostic is already reported, no need
        // to report it again on accessors
        resolveUnknownVisibilityForMember(
            accessor, maxVisibility == null ? null : cannotInferVisibility);
      }
    } else if (memberDescriptor instanceof FunctionDescriptorImpl) {
      ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    } else {
      assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
      ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    }
  }

  @Nullable
  private static Visibility computeVisibilityToInherit(
      @NotNull CallableMemberDescriptor memberDescriptor) {
    Collection<? extends CallableMemberDescriptor> overriddenDescriptors =
        memberDescriptor.getOverriddenDescriptors();
    Visibility maxVisibility = findMaxVisibility(overriddenDescriptors);
    if (maxVisibility == null) {
      return null;
    }
    if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
      for (CallableMemberDescriptor overridden : overriddenDescriptors) {
        // An implementation (a non-abstract overridden member) of a fake override should have the
        // maximum possible visibility
        if (overridden.getModality() != Modality.ABSTRACT
            && !overridden.getVisibility().equals(maxVisibility)) {
          return null;
        }
      }
      return maxVisibility;
    }
    return maxVisibility.normalize();
  }

  @Nullable
  public static Visibility findMaxVisibility(
      @NotNull Collection<? extends CallableMemberDescriptor> descriptors) {
    if (descriptors.isEmpty()) {
      return Visibilities.DEFAULT_VISIBILITY;
    }
    Visibility maxVisibility = null;
    for (CallableMemberDescriptor descriptor : descriptors) {
      Visibility visibility = descriptor.getVisibility();
      assert visibility != Visibilities.INHERITED
          : "Visibility should have been computed for " + descriptor;
      if (maxVisibility == null) {
        maxVisibility = visibility;
        continue;
      }
      Integer compareResult = Visibilities.compare(visibility, maxVisibility);
      if (compareResult == null) {
        maxVisibility = null;
      } else if (compareResult > 0) {
        maxVisibility = visibility;
      }
    }
    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 addFakeOverride(@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(OVERRIDABLE, "SUCCESS");

    @NotNull
    public static OverrideCompatibilityInfo success() {
      return SUCCESS;
    }

    @NotNull
    public static OverrideCompatibilityInfo incompatible(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, debugMessage);
    }

    @NotNull
    public static OverrideCompatibilityInfo conflict(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(CONFLICT, debugMessage);
    }

    private final Result overridable;
    private final String debugMessage;

    public OverrideCompatibilityInfo(@NotNull Result success, @NotNull String debugMessage) {
      this.overridable = success;
      this.debugMessage = debugMessage;
    }

    @NotNull
    public Result getResult() {
      return overridable;
    }

    @NotNull
    public String getDebugMessage() {
      return debugMessage;
    }
  }
}
コード例 #3
0
ファイル: OverridingUtil.java プロジェクト: sys1yagi/kotlin
public class OverridingUtil {

  private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS =
      CollectionsKt.toList(
          ServiceLoader.load(
              ExternalOverridabilityCondition.class,
              ExternalOverridabilityCondition.class.getClassLoader()));

  public static final OverridingUtil DEFAULT =
      new OverridingUtil(
          new KotlinTypeChecker.TypeConstructorEquality() {
            @Override
            public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
              return a.equals(b);
            }
          });

  @NotNull
  public static OverridingUtil createWithEqualityAxioms(
      @NotNull KotlinTypeChecker.TypeConstructorEquality equalityAxioms) {
    return new OverridingUtil(equalityAxioms);
  }

  private final KotlinTypeChecker.TypeConstructorEquality equalityAxioms;

  private OverridingUtil(KotlinTypeChecker.TypeConstructorEquality axioms) {
    equalityAxioms = axioms;
  }

  /**
   * Given a set of descriptors, returns a set containing all the given descriptors except those
   * which _are overridden_ by at least one other descriptor from the original set.
   */
  @NotNull
  public static <D extends CallableDescriptor> Set<D> filterOutOverridden(
      @NotNull Set<D> candidateSet) {
    return filterOverrides(candidateSet, FunctionsKt.<CallableDescriptor>identity());
  }

  @NotNull
  public static <D> Set<D> filterOverrides(
      @NotNull Set<D> candidateSet,
      @NotNull Function1<? super D, ? extends CallableDescriptor> transform) {
    if (candidateSet.size() <= 1) return candidateSet;

    Set<D> result = new LinkedHashSet<D>();
    outerLoop:
    for (D meD : candidateSet) {
      CallableDescriptor me = transform.invoke(meD);
      for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
        D otherD = iterator.next();
        CallableDescriptor other = transform.invoke(otherD);
        if (overrides(me, other)) {
          iterator.remove();
        } else if (overrides(other, me)) {
          continue outerLoop;
        }
      }
      result.add(meD);
    }

    assert !result.isEmpty() : "All candidates filtered out from " + candidateSet;

    return result;
  }

  /** @return whether f overrides g */
  public static <D extends CallableDescriptor> boolean overrides(@NotNull D f, @NotNull D g) {
    // In a multi-module project different "copies" of the same class may be present in different
    // libraries,
    // that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides).

    // This first check cover the case of duplicate classes in different modules:
    // when B is defined in modules m1 and m2, and C (indirectly) inherits from both versions,
    // we'll be getting sets of members that do not override each other, but are structurally
    // equivalent.
    // As other code relies on no equal descriptors passed here, we guard against f == g, but this
    // may not be necessary
    // Note that this is needed for the usage of this function in the IDE code
    if (!f.equals(g)
        && DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(
            f.getOriginal(), g.getOriginal())) return true;

    CallableDescriptor originalG = g.getOriginal();
    for (D overriddenFunction : DescriptorUtils.getAllOverriddenDescriptors(f)) {
      if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(originalG, overriddenFunction))
        return true;
    }
    return false;
  }

  /**
   * @return overridden real descriptors (not fake overrides). Note that most usages of this method
   *     should be followed by calling {@link #filterOutOverridden(Set)}, because some of the
   *     declarations can override the other.
   */
  @NotNull
  public static Set<CallableMemberDescriptor> getOverriddenDeclarations(
      @NotNull CallableMemberDescriptor descriptor) {
    Set<CallableMemberDescriptor> result = new LinkedHashSet<CallableMemberDescriptor>();
    collectOverriddenDeclarations(descriptor, result);
    return result;
  }

  private static void collectOverriddenDeclarations(
      @NotNull CallableMemberDescriptor descriptor, @NotNull Set<CallableMemberDescriptor> result) {
    if (descriptor.getKind().isReal()) {
      result.add(descriptor);
    } else {
      if (descriptor.getOverriddenDescriptors().isEmpty()) {
        throw new IllegalStateException(
            "No overridden descriptors found for (fake override) " + descriptor);
      }
      for (CallableMemberDescriptor overridden : descriptor.getOverriddenDescriptors()) {
        collectOverriddenDeclarations(overridden, result);
      }
    }
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      @Nullable ClassDescriptor subClassDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, subClassDescriptor, false);
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      @Nullable ClassDescriptor subClassDescriptor,
      boolean checkReturnType) {
    OverrideCompatibilityInfo basicResult =
        isOverridableByWithoutExternalConditions(superDescriptor, subDescriptor, checkReturnType);
    boolean wasSuccess = basicResult.getResult() == OVERRIDABLE;

    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      // Do not run CONFLICTS_ONLY while there was no success
      if (externalCondition.getContract()
          == ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY) continue;
      if (wasSuccess
          && externalCondition.getContract()
              == ExternalOverridabilityCondition.Contract.SUCCESS_ONLY) continue;

      ExternalOverridabilityCondition.Result result =
          externalCondition.isOverridable(superDescriptor, subDescriptor, subClassDescriptor);

      switch (result) {
        case OVERRIDABLE:
          wasSuccess = true;
          break;
        case CONFLICT:
          return OverrideCompatibilityInfo.conflict("External condition failed");
        case INCOMPATIBLE:
          return OverrideCompatibilityInfo.incompatible("External condition");
        case UNKNOWN:
          // do nothing
          // go to the next external condition or default override check
      }
    }

    if (!wasSuccess) {
      return basicResult;
    }

    // Search for conflicts from external conditions
    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      // Run all conditions that was not run before (i.e. CONFLICTS_ONLY)
      if (externalCondition.getContract()
          != ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY) continue;

      ExternalOverridabilityCondition.Result result =
          externalCondition.isOverridable(superDescriptor, subDescriptor, subClassDescriptor);
      switch (result) {
        case CONFLICT:
          return OverrideCompatibilityInfo.conflict("External condition failed");
        case INCOMPATIBLE:
          return OverrideCompatibilityInfo.incompatible("External condition");
        case OVERRIDABLE:
          throw new IllegalStateException(
              "Contract violation in "
                  + externalCondition.getClass().getName()
                  + " condition. It's not supposed to end with success");
        case UNKNOWN:
          // do nothing
          // go to the next external condition or default override check
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableByWithoutExternalConditions(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      boolean checkReturnType) {
    OverrideCompatibilityInfo basicOverridability =
        getBasicOverridabilityProblem(superDescriptor, subDescriptor);
    if (basicOverridability != null) return basicOverridability;

    List<KotlinType> superValueParameters = compiledValueParameters(superDescriptor);
    List<KotlinType> subValueParameters = compiledValueParameters(subDescriptor);

    List<TypeParameterDescriptor> superTypeParameters = superDescriptor.getTypeParameters();
    List<TypeParameterDescriptor> subTypeParameters = subDescriptor.getTypeParameters();

    if (superTypeParameters.size() != subTypeParameters.size()) {
      for (int i = 0; i < superValueParameters.size(); ++i) {
        // TODO: compare erasure
        if (!KotlinTypeChecker.DEFAULT.equalTypes(
            superValueParameters.get(i), subValueParameters.get(i))) {
          return OverrideCompatibilityInfo.incompatible("Type parameter number mismatch");
        }
      }
      return OverrideCompatibilityInfo.conflict("Type parameter number mismatch");
    }

    KotlinTypeChecker typeChecker = createTypeChecker(superTypeParameters, subTypeParameters);

    for (int i = 0; i < superTypeParameters.size(); i++) {
      if (!areTypeParametersEquivalent(
          superTypeParameters.get(i), subTypeParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch");
      }
    }

    for (int i = 0; i < superValueParameters.size(); i++) {
      if (!areTypesEquivalent(
          superValueParameters.get(i), subValueParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch");
      }
    }

    if (checkReturnType) {
      KotlinType superReturnType = superDescriptor.getReturnType();
      KotlinType subReturnType = subDescriptor.getReturnType();

      if (superReturnType != null && subReturnType != null) {
        boolean bothErrors = subReturnType.isError() && superReturnType.isError();
        if (!bothErrors && !typeChecker.isSubtypeOf(subReturnType, superReturnType)) {
          return OverrideCompatibilityInfo.conflict("Return type mismatch");
        }
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @Nullable
  public static OverrideCompatibilityInfo getBasicOverridabilityProblem(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    if (superDescriptor instanceof FunctionDescriptor
            && !(subDescriptor instanceof FunctionDescriptor)
        || superDescriptor instanceof PropertyDescriptor
            && !(subDescriptor instanceof PropertyDescriptor)) {
      return OverrideCompatibilityInfo.incompatible("Member kind mismatch");
    }

    if (!(superDescriptor instanceof FunctionDescriptor)
        && !(superDescriptor instanceof PropertyDescriptor)) {
      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.incompatible("Name mismatch");
    }

    OverrideCompatibilityInfo receiverAndParameterResult =
        checkReceiverAndParameterCount(superDescriptor, subDescriptor);
    if (receiverAndParameterResult != null) {
      return receiverAndParameterResult;
    }

    return null;
  }

  @NotNull
  private KotlinTypeChecker createTypeChecker(
      @NotNull List<TypeParameterDescriptor> firstParameters,
      @NotNull List<TypeParameterDescriptor> secondParameters) {
    assert firstParameters.size() == secondParameters.size()
        : "Should be the same number of type parameters: "
            + firstParameters
            + " vs "
            + secondParameters;
    if (firstParameters.isEmpty()) return KotlinTypeCheckerImpl.withAxioms(equalityAxioms);

    final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors =
        new HashMap<TypeConstructor, TypeConstructor>();
    for (int i = 0; i < firstParameters.size(); i++) {
      matchingTypeConstructors.put(
          firstParameters.get(i).getTypeConstructor(),
          secondParameters.get(i).getTypeConstructor());
    }

    return KotlinTypeCheckerImpl.withAxioms(
        new KotlinTypeChecker.TypeConstructorEquality() {
          @Override
          public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
            if (equalityAxioms.equals(a, b)) return true;
            TypeConstructor img1 = matchingTypeConstructors.get(a);
            TypeConstructor img2 = matchingTypeConstructors.get(b);
            return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
          }
        });
  }

  @Nullable
  private static OverrideCompatibilityInfo checkReceiverAndParameterCount(
      CallableDescriptor superDescriptor, CallableDescriptor subDescriptor) {
    if ((superDescriptor.getExtensionReceiverParameter() == null)
        != (subDescriptor.getExtensionReceiverParameter() == null)) {
      return OverrideCompatibilityInfo.incompatible("Receiver presence mismatch");
    }

    if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
      return OverrideCompatibilityInfo.incompatible("Value parameter number mismatch");
    }

    return null;
  }

  private static boolean areTypesEquivalent(
      @NotNull KotlinType typeInSuper,
      @NotNull KotlinType typeInSub,
      @NotNull KotlinTypeChecker typeChecker) {
    boolean bothErrors = typeInSuper.isError() && typeInSub.isError();
    return bothErrors || typeChecker.equalTypes(typeInSuper, typeInSub);
  }

  // See JLS 8, 8.4.4 Generic Methods
  // TODO: use TypeSubstitutor instead
  private static boolean areTypeParametersEquivalent(
      @NotNull TypeParameterDescriptor superTypeParameter,
      @NotNull TypeParameterDescriptor subTypeParameter,
      @NotNull KotlinTypeChecker typeChecker) {
    List<KotlinType> superBounds = superTypeParameter.getUpperBounds();
    List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds());
    if (superBounds.size() != subBounds.size()) return false;

    outer:
    for (KotlinType superBound : superBounds) {
      ListIterator<KotlinType> it = subBounds.listIterator();
      while (it.hasNext()) {
        KotlinType subBound = it.next();
        if (areTypesEquivalent(superBound, subBound, typeChecker)) {
          it.remove();
          continue outer;
        }
      }
      return false;
    }

    return true;
  }

  private static List<KotlinType> compiledValueParameters(CallableDescriptor callableDescriptor) {
    ReceiverParameterDescriptor receiverParameter =
        callableDescriptor.getExtensionReceiverParameter();
    List<KotlinType> parameters = new ArrayList<KotlinType>();
    if (receiverParameter != null) {
      parameters.add(receiverParameter.getType());
    }
    for (ValueParameterDescriptor valueParameterDescriptor :
        callableDescriptor.getValueParameters()) {
      parameters.add(valueParameterDescriptor.getType());
    }
    return parameters;
  }

  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 OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> notOverridden =
        new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes);

    for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
      Collection<CallableMemberDescriptor> bound =
          extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, strategy);
      notOverridden.removeAll(bound);
    }

    createAndBindFakeOverrides(current, notOverridden, strategy);
  }

  public static boolean isVisibleForOverride(
      @NotNull MemberDescriptor overriding, @NotNull MemberDescriptor fromSuper) {
    return !Visibilities.isPrivate(fromSuper.getVisibility())
        && Visibilities.isVisibleIgnoringReceiver(fromSuper, overriding);
  }

  private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember(
      @NotNull CallableMemberDescriptor fromCurrent,
      @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper,
      @NotNull ClassDescriptor current,
      @NotNull OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> bound =
        new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size());
    Collection<CallableMemberDescriptor> overridden = SmartSet.create();
    for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
      OverrideCompatibilityInfo.Result result =
          DEFAULT.isOverridableBy(fromSupertype, fromCurrent, current).getResult();

      boolean isVisibleForOverride = isVisibleForOverride(fromCurrent, fromSupertype);

      switch (result) {
        case OVERRIDABLE:
          if (isVisibleForOverride) {
            overridden.add(fromSupertype);
          }
          bound.add(fromSupertype);
          break;
        case CONFLICT:
          if (isVisibleForOverride) {
            strategy.overrideConflict(fromSupertype, fromCurrent);
          }
          bound.add(fromSupertype);
          break;
        case INCOMPATIBLE:
          break;
      }
    }

    strategy.setOverriddenDescriptors(fromCurrent, overridden);

    return bound;
  }

  private static boolean allHasSameContainingDeclaration(
      @NotNull Collection<CallableMemberDescriptor> notOverridden) {
    if (notOverridden.size() < 2) return true;

    final DeclarationDescriptor containingDeclaration =
        notOverridden.iterator().next().getContainingDeclaration();
    return CollectionsKt.all(
        notOverridden,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            return descriptor.getContainingDeclaration() == containingDeclaration;
          }
        });
  }

  private static void createAndBindFakeOverrides(
      @NotNull ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> notOverridden,
      @NotNull OverridingStrategy strategy) {
    // Optimization: If all notOverridden descriptors have the same containing declaration,
    // then we can just create fake overrides for them, because they should be matched correctly in
    // their containing declaration
    if (allHasSameContainingDeclaration(notOverridden)) {
      for (CallableMemberDescriptor descriptor : notOverridden) {
        createAndBindFakeOverride(Collections.singleton(descriptor), current, strategy);
      }
      return;
    }

    Queue<CallableMemberDescriptor> fromSuperQueue =
        new LinkedList<CallableMemberDescriptor>(notOverridden);
    while (!fromSuperQueue.isEmpty()) {
      CallableMemberDescriptor notOverriddenFromSuper =
          VisibilityUtilKt.findMemberWithMaxVisibility(fromSuperQueue);
      Collection<CallableMemberDescriptor> overridables =
          extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, strategy);
      createAndBindFakeOverride(overridables, current, strategy);
    }
  }

  public static boolean isMoreSpecific(
      @NotNull CallableDescriptor a, @NotNull CallableDescriptor b) {
    KotlinType aReturnType = a.getReturnType();
    KotlinType bReturnType = b.getReturnType();

    assert aReturnType != null : "Return type of " + a + " is null";
    assert bReturnType != null : "Return type of " + b + " is null";

    if (!isVisibilityMoreSpecific(a, b)) return false;

    if (a instanceof SimpleFunctionDescriptor) {
      assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass();

      return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
    }
    if (a instanceof PropertyDescriptor) {
      assert b instanceof PropertyDescriptor : "b is " + b.getClass();

      PropertyDescriptor pa = (PropertyDescriptor) a;
      PropertyDescriptor pb = (PropertyDescriptor) b;

      if (!isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false;

      if (pa.isVar() && pb.isVar()) {
        return DEFAULT
            .createTypeChecker(a.getTypeParameters(), b.getTypeParameters())
            .equalTypes(aReturnType, bReturnType);
      } else {
        // both vals or var vs val: val can't be more specific then var
        return !(!pa.isVar() && pb.isVar())
            && isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
      }
    }
    throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
  }

  private static boolean isVisibilityMoreSpecific(
      @NotNull DeclarationDescriptorWithVisibility a,
      @NotNull DeclarationDescriptorWithVisibility b) {
    Integer result = Visibilities.compare(a.getVisibility(), b.getVisibility());
    return result == null || result >= 0;
  }

  private static boolean isAccessorMoreSpecific(
      @Nullable PropertyAccessorDescriptor a, @Nullable PropertyAccessorDescriptor b) {
    if (a == null || b == null) return true;
    return isVisibilityMoreSpecific(a, b);
  }

  private static boolean isMoreSpecificThenAllOf(
      @NotNull CallableDescriptor candidate, @NotNull Collection<CallableDescriptor> descriptors) {
    // NB subtyping relation in Kotlin is not transitive in presence of flexible types:
    //  String? <: String! <: String, but not String? <: String
    for (CallableDescriptor descriptor : descriptors) {
      if (!isMoreSpecific(candidate, descriptor)) {
        return false;
      }
    }
    return true;
  }

  private static boolean isReturnTypeMoreSpecific(
      @NotNull CallableDescriptor a,
      @NotNull KotlinType aReturnType,
      @NotNull CallableDescriptor b,
      @NotNull KotlinType bReturnType) {
    KotlinTypeChecker typeChecker =
        DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters());
    return typeChecker.isSubtypeOf(aReturnType, bReturnType);
  }

  @NotNull
  public static <H> H selectMostSpecificMember(
      @NotNull Collection<H> overridables,
      @NotNull Function1<H, CallableDescriptor> descriptorByHandle) {
    assert !overridables.isEmpty() : "Should have at least one overridable descriptor";

    if (overridables.size() == 1) {
      return CollectionsKt.first(overridables);
    }

    Collection<H> candidates = new ArrayList<H>(2);
    List<CallableDescriptor> callableMemberDescriptors =
        CollectionsKt.map(overridables, descriptorByHandle);

    H transitivelyMostSpecific = CollectionsKt.first(overridables);
    CallableDescriptor transitivelyMostSpecificDescriptor =
        descriptorByHandle.invoke(transitivelyMostSpecific);

    for (H overridable : overridables) {
      CallableDescriptor descriptor = descriptorByHandle.invoke(overridable);
      if (isMoreSpecificThenAllOf(descriptor, callableMemberDescriptors)) {
        candidates.add(overridable);
      }
      if (isMoreSpecific(descriptor, transitivelyMostSpecificDescriptor)
          && !isMoreSpecific(transitivelyMostSpecificDescriptor, descriptor)) {
        transitivelyMostSpecific = overridable;
      }
    }

    if (candidates.isEmpty()) {
      return transitivelyMostSpecific;
    } else if (candidates.size() == 1) {
      return CollectionsKt.first(candidates);
    }

    H firstNonFlexible = null;
    for (H candidate : candidates) {
      //noinspection ConstantConditions
      if (!FlexibleTypesKt.isFlexible(descriptorByHandle.invoke(candidate).getReturnType())) {
        firstNonFlexible = candidate;
        break;
      }
    }
    if (firstNonFlexible != null) {
      return firstNonFlexible;
    }

    return CollectionsKt.first(candidates);
  }

  private static void createAndBindFakeOverride(
      @NotNull Collection<CallableMemberDescriptor> overridables,
      @NotNull ClassDescriptor current,
      @NotNull OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> visibleOverridables =
        filterVisibleFakeOverrides(current, overridables);
    boolean allInvisible = visibleOverridables.isEmpty();
    Collection<CallableMemberDescriptor> effectiveOverridden =
        allInvisible ? overridables : visibleOverridables;

    Modality modality = determineModality(effectiveOverridden);
    Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;

    // FIXME doesn't work as expected for flexible types: should create a refined signature.
    // Current algorithm produces bad results in presence of annotated Java signatures such as:
    //      J: foo(s: String!): String -- @NotNull String foo(String s);
    //      K: foo(s: String): String?
    //  --> 'foo(s: String!): String' as an inherited signature with most specific return type.
    // This is bad because it can be overridden by 'foo(s: String?): String', which is not
    // override-equivalent with K::foo above.
    // Should be 'foo(s: String): String'.
    CallableMemberDescriptor mostSpecific =
        selectMostSpecificMember(
            effectiveOverridden,
            new Function1<CallableMemberDescriptor, CallableDescriptor>() {
              @Override
              public CallableMemberDescriptor invoke(CallableMemberDescriptor descriptor) {
                return descriptor;
              }
            });
    CallableMemberDescriptor fakeOverride =
        mostSpecific.copy(
            current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
    strategy.setOverriddenDescriptors(fakeOverride, effectiveOverridden);
    assert !fakeOverride.getOverriddenDescriptors().isEmpty()
        : "Overridden descriptors should be set for " + CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
    strategy.addFakeOverride(fakeOverride);
  }

  @NotNull
  private static Modality determineModality(
      @NotNull Collection<CallableMemberDescriptor> descriptors) {
    // Optimization: avoid creating hash sets in frequent cases when modality can be computed
    // trivially
    boolean hasOpen = false;
    boolean hasAbstract = false;
    for (CallableMemberDescriptor descriptor : descriptors) {
      switch (descriptor.getModality()) {
        case FINAL:
          return Modality.FINAL;
        case SEALED:
          throw new IllegalStateException("Member cannot have SEALED modality: " + descriptor);
        case OPEN:
          hasOpen = true;
          break;
        case ABSTRACT:
          hasAbstract = true;
          break;
      }
    }

    if (hasOpen && !hasAbstract) return Modality.OPEN;
    if (!hasOpen && hasAbstract) return Modality.ABSTRACT;

    Set<CallableMemberDescriptor> allOverriddenDeclarations =
        new HashSet<CallableMemberDescriptor>();
    for (CallableMemberDescriptor descriptor : descriptors) {
      allOverriddenDeclarations.addAll(getOverriddenDeclarations(descriptor));
    }
    return getMinimalModality(filterOutOverridden(allOverriddenDeclarations));
  }

  @NotNull
  private static Modality getMinimalModality(
      @NotNull Collection<CallableMemberDescriptor> descriptors) {
    Modality modality = Modality.ABSTRACT;
    for (CallableMemberDescriptor descriptor : descriptors) {
      if (descriptor.getModality().compareTo(modality) < 0) {
        modality = descriptor.getModality();
      }
    }
    return modality;
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
      @NotNull final ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> toFilter) {
    return CollectionsKt.filter(
        toFilter,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            // nested class could capture private member, so check for private visibility added
            return !Visibilities.isPrivate(descriptor.getVisibility())
                && Visibilities.isVisibleIgnoringReceiver(descriptor, current);
          }
        });
  }

  /**
   * @param <H> is something that handles CallableDescriptor inside
   * @return
   */
  @NotNull
  public static <H> Collection<H> extractMembersOverridableInBothWays(
      @NotNull H overrider,
      @NotNull @Mutable Collection<H> extractFrom,
      @NotNull Function1<H, CallableDescriptor> descriptorByHandle,
      @NotNull Function1<H, Unit> onConflict) {
    Collection<H> overridable = new ArrayList<H>();
    overridable.add(overrider);
    CallableDescriptor overriderDescriptor = descriptorByHandle.invoke(overrider);
    for (Iterator<H> iterator = extractFrom.iterator(); iterator.hasNext(); ) {
      H candidate = iterator.next();
      CallableDescriptor candidateDescriptor = descriptorByHandle.invoke(candidate);
      if (overrider == candidate) {
        iterator.remove();
        continue;
      }

      OverrideCompatibilityInfo.Result finalResult =
          getBothWaysOverridability(overriderDescriptor, candidateDescriptor);

      if (finalResult == OVERRIDABLE) {
        overridable.add(candidate);
        iterator.remove();
      } else if (finalResult == CONFLICT) {
        onConflict.invoke(candidate);
        iterator.remove();
      }
    }
    return overridable;
  }

  @Nullable
  public static OverrideCompatibilityInfo.Result getBothWaysOverridability(
      CallableDescriptor overriderDescriptor, CallableDescriptor candidateDescriptor) {
    OverrideCompatibilityInfo.Result result1 =
        DEFAULT.isOverridableBy(candidateDescriptor, overriderDescriptor, null).getResult();
    OverrideCompatibilityInfo.Result result2 =
        DEFAULT.isOverridableBy(overriderDescriptor, candidateDescriptor, null).getResult();

    return result1 == OVERRIDABLE && result2 == OVERRIDABLE
        ? OVERRIDABLE
        : ((result1 == CONFLICT || result2 == CONFLICT) ? CONFLICT : INCOMPATIBLE);
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
      @NotNull final CallableMemberDescriptor overrider,
      @NotNull Queue<CallableMemberDescriptor> extractFrom,
      @NotNull final OverridingStrategy strategy) {
    return extractMembersOverridableInBothWays(
        overrider,
        extractFrom,
        // ID
        new Function1<CallableMemberDescriptor, CallableDescriptor>() {
          @Override
          public CallableDescriptor invoke(CallableMemberDescriptor descriptor) {
            return descriptor;
          }
        },
        new Function1<CallableMemberDescriptor, Unit>() {
          @Override
          public Unit invoke(CallableMemberDescriptor descriptor) {
            strategy.inheritanceConflict(overrider, descriptor);
            return Unit.INSTANCE;
          }
        });
  }

  public static void resolveUnknownVisibilityForMember(
      @NotNull CallableMemberDescriptor memberDescriptor,
      @Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility) {
    for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
      if (descriptor.getVisibility() == Visibilities.INHERITED) {
        resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
      }
    }

    if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
      return;
    }

    Visibility maxVisibility = computeVisibilityToInherit(memberDescriptor);
    Visibility visibilityToInherit;
    if (maxVisibility == null) {
      if (cannotInferVisibility != null) {
        cannotInferVisibility.invoke(memberDescriptor);
      }
      visibilityToInherit = Visibilities.PUBLIC;
    } else {
      visibilityToInherit = maxVisibility;
    }

    if (memberDescriptor instanceof PropertyDescriptorImpl) {
      ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
      for (PropertyAccessorDescriptor accessor :
          ((PropertyDescriptor) memberDescriptor).getAccessors()) {
        // If we couldn't infer visibility for property, the diagnostic is already reported, no need
        // to report it again on accessors
        resolveUnknownVisibilityForMember(
            accessor, maxVisibility == null ? null : cannotInferVisibility);
      }
    } else if (memberDescriptor instanceof FunctionDescriptorImpl) {
      ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    } else {
      assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
      ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    }
  }

  @Nullable
  private static Visibility computeVisibilityToInherit(
      @NotNull CallableMemberDescriptor memberDescriptor) {
    Collection<? extends CallableMemberDescriptor> overriddenDescriptors =
        memberDescriptor.getOverriddenDescriptors();
    Visibility maxVisibility = findMaxVisibility(overriddenDescriptors);
    if (maxVisibility == null) {
      return null;
    }
    if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
      for (CallableMemberDescriptor overridden : overriddenDescriptors) {
        // An implementation (a non-abstract overridden member) of a fake override should have the
        // maximum possible visibility
        if (overridden.getModality() != Modality.ABSTRACT
            && !overridden.getVisibility().equals(maxVisibility)) {
          return null;
        }
      }
      return maxVisibility;
    }
    return maxVisibility.normalize();
  }

  @Nullable
  public static Visibility findMaxVisibility(
      @NotNull Collection<? extends CallableMemberDescriptor> descriptors) {
    if (descriptors.isEmpty()) {
      return Visibilities.DEFAULT_VISIBILITY;
    }
    Visibility maxVisibility = null;
    for (CallableMemberDescriptor descriptor : descriptors) {
      Visibility visibility = descriptor.getVisibility();
      assert visibility != Visibilities.INHERITED
          : "Visibility should have been computed for " + descriptor;
      if (maxVisibility == null) {
        maxVisibility = visibility;
        continue;
      }
      Integer compareResult = Visibilities.compare(visibility, maxVisibility);
      if (compareResult == null) {
        maxVisibility = null;
      } else if (compareResult > 0) {
        maxVisibility = visibility;
      }
    }
    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 static class OverrideCompatibilityInfo {
    public enum Result {
      OVERRIDABLE,
      INCOMPATIBLE,
      CONFLICT,
    }

    private static final OverrideCompatibilityInfo SUCCESS =
        new OverrideCompatibilityInfo(OVERRIDABLE, "SUCCESS");

    @NotNull
    public static OverrideCompatibilityInfo success() {
      return SUCCESS;
    }

    @NotNull
    public static OverrideCompatibilityInfo incompatible(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, debugMessage);
    }

    @NotNull
    public static OverrideCompatibilityInfo conflict(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(CONFLICT, debugMessage);
    }

    private final Result overridable;
    private final String debugMessage;

    public OverrideCompatibilityInfo(@NotNull Result success, @NotNull String debugMessage) {
      this.overridable = success;
      this.debugMessage = debugMessage;
    }

    @NotNull
    public Result getResult() {
      return overridable;
    }

    @NotNull
    public String getDebugMessage() {
      return debugMessage;
    }
  }
}