From 46ac26147bcca4615ef362153c3b0c8ed758a850 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 16 May 2016 17:24:20 +0300 Subject: [PATCH] Annotate with `@UnsafeVariance` relevant types in built-ins --- .../java8/kotlin-collections.txt | 14 +-- .../newMethods/kotlin-collections.txt | 2 +- .../kotlin/load/kotlin/JvmBuiltInsSettings.kt | 18 ++-- .../kotlin/UnsafeVarianceTypeSubstitution.kt | 97 +++++++++++++++++++ .../descriptors/annotations/annotationUtil.kt | 8 ++ .../DisjointKeysUnionTypeSubstitution.kt | 2 + .../kotlin/types/TypeSubstitution.kt | 9 +- .../kotlin/types/TypeSubstitutor.java | 3 +- .../jetbrains/kotlin/types/VarianceChecker.kt | 7 ++ 9 files changed, 144 insertions(+), 16 deletions(-) create mode 100644 core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/UnsafeVarianceTypeSubstitution.kt diff --git a/compiler/testData/builtin-classes/java8/kotlin-collections.txt b/compiler/testData/builtin-classes/java8/kotlin-collections.txt index 88225a553d4..00be14dcffc 100644 --- a/compiler/testData/builtin-classes/java8/kotlin-collections.txt +++ b/compiler/testData/builtin-classes/java8/kotlin-collections.txt @@ -32,9 +32,9 @@ public interface Collection : kotlin.collections.Iterable { public open override /*1*/ /*fake_override*/ fun forEach(/*0*/ p0: java.util.function.Consumer!): kotlin.Unit public abstract fun isEmpty(): kotlin.Boolean public abstract override /*1*/ fun iterator(): kotlin.collections.Iterator - public open fun parallelStream(): java.util.stream.Stream! - public open override /*1*/ fun spliterator(): java.util.Spliterator! - public open fun stream(): java.util.stream.Stream! + public open fun parallelStream(): java.util.stream.Stream<@kotlin.UnsafeVariance() E!>! + public open override /*1*/ fun spliterator(): java.util.Spliterator<@kotlin.UnsafeVariance() E!>! + public open fun stream(): java.util.stream.Stream<@kotlin.UnsafeVariance() E!>! } public abstract class DoubleIterator : kotlin.collections.Iterator { @@ -64,7 +64,7 @@ public abstract class IntIterator : kotlin.collections.Iterator { public interface Iterable { public open fun forEach(/*0*/ p0: java.util.function.Consumer!): kotlin.Unit public abstract operator fun iterator(): kotlin.collections.Iterator - public open fun spliterator(): java.util.Spliterator! + public open fun spliterator(): java.util.Spliterator<@kotlin.UnsafeVariance() T!>! } public interface Iterator { @@ -87,7 +87,7 @@ public interface List : kotlin.collections.Collection { public abstract fun listIterator(): kotlin.collections.ListIterator public abstract fun listIterator(/*0*/ index: kotlin.Int): kotlin.collections.ListIterator public open override /*1*/ /*fake_override*/ fun parallelStream(): java.util.stream.Stream! - public open override /*1*/ fun spliterator(): java.util.Spliterator! + public open override /*1*/ fun spliterator(): java.util.Spliterator<@kotlin.UnsafeVariance() E!>! public open override /*1*/ /*fake_override*/ fun stream(): java.util.stream.Stream! public abstract fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.collections.List } @@ -123,7 +123,7 @@ public interface Map { public abstract fun containsValue(/*0*/ value: @kotlin.UnsafeVariance() V): kotlin.Boolean public open fun forEach(/*0*/ p0: java.util.function.BiConsumer!): kotlin.Unit public abstract operator fun get(/*0*/ key: K): V? - public open fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: V!): V! + public open fun getOrDefault(/*0*/ p0: kotlin.Any!, /*1*/ p1: @kotlin.UnsafeVariance() V!): V! public abstract fun isEmpty(): kotlin.Boolean public interface Entry { @@ -279,7 +279,7 @@ public interface Set : kotlin.collections.Collection { public abstract override /*1*/ fun isEmpty(): kotlin.Boolean public abstract override /*1*/ fun iterator(): kotlin.collections.Iterator public open override /*1*/ /*fake_override*/ fun parallelStream(): java.util.stream.Stream! - public open override /*1*/ fun spliterator(): java.util.Spliterator! + public open override /*1*/ fun spliterator(): java.util.Spliterator<@kotlin.UnsafeVariance() E!>! public open override /*1*/ /*fake_override*/ fun stream(): java.util.stream.Stream! } diff --git a/compiler/testData/builtin-classes/newMethods/kotlin-collections.txt b/compiler/testData/builtin-classes/newMethods/kotlin-collections.txt index 558b60f1820..9d9ad6ba640 100644 --- a/compiler/testData/builtin-classes/newMethods/kotlin-collections.txt +++ b/compiler/testData/builtin-classes/newMethods/kotlin-collections.txt @@ -28,7 +28,7 @@ public interface Collection : kotlin.collections.Iterable { public abstract fun containsAll(/*0*/ elements: kotlin.collections.Collection<@kotlin.UnsafeVariance() E>): kotlin.Boolean public abstract fun isEmpty(): kotlin.Boolean public abstract override /*1*/ fun iterator(): kotlin.collections.Iterator - @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public abstract fun nonExistingMethod(/*0*/ p0: E!): kotlin.String! + @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public abstract fun nonExistingMethod(/*0*/ p0: @kotlin.UnsafeVariance() E!): kotlin.String! } public abstract class DoubleIterator : kotlin.collections.Iterator { diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/JvmBuiltInsSettings.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/JvmBuiltInsSettings.kt index 95ff622ecbc..ab3441c8ee2 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/JvmBuiltInsSettings.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/JvmBuiltInsSettings.kt @@ -20,7 +20,9 @@ import org.jetbrains.kotlin.builtins.BuiltInsInitializer import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl +import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations import org.jetbrains.kotlin.descriptors.annotations.createDeprecatedAnnotation +import org.jetbrains.kotlin.descriptors.annotations.createUnsafeVarianceAnnotation import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.incremental.components.NoLookupLocation @@ -43,8 +45,8 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.getValue -import org.jetbrains.kotlin.types.DelegatingType -import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addToStdlib.check @@ -104,13 +106,17 @@ open class JvmBuiltInsSettings( } .mapNotNull { additionalMember -> - additionalMember.newCopyBuilder().apply { + val substitutedWithKotlinTypeParameters = + additionalMember.substitute( + createMappedTypeParametersSubstitution + (additionalMember.containingDeclaration as ClassDescriptor, classDescriptor).buildSubstitutor() + ) as SimpleFunctionDescriptor + + substitutedWithKotlinTypeParameters.newCopyBuilder().apply { setOwner(classDescriptor) setDispatchReceiverParameter(classDescriptor.thisAsReceiverParameter) setPreserveSourceElement() - setSubstitution(createMappedTypeParametersSubstitution( - additionalMember.containingDeclaration as ClassDescriptor, classDescriptor)) - + setSubstitution(UnsafeVarianceTypeSubstitution(moduleDescriptor.builtIns)) val memberStatus = additionalMember.getJdkMethodStatus() when (memberStatus) { diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/UnsafeVarianceTypeSubstitution.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/UnsafeVarianceTypeSubstitution.kt new file mode 100644 index 00000000000..dceb6684406 --- /dev/null +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/UnsafeVarianceTypeSubstitution.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2016 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.kotlin + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl +import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations +import org.jetbrains.kotlin.descriptors.annotations.createUnsafeVarianceAnnotation +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations + +internal class UnsafeVarianceTypeSubstitution(kotlinBuiltIns: KotlinBuiltIns) : TypeSubstitution() { + private val unsafeVarianceAnnotations = AnnotationsImpl(listOf(kotlinBuiltIns.createUnsafeVarianceAnnotation())) + + override fun get(key: KotlinType) = null + + override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance): KotlinType { + val unsafeVariancePaths = mutableListOf>() + IndexedTypeHolder(topLevelType).checkTypePosition( + position, + { typeParameter, indexedTypeHolder, errorPosition -> + unsafeVariancePaths.add(indexedTypeHolder.argumentIndices) + }, + customVariance = { null }) + + return topLevelType.annotatePartsWithUnsafeVariance(unsafeVariancePaths) + } + + private fun KotlinType.annotatePartsWithUnsafeVariance(unsafeVariancePaths: Collection>): KotlinType { + if (unsafeVariancePaths.isEmpty()) return this + + if (isFlexible()) { + return flexibility().factory.create( + lowerIfFlexible().annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 0)), + upperIfFlexible().annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1)) + ) + } + + // if root is unsafe + if (emptyList() in unsafeVariancePaths) { + return replaceAnnotations(composeAnnotations(annotations, unsafeVarianceAnnotations)) + } + + return replace(newArguments = arguments.withIndex().map { + val (index, argument) = it + if (argument.isStarProjection) return@map argument + TypeProjectionImpl( + argument.projectionKind, + argument.type.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, index))) + }) + } + + private fun subPathsWithIndex(paths: Collection>, index: Int) = paths.filter { it[0] == index }.map { it.subList(1, it.size) } + + private class IndexedTypeHolder( + override val type: KotlinType, + val argumentIndices: List = emptyList() + ) : TypeHolder { + override val flexibleBounds: Pair? get() = + if (type.isFlexible()) + Pair( + IndexedTypeHolder(type.lowerIfFlexible(), argumentIndices + 0), + IndexedTypeHolder(type.upperIfFlexible(), argumentIndices + 1)) + else null + + override val arguments: List> + get() = type.arguments.withIndex().map { projectionWithIndex -> + + val (index, projection) = projectionWithIndex + object : TypeHolderArgument { + override val projection: TypeProjection + get() = projection + override val typeParameter: TypeParameterDescriptor? + get() = type.constructor.parameters[index] + override val holder: IndexedTypeHolder + get() = IndexedTypeHolder(projection.type, argumentIndices + index) + + } + } + + } +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt index 79dce013756..13d5ec19ac8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt @@ -57,4 +57,12 @@ fun KotlinBuiltIns.createDeprecatedAnnotation( SourceElement.NO_SOURCE) } +fun KotlinBuiltIns.createUnsafeVarianceAnnotation(): AnnotationDescriptor { + val unsafeVarianceAnnotation = getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.unsafeVariance) + return AnnotationDescriptorImpl( + unsafeVarianceAnnotation.defaultType, + emptyMap(), + SourceElement.NO_SOURCE) +} + private operator fun Collection.get(parameterName: String) = single { it.name.asString() == parameterName } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/DisjointKeysUnionTypeSubstitution.kt b/core/descriptors/src/org/jetbrains/kotlin/types/DisjointKeysUnionTypeSubstitution.kt index 1db6056108b..6a034013a69 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/DisjointKeysUnionTypeSubstitution.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/DisjointKeysUnionTypeSubstitution.kt @@ -32,6 +32,8 @@ class DisjointKeysUnionTypeSubstitution private constructor( } override fun get(key: KotlinType) = first[key] ?: second[key] + override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance) = + second.prepareTopLevelType(first.prepareTopLevelType(topLevelType, position), position) override fun isEmpty() = false diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt index abc5e9f5d8e..133fdd28acd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitution.kt @@ -31,6 +31,9 @@ abstract class TypeSubstitution { abstract operator fun get(key: KotlinType): TypeProjection? + // This can be used to perform preliminary manipulations with top-level types + open fun prepareTopLevelType(topLevelType: KotlinType, position: Variance): KotlinType = topLevelType + open fun isEmpty(): Boolean = false open fun approximateCapturedTypes(): Boolean = false @@ -170,6 +173,9 @@ private class CompositeTypeSubstitution( return second.buildSubstitutor().substitute(firstResult) } + override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance) = + second.prepareTopLevelType(first.prepareTopLevelType(topLevelType, position), position) + override fun isEmpty() = first.isEmpty() && second.isEmpty() override fun approximateCapturedTypes() = first.approximateCapturedTypes() || second.approximateCapturedTypes() @@ -179,7 +185,8 @@ private class CompositeTypeSubstitution( } open class DelegatedTypeSubstitution(val substitution: TypeSubstitution): TypeSubstitution() { - override fun get(key: KotlinType) = substitution.get(key) + override fun get(key: KotlinType) = substitution[key] + override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance) = substitution.prepareTopLevelType(topLevelType, position) override fun isEmpty() = substitution.isEmpty() diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java index 68f3cdf4489..8c29574ae60 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSubstitutor.java @@ -97,7 +97,8 @@ public class TypeSubstitutor { @Nullable public KotlinType substitute(@NotNull KotlinType type, @NotNull Variance howThisTypeIsUsed) { - TypeProjection projection = substitute(new TypeProjectionImpl(howThisTypeIsUsed, type)); + TypeProjection projection = + substitute(new TypeProjectionImpl(howThisTypeIsUsed, getSubstitution().prepareTopLevelType(type, howThisTypeIsUsed))); return projection == null ? null : projection.getType(); } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/VarianceChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/VarianceChecker.kt index 1502842d8d9..78da59f82d6 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/VarianceChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/VarianceChecker.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.utils.DO_NOTHING_3 interface TypeHolder> { val type: KotlinType val arguments: List?> + val flexibleBounds: Pair? get() = null } interface TypeHolderArgument> { @@ -37,6 +38,12 @@ fun > D.checkTypePosition( reportError: (TypeParameterDescriptor, D, Variance) -> Unit = DO_NOTHING_3, customVariance: (TypeParameterDescriptor) -> Variance? = { null } ): Boolean { + + flexibleBounds?.let { + return it.first.checkTypePosition(position, reportError, customVariance) and + it.second.checkTypePosition(position, reportError, customVariance) + } + val classifierDescriptor = type.constructor.declarationDescriptor if (classifierDescriptor is TypeParameterDescriptor) { val declarationVariance = customVariance(classifierDescriptor) ?: classifierDescriptor.variance