From f31cf90de2763577500835de8e35fbe5376805f2 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Mon, 4 Apr 2022 19:24:53 +0300 Subject: [PATCH] [FE 1.0] Unbind type parameter erasing from java --- .../load/java/components/TypeUsage.java | 26 --- .../LazyJavaAnnotationDescriptor.kt | 2 +- .../descriptors/LazyJavaClassDescriptor.kt | 2 +- .../descriptors/LazyJavaClassMemberScope.kt | 2 +- .../java/lazy/descriptors/LazyJavaScope.kt | 2 +- .../LazyJavaTypeParameterDescriptor.kt | 2 +- .../java/lazy/types/JavaTypeAttributes.kt | 56 +++++++ .../java/lazy/types/JavaTypeFlexibility.kt | 12 ++ .../load/java/lazy/types/JavaTypeResolver.kt | 58 ++----- .../java/lazy/types/RawProjectionComputer.kt | 53 ++++++ .../load/java/lazy/types/RawSubstitution.kt | 94 +++++++++++ .../kotlin/load/java/lazy/types/RawType.kt | 111 ------------- .../types/TypeParameterUpperBoundEraser.kt | 131 --------------- .../kotlin/types/ErasureProjectionComputer.kt | 17 ++ .../kotlin/types/ErasureTypeAttributes.kt | 35 ++++ .../types/TypeParameterErasureOptions.kt | 11 ++ .../types/TypeParameterUpperBoundEraser.kt | 157 ++++++++++++++++++ .../org/jetbrains/kotlin/types/TypeUsage.kt | 10 ++ .../org/jetbrains/kotlin/types/TypeUtils.java | 11 ++ .../org/jetbrains/kotlin/types/TypeUtils.kt | 16 -- 20 files changed, 471 insertions(+), 337 deletions(-) delete mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/TypeUsage.java create mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeAttributes.kt create mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeFlexibility.kt create mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawProjectionComputer.kt create mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawSubstitution.kt delete mode 100644 core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/TypeParameterUpperBoundEraser.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/ErasureProjectionComputer.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/ErasureTypeAttributes.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterErasureOptions.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterUpperBoundEraser.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/TypeUsage.kt diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/TypeUsage.java b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/TypeUsage.java deleted file mode 100644 index 3da59686bc9..00000000000 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/TypeUsage.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.load.java.components; - -/** - * We convert Java types differently, depending on where they occur in the Java code - * This enum encodes the kinds of occurrences - */ -public enum TypeUsage { - SUPERTYPE, - COMMON -} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt index d7cf2148afd..f1fa90cc110 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils -import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage import org.jetbrains.kotlin.load.java.descriptors.PossiblyExternalAnnotationDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.types.toAttributes diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt index 57fe4f315d4..65ce2e3216f 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.load.java.FakePureImplementationsProvider import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.components.JavaResolverCache -import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index 03d6eb484b3..f9163ff963e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature. import org.jetbrains.kotlin.load.java.ClassicBuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.sameAsRenamedInJvmBuiltin import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils.resolveOverridesForNonStaticMembers -import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage import org.jetbrains.kotlin.load.java.descriptors.* import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.childForMethod diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt index 232bf763f5c..874965240ca 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaTypeParameterDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaTypeParameterDescriptor.kt index d8a95afa797..192eadbfb8e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaTypeParameterDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaTypeParameterDescriptor.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.load.java.lazy.descriptors import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.impl.AbstractLazyTypeParameterDescriptor -import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.types.toAttributes diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeAttributes.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeAttributes.kt new file mode 100644 index 00000000000..593d65ee811 --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeAttributes.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2022 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.load.java.lazy.types + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.types.ErasureTypeAttributes +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.TypeUsage + +data class JavaTypeAttributes( + override val howThisTypeIsUsed: TypeUsage, + val flexibility: JavaTypeFlexibility = JavaTypeFlexibility.INFLEXIBLE, + val isRaw: Boolean = false, + val isForAnnotationParameter: Boolean = false, + // we use it to prevent happening a recursion while compute type parameter's upper bounds + override val visitedTypeParameters: Set? = null, + override val defaultType: SimpleType? = null +) : ErasureTypeAttributes(howThisTypeIsUsed, visitedTypeParameters, defaultType) { + fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility) + fun markIsRaw(isRaw: Boolean) = copy(isRaw = isRaw) + override fun withDefaultType(type: SimpleType?) = copy(defaultType = type) + override fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) = + copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter)) + + override fun equals(other: Any?): Boolean { + if (other !is JavaTypeAttributes) return false + return other.defaultType == this.defaultType + && other.howThisTypeIsUsed == this.howThisTypeIsUsed + && other.flexibility == this.flexibility + && other.isRaw == this.isRaw + && other.isForAnnotationParameter == this.isForAnnotationParameter + } + + override fun hashCode(): Int { + var result = defaultType.hashCode() + result += 31 * result + howThisTypeIsUsed.hashCode() + result += 31 * result + flexibility.hashCode() + result += 31 * result + if (isRaw) 1 else 0 + result += 31 * result + if (isForAnnotationParameter) 1 else 0 + return result + } +} + +fun TypeUsage.toAttributes( + isForAnnotationParameter: Boolean = false, + isRaw: Boolean = false, + upperBoundForTypeParameter: TypeParameterDescriptor? = null +) = JavaTypeAttributes( + this, + isRaw = isRaw, + isForAnnotationParameter = isForAnnotationParameter, + visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf) +) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeFlexibility.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeFlexibility.kt new file mode 100644 index 00000000000..1d0496eb926 --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeFlexibility.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2022 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.load.java.lazy.types + +enum class JavaTypeFlexibility { + INFLEXIBLE, + FLEXIBLE_UPPER_BOUND, + FLEXIBLE_LOWER_BOUND +} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt index ab58793a55d..e439434b5c0 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt @@ -20,9 +20,9 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMapper import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.load.java.components.TypeUsage -import org.jetbrains.kotlin.load.java.components.TypeUsage.COMMON -import org.jetbrains.kotlin.load.java.components.TypeUsage.SUPERTYPE +import org.jetbrains.kotlin.types.TypeUsage +import org.jetbrains.kotlin.types.TypeUsage.COMMON +import org.jetbrains.kotlin.types.TypeUsage.SUPERTYPE import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection import org.jetbrains.kotlin.types.Variance.* import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.types.error.ErrorTypeKind @@ -43,8 +44,8 @@ class JavaTypeResolver( private val c: LazyJavaResolverContext, private val typeParameterResolver: TypeParameterResolver ) { - private val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser() - private val rawSubstitution = RawSubstitution(typeParameterUpperBoundEraser) + private val projectionComputer = RawProjectionComputer() + private val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser(projectionComputer) fun transformJavaType(javaType: JavaType?, attr: JavaTypeAttributes): KotlinType { return when (javaType) { @@ -225,15 +226,15 @@ class JavaTypeResolver( val erasedUpperBound = LazyWrappedType(c.storageManager) { typeParameterUpperBoundEraser.getErasedUpperBound( parameter, - javaType.isRaw, - attr.withDefaultType(constructor.declarationDescriptor?.defaultType) + attr.withDefaultType(constructor.declarationDescriptor?.defaultType).markIsRaw(javaType.isRaw) ) } - rawSubstitution.computeProjection( + projectionComputer.computeProjection( parameter, // if erasure happens due to invalid arguments number, use star projections instead - if (javaType.isRaw) attr else attr.withFlexibility(INFLEXIBLE), + attr.markIsRaw(javaType.isRaw), + typeParameterUpperBoundEraser, erasedUpperBound ) } @@ -309,42 +310,3 @@ class JavaTypeResolver( return !isForAnnotationParameter && howThisTypeIsUsed != SUPERTYPE } } - -internal fun makeStarProjection( - typeParameter: TypeParameterDescriptor, - attr: JavaTypeAttributes -): TypeProjection { - return if (attr.howThisTypeIsUsed == SUPERTYPE) - TypeProjectionImpl(typeParameter.starProjectionType()) - else - StarProjectionImpl(typeParameter) -} - -data class JavaTypeAttributes( - val howThisTypeIsUsed: TypeUsage, - val flexibility: JavaTypeFlexibility = INFLEXIBLE, - val isForAnnotationParameter: Boolean = false, - // we use it to prevent happening a recursion while compute type parameter's upper bounds - val visitedTypeParameters: Set? = null, - val defaultType: SimpleType? = null -) { - fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility) - fun withDefaultType(type: SimpleType?) = copy(defaultType = type) - fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) = - copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter)) -} - -enum class JavaTypeFlexibility { - INFLEXIBLE, - FLEXIBLE_UPPER_BOUND, - FLEXIBLE_LOWER_BOUND -} - -fun TypeUsage.toAttributes( - isForAnnotationParameter: Boolean = false, - upperBoundForTypeParameter: TypeParameterDescriptor? = null -) = JavaTypeAttributes( - this, - isForAnnotationParameter = isForAnnotationParameter, - visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf) -) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawProjectionComputer.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawProjectionComputer.kt new file mode 100644 index 00000000000..a452c0cee8c --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawProjectionComputer.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2022 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.load.java.lazy.types + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.TypeParameterUpperBoundEraser +import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection + +class RawProjectionComputer : ErasureProjectionComputer() { + override fun computeProjection( + parameter: TypeParameterDescriptor, + typeAttr: ErasureTypeAttributes, + typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser, + erasedUpperBound: KotlinType + ): TypeProjection { + if (typeAttr !is JavaTypeAttributes) { + return super.computeProjection(parameter, typeAttr, typeParameterUpperBoundEraser, erasedUpperBound) + } + + // if erasure happens due to invalid arguments number, use star projections instead + val newTypeAttr = if (typeAttr.isRaw) typeAttr else typeAttr.withFlexibility(JavaTypeFlexibility.INFLEXIBLE) + + return when (newTypeAttr.flexibility) { + // Raw(List) => (List..List<*>) + // Raw(Enum) => (Enum>..Enum>) + // In the last case upper bound is equal to star projection `Enum<*>`, + // but we want to keep matching tree structure of flexible bounds (at least they should have the same size) + JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND -> TypeProjectionImpl( + // T : String -> String + // in T : String -> String + // T : Enum -> Enum<*> + Variance.INVARIANT, erasedUpperBound + ) + JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> { + if (!parameter.variance.allowsOutPosition) { + // in T -> Comparable + TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType) + } else if (erasedUpperBound.constructor.parameters.isNotEmpty()) { + // T : Enum -> out Enum<*> + TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound) + } else { + // T : String -> * + makeStarProjection(parameter, newTypeAttr) + } + } + } + } +} \ No newline at end of file diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawSubstitution.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawSubstitution.kt new file mode 100644 index 00000000000..ce8dfd7d93b --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawSubstitution.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2010-2022 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.load.java.lazy.types + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.types.TypeUsage +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.TypeParameterUpperBoundEraser +import org.jetbrains.kotlin.types.error.ErrorTypeKind +import org.jetbrains.kotlin.types.error.ErrorUtils + +internal class RawSubstitution(typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser? = null) : TypeSubstitution() { + private val projectionComputer = RawProjectionComputer() + private val typeParameterUpperBoundEraser = typeParameterUpperBoundEraser ?: TypeParameterUpperBoundEraser(projectionComputer) + + override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key)) + + private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType { + return when (val declaration = type.constructor.declarationDescriptor) { + is TypeParameterDescriptor -> + eraseType(typeParameterUpperBoundEraser.getErasedUpperBound(declaration, attr.markIsRaw(true)), attr) + is ClassDescriptor -> { + val declarationForUpper = + type.upperIfFlexible().constructor.declarationDescriptor + + check(declarationForUpper is ClassDescriptor) { + "For some reason declaration for upper bound is not a class " + + "but \"$declarationForUpper\" while for lower it's \"$declaration\"" + } + + val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr) + val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declarationForUpper, upperTypeAttr) + + if (isRawL || isRawU) { + RawTypeImpl(lower, upper) + } else { + KotlinTypeFactory.flexibleType(lower, upper) + } + } + else -> error("Unexpected declaration kind: $declaration") + } + } + + // false means that type cannot be raw + private fun eraseInflexibleBasedOnClassDescriptor( + type: SimpleType, declaration: ClassDescriptor, attr: JavaTypeAttributes + ): Pair { + if (type.constructor.parameters.isEmpty()) return type to false + + if (KotlinBuiltIns.isArray(type)) { + val componentTypeProjection = type.arguments[0] + val arguments = listOf( + TypeProjectionImpl(componentTypeProjection.projectionKind, eraseType(componentTypeProjection.type, attr)) + ) + return KotlinTypeFactory.simpleType( + type.attributes, type.constructor, arguments, type.isMarkedNullable + ) to false + } + + if (type.isError) { + return ErrorUtils.createErrorType(ErrorTypeKind.ERROR_RAW_TYPE, type.constructor.toString()) to false + } + + val memberScope = declaration.getMemberScope(this) + return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope( + type.attributes, declaration.typeConstructor, + declaration.typeConstructor.parameters.map { parameter -> + projectionComputer.computeProjection(parameter, attr, typeParameterUpperBoundEraser) + }, + type.isMarkedNullable, memberScope + ) factory@{ kotlinTypeRefiner -> + val classId = (declaration as? ClassDescriptor)?.classId ?: return@factory null + + @OptIn(TypeRefinement::class) + val refinedClassDescriptor = kotlinTypeRefiner.findClassAcrossModuleDependencies(classId) ?: return@factory null + if (refinedClassDescriptor == declaration) return@factory null + + eraseInflexibleBasedOnClassDescriptor(type, refinedClassDescriptor, attr).first + } to true + } + + override fun isEmpty() = false + + companion object { + private val lowerTypeAttr = TypeUsage.COMMON.toAttributes(isRaw = true).withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND) + private val upperTypeAttr = TypeUsage.COMMON.toAttributes(isRaw = true).withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND) + } +} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawType.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawType.kt index be943a8b10b..14291709203 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawType.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/RawType.kt @@ -16,21 +16,14 @@ package org.jetbrains.kotlin.load.java.lazy.types -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.load.java.components.TypeUsage import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRendererOptions -import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner import org.jetbrains.kotlin.types.TypeRefinement -import org.jetbrains.kotlin.types.error.ErrorUtils -import org.jetbrains.kotlin.types.error.ErrorTypeKind import org.jetbrains.kotlin.types.typeUtil.builtIns class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: SimpleType, disableAssertion: Boolean) : @@ -102,107 +95,3 @@ class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: Simple ) } } - -internal class RawSubstitution(typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser? = null) : TypeSubstitution() { - private val typeParameterUpperBoundEraser = typeParameterUpperBoundEraser ?: TypeParameterUpperBoundEraser(this) - - override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key)) - - private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType { - return when (val declaration = type.constructor.declarationDescriptor) { - is TypeParameterDescriptor -> - eraseType(typeParameterUpperBoundEraser.getErasedUpperBound(declaration, isRaw = true, attr), attr) - is ClassDescriptor -> { - val declarationForUpper = - type.upperIfFlexible().constructor.declarationDescriptor - - check(declarationForUpper is ClassDescriptor) { - "For some reason declaration for upper bound is not a class " + - "but \"$declarationForUpper\" while for lower it's \"$declaration\"" - } - - val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr) - val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declarationForUpper, upperTypeAttr) - - if (isRawL || isRawU) { - RawTypeImpl(lower, upper) - } else { - KotlinTypeFactory.flexibleType(lower, upper) - } - } - else -> error("Unexpected declaration kind: $declaration") - } - } - - // false means that type cannot be raw - private fun eraseInflexibleBasedOnClassDescriptor( - type: SimpleType, declaration: ClassDescriptor, attr: JavaTypeAttributes - ): Pair { - if (type.constructor.parameters.isEmpty()) return type to false - - if (KotlinBuiltIns.isArray(type)) { - val componentTypeProjection = type.arguments[0] - val arguments = listOf( - TypeProjectionImpl(componentTypeProjection.projectionKind, eraseType(componentTypeProjection.type, attr)) - ) - return KotlinTypeFactory.simpleType( - type.attributes, type.constructor, arguments, type.isMarkedNullable - ) to false - } - - if (type.isError) { - return ErrorUtils.createErrorType(ErrorTypeKind.ERROR_RAW_TYPE, type.constructor.toString()) to false - } - - val memberScope = declaration.getMemberScope(this) - return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope( - type.attributes, declaration.typeConstructor, - declaration.typeConstructor.parameters.map { parameter -> - computeProjection(parameter, attr) - }, - type.isMarkedNullable, memberScope - ) factory@{ kotlinTypeRefiner -> - val classId = (declaration as? ClassDescriptor)?.classId ?: return@factory null - @OptIn(TypeRefinement::class) - val refinedClassDescriptor = kotlinTypeRefiner.findClassAcrossModuleDependencies(classId) ?: return@factory null - if (refinedClassDescriptor == declaration) return@factory null - - eraseInflexibleBasedOnClassDescriptor(type, refinedClassDescriptor, attr).first - } to true - } - - fun computeProjection( - parameter: TypeParameterDescriptor, - attr: JavaTypeAttributes, - erasedUpperBound: KotlinType = typeParameterUpperBoundEraser.getErasedUpperBound(parameter, isRaw = true, attr) - ) = when (attr.flexibility) { - // Raw(List) => (List..List<*>) - // Raw(Enum) => (Enum>..Enum>) - // In the last case upper bound is equal to star projection `Enum<*>`, - // but we want to keep matching tree structure of flexible bounds (at least they should have the same size) - JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND -> TypeProjectionImpl( - // T : String -> String - // in T : String -> String - // T : Enum -> Enum<*> - Variance.INVARIANT, erasedUpperBound - ) - JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> { - if (!parameter.variance.allowsOutPosition) - // in T -> Comparable - TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType) - else if (erasedUpperBound.constructor.parameters.isNotEmpty()) - // T : Enum -> out Enum<*> - TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound) - else - // T : String -> * - makeStarProjection(parameter, attr) - } - } - - override fun isEmpty() = false - - companion object { - private val lowerTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND) - private val upperTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND) - } -} diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/TypeParameterUpperBoundEraser.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/TypeParameterUpperBoundEraser.kt deleted file mode 100644 index 26de1695624..00000000000 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/TypeParameterUpperBoundEraser.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2010-2021 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.load.java.lazy.types - -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.storage.LockBasedStorageManager -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.error.ErrorUtils -import org.jetbrains.kotlin.types.error.ErrorTypeKind -import org.jetbrains.kotlin.types.typeUtil.extractTypeParametersFromUpperBounds -import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjectionOrMapped -import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections - -internal class TypeParameterUpperBoundEraser(rawSubstitution: RawSubstitution? = null) { - private val storage = LockBasedStorageManager("Type parameter upper bound erasion results") - private val erroneousErasedBound by lazy { - ErrorUtils.createErrorType(ErrorTypeKind.CANNOT_COMPUTE_ERASED_BOUND, this.toString()) - } - private val rawSubstitution = rawSubstitution ?: RawSubstitution(this) - - private data class DataToEraseUpperBound( - val typeParameter: TypeParameterDescriptor, - val isRaw: Boolean, - val typeAttr: JavaTypeAttributes - ) { - override fun equals(other: Any?): Boolean { - if (other !is DataToEraseUpperBound) return false - return other.typeParameter == this.typeParameter - && other.isRaw == this.isRaw - && other.typeAttr.flexibility == this.typeAttr.flexibility - && other.typeAttr.howThisTypeIsUsed == this.typeAttr.howThisTypeIsUsed - && other.typeAttr.isForAnnotationParameter == this.typeAttr.isForAnnotationParameter - && other.typeAttr.defaultType == this.typeAttr.defaultType - } - - override fun hashCode(): Int { - var result = typeParameter.hashCode() - result += 31 * result + if (isRaw) 1 else 0 - result += 31 * result + typeAttr.flexibility.hashCode() - result += 31 * result + typeAttr.howThisTypeIsUsed.hashCode() - result += 31 * result + if (typeAttr.isForAnnotationParameter) 1 else 0 - result += 31 * result + typeAttr.defaultType.hashCode() - return result - } - } - - private val getErasedUpperBound = storage.createMemoizedFunction { - with(it) { getErasedUpperBoundInternal(typeParameter, isRaw, typeAttr) } - } - - internal fun getErasedUpperBound( - typeParameter: TypeParameterDescriptor, - isRaw: Boolean, - typeAttr: JavaTypeAttributes - ) = getErasedUpperBound(DataToEraseUpperBound(typeParameter, isRaw, typeAttr)) - - private fun getDefaultType(typeAttr: JavaTypeAttributes) = - typeAttr.defaultType?.replaceArgumentsWithStarProjections() ?: erroneousErasedBound - - // Definition: - // ErasedUpperBound(T : G) = G<*> // UpperBound(T) is a type G with arguments - // ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments - // ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F - private fun getErasedUpperBoundInternal( - // Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound` - // E.g. `class A` - // To prevent recursive calls return defaultValue() instead - typeParameter: TypeParameterDescriptor, - isRaw: Boolean, - typeAttr: JavaTypeAttributes - ): KotlinType { - val visitedTypeParameters = typeAttr.visitedTypeParameters - - if (visitedTypeParameters != null && typeParameter.original in visitedTypeParameters) - return getDefaultType(typeAttr) - - /* - * We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types. - * E.g. for `class Foo, B>`, we'd have erasure for lower bound: Foo, Any>, - * but it's wrong type: projection(*) != projection(Any). - * So we should substitute erasure of the corresponding type parameter: `Foo, Any>` or `Foo, *>`. - */ - val erasedUpperBounds = typeParameter.defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate { - val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) { - rawSubstitution.computeProjection( - it, - // if erasure happens due to invalid arguments number, use star projections instead - if (isRaw) typeAttr else typeAttr.withFlexibility(JavaTypeFlexibility.INFLEXIBLE), - getErasedUpperBound(it, isRaw, typeAttr.withNewVisitedTypeParameter(typeParameter)) - ) - } else makeStarProjection(it, typeAttr) - - it.typeConstructor to boundProjection - } - val erasedUpperBoundsSubstitutor = TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(erasedUpperBounds)) - - val firstUpperBound = typeParameter.upperBounds.first() - - if (firstUpperBound.constructor.declarationDescriptor is ClassDescriptor) { - return firstUpperBound.replaceArgumentsWithStarProjectionOrMapped( - erasedUpperBoundsSubstitutor, - erasedUpperBounds, - Variance.OUT_VARIANCE, - typeAttr.visitedTypeParameters - ) - } - - val stopAt = typeAttr.visitedTypeParameters ?: setOf(this) - var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor - - while (current !in stopAt) { - val nextUpperBound = current.upperBounds.first() - if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) { - return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped( - erasedUpperBoundsSubstitutor, - erasedUpperBounds, - Variance.OUT_VARIANCE, - typeAttr.visitedTypeParameters - ) - } - - current = nextUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor - } - - return getDefaultType(typeAttr) - } -} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErasureProjectionComputer.kt b/core/descriptors/src/org/jetbrains/kotlin/types/ErasureProjectionComputer.kt new file mode 100644 index 00000000000..96136b5c341 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErasureProjectionComputer.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2010-2022 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.types + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor + +open class ErasureProjectionComputer { + open fun computeProjection( + parameter: TypeParameterDescriptor, + typeAttr: ErasureTypeAttributes, + typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser, + erasedUpperBound: KotlinType = typeParameterUpperBoundEraser.getErasedUpperBound(parameter, typeAttr) + ): TypeProjection = TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound) +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErasureTypeAttributes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/ErasureTypeAttributes.kt new file mode 100644 index 00000000000..30dda151d2c --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErasureTypeAttributes.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2022 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.types + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor + +open class ErasureTypeAttributes( + // we use it to prevent happening a recursion while compute type parameter's upper bounds + open val howThisTypeIsUsed: TypeUsage, + open val visitedTypeParameters: Set? = null, + open val defaultType: SimpleType? = null +) { + open fun withDefaultType(type: SimpleType?) = ErasureTypeAttributes(howThisTypeIsUsed, visitedTypeParameters, defaultType = type) + + open fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) = + ErasureTypeAttributes( + howThisTypeIsUsed, + visitedTypeParameters = visitedTypeParameters?.let { it + typeParameter } ?: setOf(typeParameter), + defaultType + ) + + override fun equals(other: Any?): Boolean { + if (other !is ErasureTypeAttributes) return false + return other.defaultType == this.defaultType && other.howThisTypeIsUsed == this.howThisTypeIsUsed + } + + override fun hashCode(): Int { + var result = defaultType.hashCode() + result += 31 * result + howThisTypeIsUsed.hashCode() + return result + } +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterErasureOptions.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterErasureOptions.kt new file mode 100644 index 00000000000..117ca7d2b43 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterErasureOptions.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2022 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.types + +class TypeParameterErasureOptions( + val leaveNonTypeParameterTypes: Boolean, + val intersectUpperBounds: Boolean, +) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterUpperBoundEraser.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterUpperBoundEraser.kt new file mode 100644 index 00000000000..9f03c86d9af --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeParameterUpperBoundEraser.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2010-2021 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.types + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection +import org.jetbrains.kotlin.types.checker.intersectTypes +import org.jetbrains.kotlin.types.error.ErrorUtils +import org.jetbrains.kotlin.types.error.ErrorTypeKind +import org.jetbrains.kotlin.types.typeUtil.* + +class TypeParameterUpperBoundEraser( + val projectionComputer: ErasureProjectionComputer, + val options: TypeParameterErasureOptions = TypeParameterErasureOptions(leaveNonTypeParameterTypes = false, intersectUpperBounds = false) +) { + private val storage = LockBasedStorageManager("Type parameter upper bound erasure results") + + private val erroneousErasedBound by lazy { + ErrorUtils.createErrorType(ErrorTypeKind.CANNOT_COMPUTE_ERASED_BOUND, this.toString()) + } + + private data class DataToEraseUpperBound( + val typeParameter: TypeParameterDescriptor, + val typeAttr: ErasureTypeAttributes + ) { + override fun equals(other: Any?): Boolean { + if (other !is DataToEraseUpperBound) return false + return other.typeParameter == this.typeParameter && other.typeAttr == this.typeAttr + } + + override fun hashCode(): Int { + var result = typeParameter.hashCode() + result += 31 * result + typeAttr.hashCode() + return result + } + } + + private val getErasedUpperBound = storage.createMemoizedFunction { + with(it) { getErasedUpperBoundInternal(typeParameter, typeAttr) } + } + + fun getErasedUpperBound( + typeParameter: TypeParameterDescriptor, + typeAttr: ErasureTypeAttributes + ): KotlinType = getErasedUpperBound(DataToEraseUpperBound(typeParameter, typeAttr)) + + private fun getDefaultType(typeAttr: ErasureTypeAttributes) = + typeAttr.defaultType?.replaceArgumentsWithStarProjections() ?: erroneousErasedBound + + // Definition: + // ErasedUpperBound(T : G) = G<*> // UpperBound(T) is a type G with arguments + // ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments + // ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F + private fun getErasedUpperBoundInternal( + // Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound` + // E.g. `class A` + // To prevent recursive calls return defaultValue() instead + typeParameter: TypeParameterDescriptor, + typeAttr: ErasureTypeAttributes + ): KotlinType { + val visitedTypeParameters = typeAttr.visitedTypeParameters + + if (visitedTypeParameters != null && typeParameter.original in visitedTypeParameters) + return getDefaultType(typeAttr) + + /* + * We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types. + * E.g. for `class Foo, B>`, we'd have erasure for lower bound: Foo, Any>, + * but it's wrong type: projection(*) != projection(Any). + * So we should substitute erasure of the corresponding type parameter: `Foo, Any>` or `Foo, *>`. + */ + val erasedTypeParameters = typeParameter.defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate { + val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) { + projectionComputer.computeProjection( + it, + typeAttr, + typeParameterUpperBoundEraser = this, + getErasedUpperBound(it, typeAttr.withNewVisitedTypeParameter(typeParameter)) + ) + } else makeStarProjection(it, typeAttr) + + it.typeConstructor to boundProjection + } + val erasedTypeParametersSubstitutor = + TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(erasedTypeParameters)) + val erasedUpperBounds = + erasedTypeParametersSubstitutor.substituteErasedUpperBounds(typeParameter.upperBounds, typeAttr) + + if (erasedUpperBounds.isNotEmpty()) { + if (!options.intersectUpperBounds) { + require(erasedUpperBounds.size == 1) { "Should only be one computed upper bound if no need to intersect all bounds" } + return erasedUpperBounds.single() + } else { + @Suppress("UNCHECKED_CAST") + return intersectTypes(erasedUpperBounds.toList().map { it.unwrap() }) + } + } + + return getDefaultType(typeAttr) + } + + private fun TypeSubstitutor.substituteErasedUpperBounds( + upperBounds: List, + typeAttr: ErasureTypeAttributes + ): Set = buildSet { + for (upperBound in upperBounds) { + when (val declaration = upperBound.constructor.declarationDescriptor) { + is ClassDescriptor -> add( + upperBound.replaceArgumentsOfUpperBound( + substitutor = this@substituteErasedUpperBounds, + typeAttr.visitedTypeParameters, + options.leaveNonTypeParameterTypes + ) + ) + is TypeParameterDescriptor -> { + if (typeAttr.visitedTypeParameters?.contains(declaration) == true) { + add(getDefaultType(typeAttr)) + } else { + addAll(substituteErasedUpperBounds(declaration.upperBounds, typeAttr)) + } + } + } + + // If no need to intersect, take only the first bound + if (!options.intersectUpperBounds) break + } + } + + companion object { + fun KotlinType.replaceArgumentsOfUpperBound( + substitutor: TypeSubstitutor, + visitedTypeParameters: Set?, + leaveNonTypeParameterTypes: Boolean = false + ): KotlinType { + val replacedArguments = replaceArgumentsByParametersWith { typeParameterDescriptor -> + val argument = arguments.getOrNull(typeParameterDescriptor.index) + if (leaveNonTypeParameterTypes && argument?.type?.containsTypeParameter() == false) + return@replaceArgumentsByParametersWith argument + + val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters + + if (argument == null || isTypeParameterVisited || substitutor.substitution[argument.type] == null) { + StarProjectionImpl(typeParameterDescriptor) + } else { + argument + } + } + + return substitutor.safeSubstitute(replacedArguments, Variance.OUT_VARIANCE) + } + } +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUsage.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUsage.kt new file mode 100644 index 00000000000..624458cef1d --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUsage.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2022 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.types + +enum class TypeUsage { + SUPERTYPE, COMMON +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java index 0b21f631cf9..a6e8cfce1d5 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java @@ -30,6 +30,8 @@ import org.jetbrains.kotlin.utils.SmartSet; import java.util.*; +import static org.jetbrains.kotlin.types.TypeUsage.SUPERTYPE; + public class TypeUtils { public static final SimpleType DONT_CARE = ErrorUtils.createErrorType(ErrorTypeKind.DONT_CARE); public static final SimpleType CANNOT_INFER_FUNCTION_PARAM_TYPE = @@ -480,6 +482,15 @@ public class TypeUtils { return new StarProjectionImpl(parameterDescriptor); } + @NotNull + public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor, ErasureTypeAttributes attr) { + if (attr.getHowThisTypeIsUsed() == SUPERTYPE) { + return new TypeProjectionImpl(StarProjectionImplKt.starProjectionType(parameterDescriptor)); + } else { + return new StarProjectionImpl(parameterDescriptor); + } + } + @NotNull public static KotlinType getDefaultPrimitiveNumberType(@NotNull IntegerValueTypeConstructor numberValueTypeConstructor) { KotlinType type = getDefaultPrimitiveNumberType(numberValueTypeConstructor.getSupertypes()); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index ea4f3a02d3d..0d66bdb0193 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.* import org.jetbrains.kotlin.types.error.ErrorType -import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.TypeArgumentMarker import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -250,21 +249,6 @@ private fun KotlinType.containsSelfTypeParameter( } } -fun KotlinType.replaceArgumentsWithStarProjectionOrMapped( - substitutor: TypeSubstitutor, - substitutionMap: Map, - variance: Variance, - visitedTypeParameters: Set? -) = - replaceArgumentsByParametersWith { typeParameterDescriptor -> - val argument = arguments.getOrNull(typeParameterDescriptor.index) - val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters - if (!isTypeParameterVisited && argument != null && argument.type.constructor in substitutionMap) { - argument - } else StarProjectionImpl(typeParameterDescriptor) - }.let { substitutor.safeSubstitute(it, variance) } - - inline fun KotlinType.replaceArgumentsByParametersWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType { val unwrapped = unwrap() return when (unwrapped) {