From 7d9b7df3b43d4c766b0b0fd4af0be70abff47893 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 1 Sep 2020 11:12:28 +0300 Subject: [PATCH] Cleanup code in EffectiveVisibility.kt and extract descriptors related part to separate file --- .../kotlin/types/AbstractTypeChecker.kt | 16 ++ .../kotlin/descriptors/EffectiveVisibility.kt | 183 +++++------------- .../descriptors/EffectiveVisibilityUtils.kt | 87 +++++++++ 3 files changed, 152 insertions(+), 134 deletions(-) create mode 100644 core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibilityUtils.kt diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index b63079e1e9c..c791d97a0c9 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -160,6 +160,22 @@ object AbstractTypeChecker { return AbstractTypeChecker.isSubtypeOf(context.newBaseTypeCheckerContext(true, stubTypesEqualToAnything), subType, superType) } + fun isSubtypeOfClass( + context: AbstractTypeCheckerContext, + typeConstructor: TypeConstructorMarker, + superConstructor: TypeConstructorMarker + ): Boolean { + if (typeConstructor == superConstructor) return true + with(context) { + for (superType in typeConstructor.supertypes()) { + if (isSubtypeOfClass(context, superType.typeConstructor(), superConstructor)) { + return true + } + } + } + return false + } + fun equalTypes( context: TypeCheckerProviderContext, a: KotlinTypeMarker, diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibility.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibility.kt index df218925706..b1373d2c60d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibility.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibility.kt @@ -16,23 +16,12 @@ package org.jetbrains.kotlin.descriptors -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.descriptors.EffectiveVisibility.* -import org.jetbrains.kotlin.descriptors.RelationToType.* -import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi +import org.jetbrains.kotlin.descriptors.EffectiveVisibility.Permissiveness import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeCheckerContext -import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext -import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContextImpl -import org.jetbrains.kotlin.types.model.KotlinTypeMarker -import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext -import org.jetbrains.kotlin.types.model.TypeSystemContext -import org.jetbrains.kotlin.types.model.typeConstructor +import org.jetbrains.kotlin.types.model.TypeConstructorMarker sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = false, val privateApi: Boolean = false) { - override fun toString() = name // Public @@ -49,31 +38,31 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals object Private : EffectiveVisibility("private", privateApi = true) { - override fun relation(other: EffectiveVisibility) = - if (this == other || Local == other) Permissiveness.SAME else Permissiveness.LESS + override fun relation(other: EffectiveVisibility): Permissiveness = + if (this == other || Local == other) Permissiveness.SAME else Permissiveness.LESS - override fun toVisibility() = Visibilities.PRIVATE + override fun toVisibility(): Visibility = Visibilities.PRIVATE } // Effectively same as Private object Local : EffectiveVisibility("local") { - override fun relation(other: EffectiveVisibility) = - if (this == other || Private == other) Permissiveness.SAME else Permissiveness.LESS + override fun relation(other: EffectiveVisibility): Permissiveness = + if (this == other || Private == other) Permissiveness.SAME else Permissiveness.LESS - override fun toVisibility() = Visibilities.LOCAL + override fun toVisibility(): Visibility = Visibilities.LOCAL } object Public : EffectiveVisibility("public", publicApi = true) { - override fun relation(other: EffectiveVisibility) = - if (this == other) Permissiveness.SAME else Permissiveness.MORE + override fun relation(other: EffectiveVisibility): Permissiveness = + if (this == other) Permissiveness.SAME else Permissiveness.MORE - override fun toVisibility() = Visibilities.PUBLIC + override fun toVisibility(): Visibility = Visibilities.PUBLIC } abstract class InternalOrPackage protected constructor(internal: Boolean) : EffectiveVisibility( - if (internal) "internal" else "public/*package*/" + if (internal) "internal" else "public/*package*/" ) { - override fun relation(other: EffectiveVisibility) = when (other) { + override fun relation(other: EffectiveVisibility): Permissiveness = when (other) { Public -> Permissiveness.LESS Private, Local, InternalProtectedBound, is InternalProtected -> Permissiveness.MORE is InternalOrPackage -> Permissiveness.SAME @@ -83,35 +72,35 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals override fun lowerBound(other: EffectiveVisibility) = when (other) { Public -> this Private, Local, InternalProtectedBound, is InternalOrPackage, is InternalProtected -> other - is Protected -> InternalProtected(other.containerType, other.typeContext) + is Protected -> InternalProtected(other.containerTypeConstructor, other.typeContext) ProtectedBound -> InternalProtectedBound } } object Internal : InternalOrPackage(true) { - override fun toVisibility() = Visibilities.INTERNAL + override fun toVisibility(): Visibility = Visibilities.INTERNAL } object PackagePrivate : InternalOrPackage(false) { - override fun toVisibility() = Visibilities.PRIVATE + override fun toVisibility(): Visibility = Visibilities.PRIVATE } class Protected( - val containerType: KotlinTypeMarker?, + val containerTypeConstructor: TypeConstructorMarker?, val typeContext: AbstractTypeCheckerContext ) : EffectiveVisibility("protected", publicApi = true) { - override fun equals(other: Any?) = (other is Protected && containerType == other.containerType) + override fun equals(other: Any?) = (other is Protected && containerTypeConstructor == other.containerTypeConstructor) - override fun hashCode() = containerType?.hashCode() ?: 0 + override fun hashCode() = containerTypeConstructor?.hashCode() ?: 0 - override fun toString() = "${super.toString()} (in ${containerType?.typeConstructor(typeContext) ?: '?'})" + override fun toString() = "${super.toString()} (in ${containerTypeConstructor ?: '?'})" - override fun relation(other: EffectiveVisibility) = when (other) { + override fun relation(other: EffectiveVisibility): Permissiveness = when (other) { Public -> Permissiveness.LESS Private, Local, ProtectedBound, InternalProtectedBound -> Permissiveness.MORE - is Protected -> containerRelation(containerType, other.containerType, typeContext) - is InternalProtected -> when (containerRelation(containerType, other.containerType, typeContext)) { + is Protected -> containerRelation(containerTypeConstructor, other.containerTypeConstructor, typeContext) + is InternalProtected -> when (containerRelation(containerTypeConstructor, other.containerTypeConstructor, typeContext)) { // Protected never can be less permissive than internal & protected Permissiveness.SAME, Permissiveness.MORE -> Permissiveness.MORE Permissiveness.UNKNOWN, Permissiveness.LESS -> Permissiveness.UNKNOWN @@ -131,15 +120,15 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals Permissiveness.LESS -> other else -> InternalProtectedBound } - is InternalOrPackage -> InternalProtected(containerType, typeContext) + is InternalOrPackage -> InternalProtected(containerTypeConstructor, typeContext) } - override fun toVisibility() = Visibilities.PROTECTED + override fun toVisibility(): Visibility = Visibilities.PROTECTED } // Lower bound for all protected visibilities object ProtectedBound : EffectiveVisibility("protected (in different classes)", publicApi = true) { - override fun relation(other: EffectiveVisibility) = when (other) { + override fun relation(other: EffectiveVisibility): Permissiveness = when (other) { Public, is Protected -> Permissiveness.LESS Private, Local, InternalProtectedBound -> Permissiveness.MORE ProtectedBound -> Permissiveness.SAME @@ -152,26 +141,26 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals is InternalOrPackage, is InternalProtected -> InternalProtectedBound } - override fun toVisibility() = Visibilities.PROTECTED + override fun toVisibility(): Visibility = Visibilities.PROTECTED } // Lower bound for internal and protected(C) class InternalProtected( - val containerType: KotlinTypeMarker?, + val containerTypeConstructor: TypeConstructorMarker?, val typeContext: AbstractTypeCheckerContext, ) : EffectiveVisibility("internal & protected") { - override fun equals(other: Any?) = (other is InternalProtected && containerType == other.containerType) + override fun equals(other: Any?) = (other is InternalProtected && containerTypeConstructor == other.containerTypeConstructor) - override fun hashCode() = containerType?.hashCode() ?: 0 + override fun hashCode() = containerTypeConstructor?.hashCode() ?: 0 - override fun toString() = "${super.toString()} (in ${containerType?.typeConstructor(typeContext) ?: '?'})" + override fun toString() = "${super.toString()} (in ${containerTypeConstructor ?: '?'})" - override fun relation(other: EffectiveVisibility) = when (other) { + override fun relation(other: EffectiveVisibility): Permissiveness = when (other) { Public, is InternalOrPackage -> Permissiveness.LESS Private, Local, InternalProtectedBound -> Permissiveness.MORE - is InternalProtected -> containerRelation(containerType, other.containerType, typeContext) - is Protected -> when (containerRelation(containerType, other.containerType, typeContext)) { + is InternalProtected -> containerRelation(containerTypeConstructor, other.containerTypeConstructor, typeContext) + is Protected -> when (containerRelation(containerTypeConstructor, other.containerTypeConstructor, typeContext)) { // Internal & protected never can be more permissive than just protected Permissiveness.SAME, Permissiveness.LESS -> Permissiveness.LESS Permissiveness.UNKNOWN, Permissiveness.MORE -> Permissiveness.UNKNOWN @@ -190,18 +179,18 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals ProtectedBound -> InternalProtectedBound } - override fun toVisibility() = Visibilities.PRIVATE + override fun toVisibility(): Visibility = Visibilities.PRIVATE } // Lower bound for internal and protected lower bound object InternalProtectedBound : EffectiveVisibility("internal & protected (in different classes)") { - override fun relation(other: EffectiveVisibility) = when (other) { + override fun relation(other: EffectiveVisibility): Permissiveness = when (other) { Public, is Protected, is InternalProtected, ProtectedBound, is InternalOrPackage -> Permissiveness.LESS Private, Local -> Permissiveness.MORE InternalProtectedBound -> Permissiveness.SAME } - override fun toVisibility() = Visibilities.PRIVATE + override fun toVisibility(): Visibility = Visibilities.PRIVATE } enum class Permissiveness { @@ -215,51 +204,13 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals abstract fun toVisibility(): Visibility - open internal fun lowerBound(other: EffectiveVisibility) = when (relation(other)) { + internal open fun lowerBound(other: EffectiveVisibility) = when (relation(other)) { Permissiveness.SAME, Permissiveness.LESS -> this Permissiveness.MORE -> other Permissiveness.UNKNOWN -> Private } } -internal fun containerRelation( - first: KotlinTypeMarker?, - second: KotlinTypeMarker?, - typeContext: AbstractTypeCheckerContext -): Permissiveness { - return when { - first == null || second == null -> Permissiveness.UNKNOWN - first == second -> Permissiveness.SAME - AbstractTypeChecker.isSubtypeOf(typeContext, first, second) -> Permissiveness.LESS - AbstractTypeChecker.isSubtypeOf(typeContext, second, first) -> Permissiveness.MORE - else -> Permissiveness.UNKNOWN - } -} - -private fun lowerBound(first: EffectiveVisibility, second: EffectiveVisibility) = - first.lowerBound(second) - -private fun lowerBound(first: EffectiveVisibility, args: List) = - args.fold(first, { x, y -> x.lowerBound(y) }) - -private fun lowerBound(args: List) = - if (args.isEmpty()) Public else lowerBound(args.first(), args.subList(1, args.size)) - -private fun Visibility.forVisibility(descriptor: DeclarationDescriptor, checkPublishedApi: Boolean = false): EffectiveVisibility = - when (this) { - Visibilities.PRIVATE, Visibilities.PRIVATE_TO_THIS, Visibilities.INVISIBLE_FAKE -> Private - Visibilities.PROTECTED -> Protected((descriptor.containingDeclaration as? ClassDescriptor)?.defaultType, ClassicTypeCheckerContext(errorTypeEqualsToAnything = false)) - Visibilities.INTERNAL -> if (!checkPublishedApi || - !descriptor.isPublishedApi()) Internal else Public - Visibilities.PUBLIC -> Public - Visibilities.LOCAL -> Local - // NB: visibility must be already normalized here, so e.g. no JavaVisibilities are possible at this point - else -> throw AssertionError("Visibility $name is not allowed in forVisibility") - } - -fun effectiveVisibility(visibility: Visibility, descriptor: DeclarationDescriptor, checkPublishedApi: Boolean = false) = - visibility.forVisibility(descriptor, checkPublishedApi) - enum class RelationToType(val description: String) { CONSTRUCTOR(""), CONTAINER(" containing declaration"), @@ -274,53 +225,17 @@ enum class RelationToType(val description: String) { override fun toString() = description } -data class DescriptorWithRelation(val descriptor: ClassifierDescriptor, private val relation: RelationToType) { - fun effectiveVisibility() = - (descriptor as? ClassDescriptor)?.visibility?.effectiveVisibility(descriptor, false) ?: Public - - override fun toString() = "$relation ${descriptor.name}" -} - -private fun ClassifierDescriptor.dependentDescriptors(ownRelation: RelationToType): Set = - setOf(DescriptorWithRelation(this, ownRelation)) + - ((this.containingDeclaration as? ClassifierDescriptor)?.dependentDescriptors(ownRelation.containerRelation()) ?: emptySet()) - -fun ClassDescriptor.effectiveVisibility(checkPublishedApi: Boolean = false) = effectiveVisibility(emptySet(), checkPublishedApi) - -private fun ClassDescriptor.effectiveVisibility(classes: Set, checkPublishedApi: Boolean): EffectiveVisibility = - if (this in classes) Public - else with(this.containingDeclaration as? ClassDescriptor) { - lowerBound(visibility.effectiveVisibility(this@effectiveVisibility, checkPublishedApi), this?.effectiveVisibility(classes + this@effectiveVisibility, checkPublishedApi) ?: Public) - } - -// Should collect all dependent classifier descriptors, to get verbose diagnostic -private fun KotlinType.dependentDescriptors() = dependentDescriptors(emptySet(), CONSTRUCTOR) - -private fun KotlinType.dependentDescriptors(types: Set, ownRelation: RelationToType): Set { - if (this in types) return emptySet() - val ownDependent = constructor.declarationDescriptor?.dependentDescriptors(ownRelation) ?: emptySet() - val argumentDependent = arguments.map { it.type.dependentDescriptors(types + this, ARGUMENT) }.flatten() - return ownDependent + argumentDependent -} - -private fun Set.leastPermissive(base: EffectiveVisibility): DescriptorWithRelation? { - for (descriptorWithRelation in this) { - val currentVisibility = descriptorWithRelation.effectiveVisibility() - when (currentVisibility.relation(base)) { - Permissiveness.LESS, Permissiveness.UNKNOWN -> { - return descriptorWithRelation - } - else -> {} - } +internal fun containerRelation( + first: TypeConstructorMarker?, + second: TypeConstructorMarker?, + typeContext: AbstractTypeCheckerContext +): Permissiveness { + return when { + first == null || second == null -> Permissiveness.UNKNOWN + first == second -> Permissiveness.SAME + AbstractTypeChecker.isSubtypeOfClass(typeContext, first, second) -> Permissiveness.LESS + AbstractTypeChecker.isSubtypeOfClass(typeContext, second, first) -> Permissiveness.MORE + else -> Permissiveness.UNKNOWN } - return null } -fun KotlinType.leastPermissiveDescriptor(base: EffectiveVisibility) = dependentDescriptors().leastPermissive(base) - -fun DeclarationDescriptorWithVisibility.effectiveVisibility( - visibility: Visibility = this.visibility, checkPublishedApi: Boolean = false -): EffectiveVisibility = - lowerBound(visibility.effectiveVisibility(this, checkPublishedApi), - (this.containingDeclaration as? ClassDescriptor)?.effectiveVisibility(checkPublishedApi) ?: Public) - diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibilityUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibilityUtils.kt new file mode 100644 index 00000000000..e6da9261d65 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/EffectiveVisibilityUtils.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.descriptors + +import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext + +fun effectiveVisibility(visibility: Visibility, descriptor: DeclarationDescriptor, checkPublishedApi: Boolean = false) = + visibility.forVisibility(descriptor, checkPublishedApi) + +private fun Visibility.forVisibility(descriptor: DeclarationDescriptor, checkPublishedApi: Boolean = false): EffectiveVisibility = + when (this) { + Visibilities.PRIVATE, Visibilities.PRIVATE_TO_THIS, Visibilities.INVISIBLE_FAKE -> EffectiveVisibility.Private + Visibilities.PROTECTED -> EffectiveVisibility.Protected( + (descriptor.containingDeclaration as? ClassDescriptor)?.defaultType?.constructor, + ClassicTypeCheckerContext(errorTypeEqualsToAnything = false) + ) + Visibilities.INTERNAL -> if (!checkPublishedApi || + !descriptor.isPublishedApi() + ) EffectiveVisibility.Internal else EffectiveVisibility.Public + Visibilities.PUBLIC -> EffectiveVisibility.Public + Visibilities.LOCAL -> EffectiveVisibility.Local + // NB: visibility must be already normalized here, so e.g. no JavaVisibilities are possible at this point + else -> throw AssertionError("Visibility $name is not allowed in forVisibility") + } + +data class DescriptorWithRelation(val descriptor: ClassifierDescriptor, private val relation: RelationToType) { + fun effectiveVisibility() = + (descriptor as? ClassDescriptor)?.visibility?.effectiveVisibility(descriptor, false) ?: EffectiveVisibility.Public + + override fun toString() = "$relation ${descriptor.name}" +} + +private fun ClassifierDescriptor.dependentDescriptors(ownRelation: RelationToType): Set = + setOf(DescriptorWithRelation(this, ownRelation)) + + ((this.containingDeclaration as? ClassifierDescriptor)?.dependentDescriptors(ownRelation.containerRelation()) ?: emptySet()) + +fun ClassDescriptor.effectiveVisibility(checkPublishedApi: Boolean = false) = effectiveVisibility(emptySet(), checkPublishedApi) + +private fun ClassDescriptor.effectiveVisibility(classes: Set, checkPublishedApi: Boolean): EffectiveVisibility = + if (this in classes) EffectiveVisibility.Public + else with(this.containingDeclaration as? ClassDescriptor) { + lowerBound( + visibility.effectiveVisibility(this@effectiveVisibility, checkPublishedApi), + this?.effectiveVisibility(classes + this@effectiveVisibility, checkPublishedApi) ?: EffectiveVisibility.Public + ) + } + +// Should collect all dependent classifier descriptors, to get verbose diagnostic +private fun KotlinType.dependentDescriptors() = dependentDescriptors(emptySet(), RelationToType.CONSTRUCTOR) + +private fun KotlinType.dependentDescriptors(types: Set, ownRelation: RelationToType): Set { + if (this in types) return emptySet() + val ownDependent = constructor.declarationDescriptor?.dependentDescriptors(ownRelation) ?: emptySet() + val argumentDependent = arguments.map { it.type.dependentDescriptors(types + this, RelationToType.ARGUMENT) }.flatten() + return ownDependent + argumentDependent +} + +private fun Set.leastPermissive(base: EffectiveVisibility): DescriptorWithRelation? { + for (descriptorWithRelation in this) { + val currentVisibility = descriptorWithRelation.effectiveVisibility() + when (currentVisibility.relation(base)) { + EffectiveVisibility.Permissiveness.LESS, EffectiveVisibility.Permissiveness.UNKNOWN -> { + return descriptorWithRelation + } + else -> { + } + } + } + return null +} + +fun KotlinType.leastPermissiveDescriptor(base: EffectiveVisibility) = dependentDescriptors().leastPermissive(base) + +fun DeclarationDescriptorWithVisibility.effectiveVisibility( + visibility: Visibility = this.visibility, checkPublishedApi: Boolean = false +): EffectiveVisibility = + lowerBound( + visibility.effectiveVisibility(this, checkPublishedApi), + (this.containingDeclaration as? ClassDescriptor)?.effectiveVisibility(checkPublishedApi) ?: EffectiveVisibility.Public + ) + +private fun lowerBound(first: EffectiveVisibility, second: EffectiveVisibility): EffectiveVisibility = first.lowerBound(second)