From e4ae7ca4ce6ab9f4d026976d852a22cef52c8239 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 21 Mar 2017 16:28:31 +0300 Subject: [PATCH] Use type substitution when matching header-impl members Previously, type substitution, which is critical for matching generic header/impl members with each other, was only performed when checkImplementationHasHeaderDeclaration was called for impl class (areCompatibleClassifiers creates the correct substitutor). This was done in areCompatibleClassifiers: a substitutor which maps type parameters of the header class to type parameters of the impl class was created. Now we create the same substitutor when checkImplementationHasHeaderDeclaration is called for an impl member of an impl class as well, manually. #KT-15230 Fixed --- .../checkers/HeaderImplDeclarationChecker.kt | 14 ++++++++--- .../generic/membersInGenericClass.kt | 18 ++++++++++++++ .../generic/membersInGenericClass.txt | 24 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 15 ++++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt index 22ed91754e3..68b47f4e980 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt @@ -119,9 +119,18 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) else -> return // do not report anything for incorrect code, e.g. 'impl' local function } candidates.any { declaration -> + // TODO: optimize by caching this per impl-header class pair, do not create a new substitutor for each impl member + val substitutor = + if (container is ClassDescriptor) { + val headerClass = declaration.containingDeclaration as ClassDescriptor + // TODO: this might not work for members of inner generic classes + Substitutor(headerClass.declaredTypeParameters, container.declaredTypeParameters) + } + else null + descriptor != declaration && declaration.isHeader && - areCompatibleCallables(declaration, descriptor, checkImpl = false) == Compatible + areCompatibleCallables(declaration, descriptor, checkImpl = false, parentSubstitutor = substitutor) == Compatible } } is ClassifierDescriptorWithTypeParameters -> descriptor.findDeclarationForClass() != null @@ -342,12 +351,11 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) val bTypeParams = b.declaredTypeParameters if (aTypeParams.size != bTypeParams.size) return Incompatible.TypeParameterCount - val substitutor = Substitutor(aTypeParams, bTypeParams, parentSubstitutor) - if (a.modality != b.modality && !(a.modality == Modality.FINAL && b.modality == Modality.OPEN)) return Incompatible.Modality if (a.visibility != b.visibility) return Incompatible.Visibility + val substitutor = Substitutor(aTypeParams, bTypeParams, parentSubstitutor) areCompatibleTypeParameters(aTypeParams, bTypeParams, substitutor).let { if (it != Compatible) return it } // Subtract kotlin.Any from supertypes because it's implicitly added if no explicit supertype is specified, diff --git a/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt b/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt new file mode 100644 index 00000000000..13542922806 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt @@ -0,0 +1,18 @@ +// !LANGUAGE: +MultiPlatformProjects +// MODULE: m1-common +// FILE: common.kt + +header interface A { + val x: T + var y: List + fun f(p: Collection): Map> +} + +// MODULE: m2-jvm(m1-common) +// FILE: jvm.kt + +impl interface A { + impl val x: T + impl var y: List + impl fun f(p: Collection): Map> +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.txt b/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.txt new file mode 100644 index 00000000000..dae790fbc1b --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.txt @@ -0,0 +1,24 @@ +// -- Module: -- +package + +public header interface A { + public header abstract val x: T + public header abstract var y: kotlin.collections.List + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract header fun f(/*0*/ p: kotlin.collections.Collection): kotlin.collections.Map> + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + + +// -- Module: -- +package + +public impl interface A { + public impl abstract val x: T + public impl abstract var y: kotlin.collections.List + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract impl fun f(/*0*/ p: kotlin.collections.Collection): kotlin.collections.Map> + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 21bcb6cdd58..61cd16f6342 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -13153,6 +13153,21 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/multiplatform/generic") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Generic extends AbstractDiagnosticsTest { + public void testAllFilesPresentInGeneric() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/generic"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("membersInGenericClass.kt") + public void testMembersInGenericClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)