From 617bed1bf1b57f834f0cea560b82de535addf5d8 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Fri, 22 Feb 2019 17:39:40 +0300 Subject: [PATCH] Fix nested typealiases expansion and as a result fix serialization Consider the following situation: ``` class Inv typealias A = Inv typealias B = Inv> fun materialize(): B = TODO() ``` Type `B` is expanding to `Inv>` and for this type `B` and `Inv>` are abbreviated types, but due to a bug we forgot to make substitution for `Inv>` and were getting abbreviated type `Inv>` where `K` is a type parameter from the typealias declaration. This bug didn't affect subtyping anyhow but the incorrect type was serialized and caused problems during deserialization as there wasn't `K` in deserialization context. #KT-24964 Fixed #KT-20780 Fixed #KT-20065 Fixed #KT-28236 Fixed #KT-21775 Fixed --- .../kotlin/resolve/TypeAliasExpander.kt | 34 +++++++++------ .../compileKotlinAgainstKotlin/kt21775.kt | 43 +++++++++++++++++++ .../nestedFunctionTypeAliasExpansion.kt | 17 ++++++++ .../nestedTypeAliasExpansion.kt | 19 ++++++++ ...mpileKotlinAgainstKotlinTestGenerated.java | 15 +++++++ 5 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstKotlin/kt21775.kt create mode 100644 compiler/testData/compileKotlinAgainstKotlin/nestedFunctionTypeAliasExpansion.kt create mode 100644 compiler/testData/compileKotlinAgainstKotlin/nestedTypeAliasExpansion.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt index 53e24a674d2..dad93fb3fe7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt @@ -214,25 +214,16 @@ class TypeAliasExpander( withAbbreviatedType = false ) + val substitutedType = type.substituteArguments(typeAliasExpansion, recursionDepth) + // 'dynamic' type can't be abbreviated - will be reported separately val typeWithAbbreviation = - if (nestedExpandedType.isDynamic()) nestedExpandedType else nestedExpandedType.withAbbreviation(type) + if (nestedExpandedType.isDynamic()) nestedExpandedType else nestedExpandedType.withAbbreviation(substitutedType) TypeProjectionImpl(originalProjection.projectionKind, typeWithAbbreviation) } else -> { - val substitutedArguments = type.arguments.mapIndexed { i, originalArgument -> - val projection = expandTypeProjection( - originalArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth + 1 - ) - if (projection.isStarProjection) projection - else TypeProjectionImpl( - projection.projectionKind, - TypeUtils.makeNullableIfNeeded(projection.type, originalArgument.type.isMarkedNullable) - ) - } - - val substitutedType = type.replace(newArguments = substitutedArguments) + val substitutedType = type.substituteArguments(typeAliasExpansion, recursionDepth) checkTypeArgumentsSubstitution(type, substitutedType) @@ -241,6 +232,23 @@ class TypeAliasExpander( } } + private fun SimpleType.substituteArguments(typeAliasExpansion: TypeAliasExpansion, recursionDepth: Int): SimpleType { + val typeConstructor = this.constructor + + val substitutedArguments = this.arguments.mapIndexed { i, originalArgument -> + val projection = expandTypeProjection( + originalArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth + 1 + ) + if (projection.isStarProjection) projection + else TypeProjectionImpl( + projection.projectionKind, + TypeUtils.makeNullableIfNeeded(projection.type, originalArgument.type.isMarkedNullable) + ) + } + + return this.replace(newArguments = substitutedArguments) + } + private fun checkTypeArgumentsSubstitution(unsubstitutedType: KotlinType, substitutedType: KotlinType) { val typeSubstitutor = TypeSubstitutor.create(substitutedType) diff --git a/compiler/testData/compileKotlinAgainstKotlin/kt21775.kt b/compiler/testData/compileKotlinAgainstKotlin/kt21775.kt new file mode 100644 index 00000000000..a683731e927 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/kt21775.kt @@ -0,0 +1,43 @@ +// FILE: lib.kt + +package lib + +class TestObserver { + fun assertValue(valuePredicate: (T) -> Boolean): Unit = TODO() +} + +class Single { + fun test(): TestObserver = TODO() +} + +class Employee + +class Either + +typealias DomainEither = Either +typealias DomainSingle = Single> + +fun provideDomainSingle(): DomainSingle = TODO() + +class CreateEmployeeUseCaseAccessor { + fun testNormalName() { + val testObs = provideDomainSingle().test() + testObs.assertValue { true } + } +} + + +// FILE: main.kt + +import lib.* + +class CreateEmployeeUseCaseTest { + fun testNormalName() { + val testObs = provideDomainSingle().test() + testObs.assertValue { true } + } +} + +fun box(): String { + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstKotlin/nestedFunctionTypeAliasExpansion.kt b/compiler/testData/compileKotlinAgainstKotlin/nestedFunctionTypeAliasExpansion.kt new file mode 100644 index 00000000000..dd56222f2ed --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/nestedFunctionTypeAliasExpansion.kt @@ -0,0 +1,17 @@ +// FILE: lib.kt + +package lib + +typealias Dispatch = (Msg) -> Unit +typealias Effect = (Dispatch) -> Unit + +fun noEffect(): Effect = TODO() + +// FILE: main.kt + +import lib.* + +fun box(): String { + val s = { noEffect() } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstKotlin/nestedTypeAliasExpansion.kt b/compiler/testData/compileKotlinAgainstKotlin/nestedTypeAliasExpansion.kt new file mode 100644 index 00000000000..4ec42b0eb62 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/nestedTypeAliasExpansion.kt @@ -0,0 +1,19 @@ +// FILE: lib.kt + +package lib + +class Inv + +typealias A = Inv +typealias B = Inv> + +fun materialize(): B? = null + +// FILE: main.kt + +import lib.* + +fun box(): String { + val s = { materialize() } + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java index 57f579db828..683a2c03ddd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java @@ -213,6 +213,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/kt14012_multi.kt"); } + @TestMetadata("kt21775.kt") + public void testKt21775() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/kt21775.kt"); + } + @TestMetadata("multifileClassInlineFunctionAccessingProperty.kt") public void testMultifileClassInlineFunctionAccessingProperty() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/multifileClassInlineFunctionAccessingProperty.kt"); @@ -233,11 +238,21 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/nestedEnum.kt"); } + @TestMetadata("nestedFunctionTypeAliasExpansion.kt") + public void testNestedFunctionTypeAliasExpansion() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/nestedFunctionTypeAliasExpansion.kt"); + } + @TestMetadata("nestedObject.kt") public void testNestedObject() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/nestedObject.kt"); } + @TestMetadata("nestedTypeAliasExpansion.kt") + public void testNestedTypeAliasExpansion() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/nestedTypeAliasExpansion.kt"); + } + @TestMetadata("optionalAnnotation.kt") public void testOptionalAnnotation() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt");