All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.resolve.jvm.kotlinSignature.SignaturesPropagationData Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.resolve.jvm.kotlinSignature;

import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.components.TypeUsage;
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolverKt;
import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapper;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapperKt;
import org.jetbrains.kotlin.resolve.scopes.KtScope;
import org.jetbrains.kotlin.types.*;

import java.util.*;

import static org.jetbrains.kotlin.load.java.components.TypeUsage.*;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
import static org.jetbrains.kotlin.types.Variance.INVARIANT;

public class SignaturesPropagationData {

    private static final KotlinToJvmSignatureMapper SIGNATURE_MAPPER = ServiceLoader.load(
            KotlinToJvmSignatureMapper.class,
            KotlinToJvmSignatureMapper.class.getClassLoader()
    ).iterator().next();

    private final JavaMethodDescriptor autoMethodDescriptor;

    private final List modifiedTypeParameters;
    private final ValueParameters modifiedValueParameters;

    private final KotlinType modifiedReturnType;
    private final List signatureErrors = Lists.newArrayList();
    private final List superFunctions;
    private final Map autoTypeParameterToModified;
    final ClassDescriptor containingClass;

    public SignaturesPropagationData(
            @NotNull ClassDescriptor containingClass,
            @NotNull KotlinType autoReturnType, // type built by JavaTypeTransformer from Java signature and @NotNull annotations
            @Nullable KotlinType receiverType,
            @NotNull List autoValueParameters, // descriptors built by parameters resolver
            @NotNull List autoTypeParameters, // descriptors built by signature resolver
            @NotNull JavaMethod method
    ) {
        this.containingClass = containingClass;

        autoMethodDescriptor =
                createAutoMethodDescriptor(containingClass, method, autoReturnType, receiverType, autoValueParameters, autoTypeParameters);

        superFunctions = getSuperFunctionsForMethod(method, autoMethodDescriptor, containingClass);

        autoTypeParameterToModified = JavaResolverUtils.recreateTypeParametersAndReturnMapping(autoTypeParameters, null);

        modifiedTypeParameters = modifyTypeParametersAccordingToSuperMethods(autoTypeParameters);
        modifiedReturnType = modifyReturnTypeAccordingToSuperMethods(autoReturnType);
        modifiedValueParameters = modifyValueParametersAccordingToSuperMethods(receiverType, autoValueParameters);
    }

    @NotNull
    private static JavaMethodDescriptor createAutoMethodDescriptor(
            @NotNull ClassDescriptor containingClass,
            @NotNull JavaMethod method, KotlinType autoReturnType,
            @Nullable KotlinType receiverType,
            @NotNull List autoValueParameters,
            @NotNull List autoTypeParameters
    ) {
        JavaMethodDescriptor autoMethodDescriptor = JavaMethodDescriptor.createJavaMethod(
                containingClass,
                Annotations.Companion.getEMPTY(),
                method.getName(),
                //TODO: what to do?
                SourceElement.NO_SOURCE
        );
        autoMethodDescriptor.initialize(
                receiverType,
                containingClass.getThisAsReceiverParameter(),
                autoTypeParameters,
                autoValueParameters,
                autoReturnType,
                Modality.OPEN,
                Visibilities.PUBLIC
        );
        return autoMethodDescriptor;
    }

    public List getModifiedTypeParameters() {
        return modifiedTypeParameters;
    }

    public KotlinType getModifiedReceiverType() {
        return modifiedValueParameters.receiverType;
    }

    public List getModifiedValueParameters() {
        return modifiedValueParameters.descriptors;
    }

    public boolean getModifiedHasStableParameterNames() {
        return modifiedValueParameters.hasStableParameterNames;
    }

    public KotlinType getModifiedReturnType() {
        return modifiedReturnType;
    }

    public List getSignatureErrors() {
        return signatureErrors;
    }

    public List getSuperFunctions() {
        return superFunctions;
    }

    void reportError(String error) {
        signatureErrors.add(error);
    }

    private KotlinType modifyReturnTypeAccordingToSuperMethods(
            @NotNull KotlinType autoType // type built by JavaTypeTransformer
    ) {
        if (JavaDescriptorResolverKt.getPLATFORM_TYPES()) return autoType;

        List typesFromSuperMethods = ContainerUtil.map(superFunctions,
                                                                        new Function() {
                                                                            @Override
                                                                            public TypeAndVariance fun(FunctionDescriptor superFunction) {
                                                                                return new TypeAndVariance(superFunction.getReturnType(),
                                                                                                           Variance.OUT_VARIANCE);
                                                                            }
                                                                        });

        return modifyTypeAccordingToSuperMethods(autoType, typesFromSuperMethods, MEMBER_SIGNATURE_COVARIANT);
    }

    private List modifyTypeParametersAccordingToSuperMethods(List autoTypeParameters) {
        if (JavaDescriptorResolverKt.getPLATFORM_TYPES()) return autoTypeParameters;

        List result = Lists.newArrayList();

        for (TypeParameterDescriptor autoParameter : autoTypeParameters) {
            int index = autoParameter.getIndex();
            TypeParameterDescriptorImpl modifiedTypeParameter = autoTypeParameterToModified.get(autoParameter);

            List> upperBoundFromSuperFunctionsIterators = Lists.newArrayList();
            for (FunctionDescriptor superFunction : superFunctions) {
                upperBoundFromSuperFunctionsIterators.add(superFunction.getTypeParameters().get(index).getUpperBounds().iterator());
            }

            for (KotlinType autoUpperBound : autoParameter.getUpperBounds()) {
                List upperBoundsFromSuperFunctions = Lists.newArrayList();

                for (Iterator iterator : upperBoundFromSuperFunctionsIterators) {
                    assert iterator.hasNext();
                    upperBoundsFromSuperFunctions.add(new TypeAndVariance(iterator.next(), INVARIANT));
                }

                KotlinType modifiedUpperBound = modifyTypeAccordingToSuperMethods(autoUpperBound, upperBoundsFromSuperFunctions, UPPER_BOUND);
                modifiedTypeParameter.addUpperBound(modifiedUpperBound);
            }

            for (Iterator iterator : upperBoundFromSuperFunctionsIterators) {
                assert !iterator.hasNext();
            }

            modifiedTypeParameter.setInitialized();
            result.add(modifiedTypeParameter);
        }

        return result;
    }

    private ValueParameters modifyValueParametersAccordingToSuperMethods(
            @Nullable KotlinType receiverType,
            @NotNull List parameters // descriptors built by parameters resolver
    ) {
        assert receiverType == null : "Parameters before propagation have receiver type," +
                                      " but propagation should be disabled for functions compiled from Kotlin in class: " +
                                      DescriptorUtils.getFqName(containingClass);

        KotlinType resultReceiverType = null;
        List resultParameters = new ArrayList(parameters.size());

        boolean shouldBeExtension = checkIfShouldBeExtension();

        for (final ValueParameterDescriptor originalParam : parameters) {
            final int originalIndex = originalParam.getIndex();
            List typesFromSuperMethods = ContainerUtil.map(superFunctions,
                    new Function() {
                        @Override
                        public TypeAndName fun(FunctionDescriptor superFunction) {
                            ReceiverParameterDescriptor receiver = superFunction.getExtensionReceiverParameter();
                            int index = receiver != null ? originalIndex - 1 : originalIndex;
                            if (index == -1) {
                                assert receiver != null : "can't happen: index is -1, while function is not extension";
                                return new TypeAndName(receiver.getType(), originalParam.getName());
                            }
                            ValueParameterDescriptor parameter = superFunction.getValueParameters().get(index);
                            return new TypeAndName(parameter.getType(), parameter.getName());
                        }
                    });

            VarargCheckResult varargCheckResult = checkVarargInSuperFunctions(originalParam);

            KotlinType altType = modifyTypeAccordingToSuperMethods(varargCheckResult.parameterType,
                                                                   convertToTypeVarianceList(typesFromSuperMethods),
                                                                   MEMBER_SIGNATURE_CONTRAVARIANT);

            if (shouldBeExtension && originalIndex == 0) {
                resultReceiverType = altType;
            }
            else {
                Name stableName = null;
                for (int i = 0; i < superFunctions.size(); i++) {
                    if (superFunctions.get(i).hasStableParameterNames()) {
                        // When there's more than one stable name in super functions, we pick the first one. This behaviour is similar to
                        // the compiler front-end, except that it reports a warning in such cases
                        // TODO: report a warning somewhere if there's more than one stable name in super functions
                        stableName = typesFromSuperMethods.get(i).name;
                        break;
                    }
                }

                resultParameters.add(new ValueParameterDescriptorImpl(
                        originalParam.getContainingDeclaration(),
                        null,
                        shouldBeExtension ? originalIndex - 1 : originalIndex,
                        originalParam.getAnnotations(),
                        stableName != null ? stableName : originalParam.getName(),
                        altType,
                        originalParam.declaresDefaultValue(),
                        originalParam.isCrossinline(),
                        originalParam.isNoinline(),
                        varargCheckResult.isVararg ? DescriptorUtilsKt.getBuiltIns(originalParam).getArrayElementType(altType) : null,
                        SourceElement.NO_SOURCE
                ));
            }
        }

        boolean hasStableParameterNames = CollectionsKt.any(superFunctions, new Function1() {
            @Override
            public Boolean invoke(FunctionDescriptor descriptor) {
                return descriptor.hasStableParameterNames();
            }
        });

        return new ValueParameters(resultReceiverType, resultParameters, hasStableParameterNames);
    }

    @NotNull
    private static List convertToTypeVarianceList(@NotNull List list) {
        return CollectionsKt.map(list, new Function1() {
            @Override
            public TypeAndVariance invoke(TypeAndName tvn) {
                return new TypeAndVariance(tvn.type, INVARIANT);
            }
        });
    }

    private static List getSuperFunctionsForMethod(
            @NotNull JavaMethod method,
            @NotNull JavaMethodDescriptor autoMethodDescriptor,
            @NotNull ClassDescriptor containingClass
    ) {
        List superFunctions = Lists.newArrayList();

        // TODO: Add propagation for other kotlin descriptors (KT-3621)
        Name name = method.getName();
        JvmMethodSignature autoSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(autoMethodDescriptor);
        for (KotlinType supertype : containingClass.getTypeConstructor().getSupertypes()) {
            Collection superFunctionCandidates = supertype.getMemberScope().getFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS);
            for (FunctionDescriptor candidate : superFunctionCandidates) {
                JvmMethodSignature candidateSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(candidate);
                if (KotlinToJvmSignatureMapperKt.erasedSignaturesEqualIgnoringReturnTypes(autoSignature, candidateSignature)) {
                    superFunctions.add(candidate);
                }
            }
        }

        // sorting for diagnostic stability
        Collections.sort(superFunctions, new Comparator() {
            @Override
            public int compare(@NotNull FunctionDescriptor fun1, @NotNull FunctionDescriptor fun2) {
                FqNameUnsafe fqName1 = getFqName(fun1.getContainingDeclaration());
                FqNameUnsafe fqName2 = getFqName(fun2.getContainingDeclaration());
                return fqName1.asString().compareTo(fqName2.asString());
            }
        });
        return superFunctions;
    }

    private boolean checkIfShouldBeExtension() {
        boolean someSupersExtension = false;
        boolean someSupersNotExtension = false;

        for (FunctionDescriptor superFunction : superFunctions) {
            if (superFunction.getExtensionReceiverParameter() != null)  {
                someSupersExtension = true;
            }
            else {
                someSupersNotExtension = true;
            }
        }

        if (someSupersExtension) {
            if (someSupersNotExtension) {
                reportError("Incompatible super methods: some are extension functions, some are not");
            }
            else {
                return true;
            }
        }
        return false;
    }

    @NotNull
    private VarargCheckResult checkVarargInSuperFunctions(@NotNull ValueParameterDescriptor originalParam) {
        boolean someSupersVararg = false;
        boolean someSupersNotVararg = false;
        for (FunctionDescriptor superFunction : superFunctions) {
            int originalIndex = originalParam.getIndex();
            int index = superFunction.getExtensionReceiverParameter() != null ? originalIndex - 1 : originalIndex;
            if (index != -1 && superFunction.getValueParameters().get(index).getVarargElementType() != null) {
                someSupersVararg = true;
            }
            else {
                someSupersNotVararg = true;
            }
        }

        KotlinType originalVarargElementType = originalParam.getVarargElementType();
        KotlinType originalType = originalParam.getType();

        if (someSupersVararg && someSupersNotVararg) {
            reportError("Incompatible super methods: some have vararg parameter, some have not");
            return new VarargCheckResult(originalType, originalVarargElementType != null);
        }

        if (someSupersVararg && originalVarargElementType == null) {
            // convert to vararg

            assert isArrayType(originalType);
            return new VarargCheckResult(TypeUtils.makeNotNullable(originalType), true);
        }
        else if (someSupersNotVararg && originalVarargElementType != null) {
            // convert to non-vararg

            assert isArrayType(originalType);
            return new VarargCheckResult(TypeUtils.makeNullable(originalType), false);
        }
        return new VarargCheckResult(originalType, originalVarargElementType != null);
    }

    @NotNull
    private KotlinType modifyTypeAccordingToSuperMethods(
            @NotNull KotlinType autoType,
            @NotNull List typesFromSuper,
            @NotNull TypeUsage howThisTypeIsUsed
    ) {
        if (autoType.isError()) return autoType;

        if (JavaDescriptorResolverKt.getPLATFORM_TYPES()) return autoType;

        boolean resultNullable = typeMustBeNullable(autoType, typesFromSuper, howThisTypeIsUsed);
        ClassifierDescriptor resultClassifier = modifyTypeClassifier(autoType, typesFromSuper);
        List resultArguments = getTypeArgsOfType(autoType, resultClassifier, typesFromSuper);
        KtScope resultScope;
        if (resultClassifier instanceof ClassDescriptor) {
            resultScope = ((ClassDescriptor) resultClassifier).getMemberScope(resultArguments);
        }
        else {
            resultScope = autoType.getMemberScope();
        }

        KotlinType type = KotlinTypeImpl.create(autoType.getAnnotations(),
                                                resultClassifier.getTypeConstructor(),
                                                resultNullable,
                                                resultArguments,
                                                resultScope);

        PropagationHeuristics.checkArrayInReturnType(this, type, typesFromSuper);
        return type;
    }

    @NotNull
    private List getTypeArgsOfType(
            @NotNull KotlinType autoType,
            @NotNull ClassifierDescriptor classifier,
            @NotNull List typesFromSuper
    ) {
        if (typesFromSuper.isEmpty()) return autoType.getArguments();

        List autoArguments = autoType.getArguments();

        if (!(classifier instanceof ClassDescriptor)) {
            assert autoArguments.isEmpty() :
                    "Unexpected type arguments when type constructor is not ClassDescriptor, type = " + autoType +
                    ", classifier = " + classifier + ", classifier class = " + classifier.getClass();
            return autoArguments;
        }

        List> typeArgumentsFromSuper = calculateTypeArgumentsFromSuper((ClassDescriptor) classifier,
                                                                                                       typesFromSuper);

        // Modify type arguments using info from typesFromSuper
        List resultArguments = Lists.newArrayList();
        for (TypeParameterDescriptor parameter : classifier.getTypeConstructor().getParameters()) {
            TypeProjection argument = autoArguments.get(parameter.getIndex());

            KotlinType argumentType = argument.getType();
            List projectionsFromSuper = typeArgumentsFromSuper.get(parameter.getIndex());
            List argTypesFromSuper = getTypes(projectionsFromSuper);

            KotlinType type = modifyTypeAccordingToSuperMethods(argumentType, argTypesFromSuper, TYPE_ARGUMENT);
            Variance projectionKind = calculateArgumentProjectionKindFromSuper(argument, projectionsFromSuper);

            resultArguments.add(new TypeProjectionImpl(projectionKind, type));
        }
        return resultArguments;
    }

    private Variance calculateArgumentProjectionKindFromSuper(
            @NotNull TypeProjection argument,
            @NotNull List projectionsFromSuper
    ) {
        if (projectionsFromSuper.isEmpty()) return argument.getProjectionKind();

        Set projectionKindsInSuper = Sets.newLinkedHashSet();
        for (TypeProjectionAndVariance projectionAndVariance : projectionsFromSuper) {
            projectionKindsInSuper.add(projectionAndVariance.typeProjection.getProjectionKind());
        }

        Variance defaultProjectionKind = argument.getProjectionKind();
        if (projectionKindsInSuper.size() == 0) {
            return defaultProjectionKind;
        }
        else if (projectionKindsInSuper.size() == 1) {
            Variance projectionKindInSuper = projectionKindsInSuper.iterator().next();
            if (defaultProjectionKind == INVARIANT || defaultProjectionKind == projectionKindInSuper) {
                return projectionKindInSuper;
            }
            else {
                reportError("Incompatible projection kinds in type arguments of super methods' return types: "
                            + projectionsFromSuper + ", defined in current: " + argument);
                return defaultProjectionKind;
            }
        }
        else {
            reportError("Incompatible projection kinds in type arguments of super methods' return types: " + projectionsFromSuper);
            return defaultProjectionKind;
        }
    }

    @NotNull
    private static List getTypes(@NotNull List projections) {
        List types = Lists.newArrayList();
        for (TypeProjectionAndVariance projection : projections) {
            types.add(new TypeAndVariance(projection.typeProjection.getType(),
                                          merge(projection.varianceOfPosition, projection.typeProjection.getProjectionKind())));
        }
        return types;
    }

    private static Variance merge(Variance positionOfOuter, Variance projectionKind) {
        // Inv>, X is in invariant position
        if (positionOfOuter == INVARIANT) return INVARIANT;
        // Out, X is in out-position
        if (projectionKind == INVARIANT) return positionOfOuter;
        // Out>, X is in out-position
        // In>, X is in out-position
        // Out>, X is in in-position
        // In>, X is in in-position
        return positionOfOuter.superpose(projectionKind);
    }

    // Returns list with type arguments info from supertypes
    // Example:
    //     - Foo is a subtype of Bar>, Baz
    //     - input: klass = Foo, typesFromSuper = [Bar>, Baz]
    //     - output[0] = [String, CharSequence], output[1] = []
    private static List> calculateTypeArgumentsFromSuper(
            @NotNull ClassDescriptor klass,
            @NotNull Collection typesFromSuper
    ) {
        // For each superclass of klass and its parameters, hold their mapping to klass' parameters
        // #0 of Bar ->  A
        // #1 of Bar ->  List
        // #0 of Baz ->  Boolean
        // #1 of Baz ->  A
        // #0 of Foo ->  A (mapped to itself)
        // #1 of Foo ->  B (mapped to itself)
        Multimap substitution = SubstitutionUtils.buildDeepSubstitutionMultimap(
                TypeUtils.makeUnsubstitutedType(klass, ErrorUtils.createErrorScope("Do not access this scope", true)));

        // for each parameter of klass, hold arguments in corresponding supertypes
        List> parameterToArgumentsFromSuper = Lists.newArrayList();
        for (TypeParameterDescriptor ignored : klass.getTypeConstructor().getParameters()) {
            parameterToArgumentsFromSuper.add(new ArrayList());
        }

        // Enumerate all types from super and all its parameters
        for (TypeAndVariance typeFromSuper : typesFromSuper) {
            for (TypeParameterDescriptor parameter : typeFromSuper.type.getConstructor().getParameters()) {
                TypeProjection argument = typeFromSuper.type.getArguments().get(parameter.getIndex());

                // for given example, this block is executed four times:
                // 1. typeFromSuper = Bar>,      parameter = "#0 of Bar",  argument = String
                // 2. typeFromSuper = Bar>,      parameter = "#1 of Bar",  argument = List
                // 3. typeFromSuper = Baz,  parameter = "#0 of Baz",  argument = Boolean
                // 4. typeFromSuper = Baz,  parameter = "#1 of Baz",  argument = CharSequence

                // if it is mapped to klass' parameter, then store it into map
                for (TypeProjection projection : substitution.get(parameter.getTypeConstructor())) {
                    // 1. projection = A
                    // 2. projection = List
                    // 3. projection = Boolean
                    // 4. projection = A
                    ClassifierDescriptor classifier = projection.getType().getConstructor().getDeclarationDescriptor();

                    // this condition is true for 1 and 4, false for 2 and 3
                    if (classifier instanceof TypeParameterDescriptor && classifier.getContainingDeclaration() == klass) {
                        int parameterIndex = ((TypeParameterDescriptor) classifier).getIndex();
                        Variance effectiveVariance = parameter.getVariance().superpose(typeFromSuper.varianceOfPosition);
                        parameterToArgumentsFromSuper.get(parameterIndex).add(new TypeProjectionAndVariance(argument, effectiveVariance));
                    }
                }
            }
        }
        return parameterToArgumentsFromSuper;
    }

    private boolean typeMustBeNullable(
            @NotNull KotlinType autoType,
            @NotNull List typesFromSuper,
            @NotNull TypeUsage howThisTypeIsUsed
    ) {
        boolean someSupersNotCovariantNullable = false;
        boolean someSupersCovariantNullable = false;
        boolean someSupersNotNull = false;
        for (TypeAndVariance typeFromSuper : typesFromSuper) {
            if (!TypeUtils.isNullableType(typeFromSuper.type)) {
                someSupersNotNull = true;
            }
            else {
                if (typeFromSuper.varianceOfPosition == Variance.OUT_VARIANCE) {
                    someSupersCovariantNullable = true;
                }
                else {
                    someSupersNotCovariantNullable = true;
                }
            }
        }

        if (someSupersNotNull && someSupersNotCovariantNullable) {
            reportError("Incompatible types in superclasses: " + typesFromSuper);
            return TypeUtils.isNullableType(autoType);
        }
        else if (someSupersNotNull) {
            return false;
        }
        else if (someSupersNotCovariantNullable || someSupersCovariantNullable) {
            boolean annotatedAsNotNull = howThisTypeIsUsed != TYPE_ARGUMENT && !TypeUtils.isNullableType(autoType);

            if (annotatedAsNotNull && someSupersNotCovariantNullable) {
                DescriptorRenderer renderer = DescriptorRenderer.SHORT_NAMES_IN_TYPES;
                reportError("In superclass type is nullable: " + typesFromSuper + ", in subclass it is not: " + renderer.renderType(autoType));
                return true;
            }

            return !annotatedAsNotNull;
        }
        return TypeUtils.isNullableType(autoType);
    }

    @NotNull
    private ClassifierDescriptor modifyTypeClassifier(
            @NotNull KotlinType autoType,
            @NotNull List typesFromSuper
    ) {
        ClassifierDescriptor classifier = autoType.getConstructor().getDeclarationDescriptor();
        if (!(classifier instanceof ClassDescriptor)) {
            assert classifier != null : "no declaration descriptor for type " + autoType + ", auto method descriptor: " + autoMethodDescriptor;

            if (classifier instanceof TypeParameterDescriptor && autoTypeParameterToModified.containsKey(classifier)) {
                return autoTypeParameterToModified.get(classifier);
            }
            return classifier;
        }
        ClassDescriptor klass = (ClassDescriptor) classifier;

        boolean someSupersMutable = false;
        boolean someSupersCovariantReadOnly = false;
        boolean someSupersNotCovariantReadOnly = false;
        for (TypeAndVariance typeFromSuper : typesFromSuper) {
            ClassifierDescriptor classifierFromSuper = typeFromSuper.type.getConstructor().getDeclarationDescriptor();
            if (classifierFromSuper instanceof ClassDescriptor) {
                ClassDescriptor classFromSuper = (ClassDescriptor) classifierFromSuper;

                if (JavaToKotlinClassMap.INSTANCE.isMutable(classFromSuper)) {
                    someSupersMutable = true;
                }
                else if (JavaToKotlinClassMap.INSTANCE.isReadOnly(classFromSuper)) {
                    if (typeFromSuper.varianceOfPosition == Variance.OUT_VARIANCE) {
                        someSupersCovariantReadOnly = true;
                    }
                    else {
                        someSupersNotCovariantReadOnly = true;
                    }
                }
            }
        }

        if (someSupersMutable && someSupersNotCovariantReadOnly) {
            reportError("Incompatible types in superclasses: " + typesFromSuper);
            return classifier;
        }
        else if (someSupersMutable) {
            if (JavaToKotlinClassMap.INSTANCE.isReadOnly(klass)) {
                return JavaToKotlinClassMap.INSTANCE.convertReadOnlyToMutable(klass);
            }
        }
        else if (someSupersNotCovariantReadOnly || someSupersCovariantReadOnly) {
            if (JavaToKotlinClassMap.INSTANCE.isMutable(klass)) {
                return JavaToKotlinClassMap.INSTANCE.convertMutableToReadOnly(klass);
            }
        }

        ClassifierDescriptor fixed = PropagationHeuristics.tryToFixOverridingTWithRawType(this, typesFromSuper);
        return fixed != null ? fixed : classifier;
    }

    private static boolean isArrayType(@NotNull KotlinType type) {
        return KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type);
    }

    private static class VarargCheckResult {
        public final KotlinType parameterType;
        public final boolean isVararg;

        public VarargCheckResult(KotlinType parameterType, boolean isVararg) {
            this.parameterType = parameterType;
            this.isVararg = isVararg;
        }
    }

    private static class TypeProjectionAndVariance {
        public final TypeProjection typeProjection;
        public final Variance varianceOfPosition;

        public TypeProjectionAndVariance(TypeProjection typeProjection, Variance varianceOfPosition) {
            this.typeProjection = typeProjection;
            this.varianceOfPosition = varianceOfPosition;
        }

        public String toString() {
            return typeProjection.toString();
        }
    }

    static class TypeAndVariance {
        public final KotlinType type;
        public final Variance varianceOfPosition;

        public TypeAndVariance(KotlinType type, Variance varianceOfPosition) {
            this.type = type;
            this.varianceOfPosition = varianceOfPosition;
        }

        public String toString() {
            return type.toString();
        }
    }

    private static class TypeAndName {
        public final KotlinType type;
        public final Name name;

        public TypeAndName(KotlinType type, Name name) {
            this.type = type;
            this.name = name;
        }
    }

    private static class ValueParameters {
        private final KotlinType receiverType;
        private final List descriptors;
        private final boolean hasStableParameterNames;

        public ValueParameters(
                @Nullable KotlinType receiverType,
                @NotNull List descriptors,
                boolean hasStableParameterNames
        ) {
            this.receiverType = receiverType;
            this.descriptors = descriptors;
            this.hasStableParameterNames = hasStableParameterNames;
        }
    }
}