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
This commit is contained in:
Alexander Udalov
2017-03-21 16:28:31 +03:00
parent 4c868be869
commit e4ae7ca4ce
4 changed files with 68 additions and 3 deletions
@@ -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,
@@ -0,0 +1,18 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: m1-common
// FILE: common.kt
header interface A<T> {
val x: T
var y: List<T>
fun f(p: Collection<T>): Map<T, A<T?>>
}
// MODULE: m2-jvm(m1-common)
// FILE: jvm.kt
impl interface A<T> {
impl val x: T
impl var y: List<T>
impl fun f(p: Collection<T>): Map<T, A<T?>>
}
@@ -0,0 +1,24 @@
// -- Module: <m1-common> --
package
public header interface A</*0*/ T> {
public header abstract val x: T
public header abstract var y: kotlin.collections.List<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract header fun f(/*0*/ p: kotlin.collections.Collection<T>): kotlin.collections.Map<T, A<T?>>
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
// -- Module: <m2-jvm> --
package
public impl interface A</*0*/ T> {
public impl abstract val x: T
public impl abstract var y: kotlin.collections.List<T>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract impl fun f(/*0*/ p: kotlin.collections.Collection<T>): kotlin.collections.Map<T, A<T?>>
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -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)