Do not create error types in deserialization on not found classes

For a class which cannot be resolved in the current deserialization session,
create a special ClassDescriptor instance with an empty scope and put in the
correct package under the current module. Codegen will perfectly map such class
to its JVM signature (because only the precise FQ name is needed, which is
available). For more details on this approach, see the issue description.

 #KT-4328 Fixed
 #KT-11497 Fixed
This commit is contained in:
Alexander Udalov
2016-03-09 20:48:00 +03:00
parent 3944d56ac2
commit accf80a624
17 changed files with 228 additions and 34 deletions
@@ -0,0 +1,9 @@
package a
class A<T> {
inner class Inner<X : Number, Y>
}
class AA<T> {
inner class Inner<U, V>
}
@@ -0,0 +1,9 @@
package b
import a.A
import a.AA
interface B1 {
fun produceA(): A<String>.Inner<Int, Unit>
fun produceAA(): AA<Int>.Inner<Unit, String>
}
@@ -0,0 +1,9 @@
package a
class A<T, U: CharSequence, V> {
inner class Inner<Z>
}
class AA<T, U> {
inner class Inner<V>
}
@@ -0,0 +1,9 @@
package b
import a.A
import a.AA
interface B2 {
fun consumeA(a: A<Int, String, Double>.Inner<B2>)
fun consumeAA(a: AA<Int, Unit>.Inner<String>)
}
@@ -0,0 +1,7 @@
compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt:7:17: error: type mismatch: inferred type is A<String>.Inner<Int, Unit> but A<Int, String, Double>.Inner<B2> was expected
b2.consumeA(b1.produceA())
^
compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt:11:18: error: type mismatch: inferred type is AA<Int>.Inner<Unit, String> but AA<Int, Unit>.Inner<String> was expected
b2.consumeAA(b1.produceAA())
^
COMPILATION_ERROR
@@ -0,0 +1,12 @@
package c
import b.B1
import b.B2
fun testA(b1: B1, b2: B2) {
b2.consumeA(b1.produceA())
}
fun testAA(b1: B1, b2: B2) {
b2.consumeAA(b1.produceAA())
}
@@ -0,0 +1,3 @@
package a
interface A
@@ -0,0 +1,7 @@
package b
import a.A
interface B {
fun foo(): A
}
@@ -0,0 +1,4 @@
compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/source.kt:10:21: error: type mismatch: inferred type is A but String was expected
val x: String = b.foo()
^
COMPILATION_ERROR
@@ -0,0 +1,11 @@
package c
import b.B
fun bar(b: B) {
// Implicit usage of (unavailable) a.A, return value is not used. It should still be an error as in Java
b.foo()
// Return value is used but the type is incorrect, also an error
val x: String = b.foo()
}
@@ -1,8 +1,8 @@
package test
public final annotation class Anno : kotlin.Annotation {
public constructor Anno(/*0*/ e: [ERROR : test.E])
public final val e: [ERROR : test.E]
public constructor Anno(/*0*/ e: test.E)
public final val e: test.E
}
@test.Anno(e = Unresolved enum entry: test/E.ENTRY) public open class Class {
@@ -290,6 +290,19 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
doTestBrokenKotlinLibrary("library", "test/Super.class");
}
public void testMissingDependencySimple() throws Exception {
doTestBrokenKotlinLibrary("library", "a/A.class", "a/A$Inner.class");
}
public void testMissingDependencyConflictingLibraries() throws Exception {
File library1 = copyJarFileWithoutEntry(compileLibrary("library1"),
"a/A.class", "a/A$Inner.class", "a/AA.class", "a/AA$Inner.class");
File library2 = copyJarFileWithoutEntry(compileLibrary("library2"),
"a/A.class", "a/A$Inner.class", "a/AA.class", "a/AA$Inner.class");
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, library1, library2);
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
}
/*test source mapping generation when source info is absent*/
public void testInlineFunWithoutDebugInfo() throws Exception {
compileKotlin("sourceInline.kt", tmpdir);
@@ -88,6 +88,4 @@ class DeserializedType(
}
private class PossiblyInnerTypeCapabilityImpl(override val possiblyInnerType: PossiblyInnerType?) : PossiblyInnerTypeCapability
fun getPresentableText(): String = typeDeserializer.presentableTextForErrorType(typeProto)
}
@@ -0,0 +1,117 @@
/*
* 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.serialization.deserialization
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.ClassTypeConstructorImpl
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.toReadOnlyList
class NotFoundClasses(private val storageManager: StorageManager, private val module: ModuleDescriptor) {
/**
* @param typeParametersCount list of numbers of type parameters in this class and all its outer classes, starting from this class
*/
private data class ClassRequest(val classId: ClassId, val typeParametersCount: List<Int>)
private val packageFragments = storageManager.createMemoizedFunction<FqName, PackageFragmentDescriptor> { fqName ->
EmptyPackageFragmentDescriptor(module, fqName)
}
private val classes = storageManager.createMemoizedFunction<ClassRequest, ClassDescriptor> { request ->
val (classId, typeParametersCount) = request
if (classId.isLocal) {
throw UnsupportedOperationException("Unresolved local class: $classId")
}
val container =
if (classId.isNestedClass) get(classId.outerClassId, typeParametersCount.drop(1))
else packageFragments(classId.packageFqName)
// Treat a class with a nested ClassId as inner for simplicity, otherwise the outer type cannot have generic arguments
val isInner = classId.isNestedClass
MockClassDescriptor(storageManager, container, classId.shortClassName, isInner, typeParametersCount.firstOrNull() ?: 0)
}
class MockClassDescriptor(
storageManager: StorageManager,
container: DeclarationDescriptor,
name: Name,
private val isInner: Boolean,
numberOfDeclaredTypeParameters: Int
) : ClassDescriptorBase(storageManager, container, name, SourceElement.NO_SOURCE) {
private val typeParameters = (1..numberOfDeclaredTypeParameters).map { index ->
TypeParameterDescriptorImpl.createWithDefaultBound(
this, Annotations.EMPTY, false, Variance.INVARIANT, Name.identifier("T$index"), index
)
}
private val typeConstructor = ClassTypeConstructorImpl(
this, Annotations.EMPTY, /* isFinal = */ true, typeParameters, setOf(module.builtIns.anyType)
)
override fun getKind() = ClassKind.CLASS
override fun getModality() = Modality.FINAL
override fun getVisibility() = Visibilities.PUBLIC
override fun getTypeConstructor() = typeConstructor
override fun getDeclaredTypeParameters() = typeParameters
override fun isInner() = isInner
override fun isCompanionObject() = false
override fun isData() = false
override fun getAnnotations() = Annotations.EMPTY
override fun getUnsubstitutedMemberScope() = MemberScope.Empty
override fun getStaticScope() = MemberScope.Empty
override fun getConstructors(): Collection<ConstructorDescriptor> = emptySet()
override fun getUnsubstitutedPrimaryConstructor(): ConstructorDescriptor? = null
override fun getCompanionObjectDescriptor(): ClassDescriptor? = null
override fun toString() = "class $name (not found)"
}
// We create different ClassDescriptor instances for types with the same ClassId but different number of type arguments.
// (This may happen when a class with the same FQ name is instantiated with different type arguments in different modules.)
// It's better than creating just one descriptor because otherwise would fail in multiple places where it's asserted that
// the number of type arguments in a type must be equal to the number of the type parameters of the class
private fun get(classId: ClassId, typeParametersCount: List<Int>): ClassDescriptor {
return classes(ClassRequest(classId, typeParametersCount))
}
fun get(proto: ProtoBuf.Type, nameResolver: NameResolver, typeTable: TypeTable): TypeConstructor {
val classId = nameResolver.getClassId(proto.className)
val typeParametersCount = generateSequence(proto) { it.outerType(typeTable) }.map { it.argumentCount }.toMutableList()
val classNestingLevel = generateSequence(classId) { if (it.isNestedClass) it.outerClassId else null }.count()
while (typeParametersCount.size < classNestingLevel) {
typeParametersCount.add(0)
}
return get(classId, typeParametersCount.toReadOnlyList()).typeConstructor
}
}
@@ -71,10 +71,13 @@ class TypeDeserializer(
fun typeConstructor(proto: ProtoBuf.Type): TypeConstructor =
when {
proto.hasClassName() ->
proto.hasClassName() -> {
classDescriptors(proto.className)?.typeConstructor
?: c.components.notFoundClasses.get(proto, c.nameResolver, c.typeTable)
}
proto.hasTypeParameter() ->
typeParameterTypeConstructor(proto.typeParameter)
?: ErrorUtils.createErrorType("Unknown type parameter ${proto.typeParameter}").constructor
proto.hasTypeParameterName() -> {
val container = c.containingDeclaration
val typeParameters = when (container) {
@@ -86,20 +89,8 @@ class TypeDeserializer(
val parameter = typeParameters.find { it.name.asString() == name }
parameter?.typeConstructor ?: ErrorUtils.createErrorType("Deserialized type parameter $name in $container").constructor
}
else ->
null
} ?: ErrorUtils.createErrorType(presentableTextForErrorType(proto)).constructor
internal fun presentableTextForErrorType(proto: ProtoBuf.Type): String = when {
proto.hasClassName() ->
c.nameResolver.getClassId(proto.className).asSingleFqName().asString()
proto.hasTypeParameter() ->
"Unknown type parameter ${proto.typeParameter}"
proto.hasTypeParameterName() ->
"Unknown type parameter ${c.nameResolver.getString(proto.typeParameterName)}"
else ->
"Unknown type"
}
else -> ErrorUtils.createErrorType("Unknown type").constructor
}
private fun typeParameterTypeConstructor(typeParameterId: Int): TypeConstructor? =
typeParameterDescriptors().get(typeParameterId)?.typeConstructor ?:
@@ -41,6 +41,7 @@ class DeserializationComponents(
val additionalSupertypes: AdditionalSupertypes = AdditionalSupertypes.None
) {
val classDeserializer: ClassDeserializer = ClassDeserializer(this)
val notFoundClasses: NotFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
fun deserializeClass(classId: ClassId): ClassDescriptor? = classDeserializer.deserializeClass(classId)
@@ -25,8 +25,8 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy
import org.jetbrains.kotlin.resolve.OverridingStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinEnum
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.types.AbstractClassTypeConstructor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.upperIfFlexible
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.singletonOrEmptyList
import org.jetbrains.kotlin.utils.toReadOnlyList
@@ -154,24 +153,19 @@ class DeserializedClassDescriptor(
}
override fun computeSupertypes(): Collection<KotlinType> {
val result = ArrayList<KotlinType>(classProto.supertypeCount)
val unresolved = ArrayList<DeserializedType>(0)
val result = classProto.supertypes(c.typeTable).map { supertypeProto ->
c.typeDeserializer.type(supertypeProto)
} + c.components.additionalSupertypes.forClass(this@DeserializedClassDescriptor)
for (supertypeProto in classProto.supertypes(c.typeTable)) {
val supertype = c.typeDeserializer.type(supertypeProto)
if (supertype.isError) {
unresolved.add(supertype.upperIfFlexible() as? DeserializedType ?: error("Not a deserialized type: $supertype"))
}
else {
result.add(supertype)
}
val unresolved = result.mapNotNull { supertype ->
supertype.constructor.declarationDescriptor as? NotFoundClasses.MockClassDescriptor
}
result.addAll(c.components.additionalSupertypes.forClass(this@DeserializedClassDescriptor))
if (unresolved.isNotEmpty()) {
c.components.errorReporter.reportIncompleteHierarchy(
this@DeserializedClassDescriptor, unresolved.map(DeserializedType::getPresentableText))
this@DeserializedClassDescriptor,
unresolved.map { it.classId.asSingleFqName().asString() }
)
}
return result.toReadOnlyList()