Do not load error classes in ReflectionTypes
Previously ReflectionTypes.find returned an error class in case a class is not found in the module dependencies. The problem with this approach is that each call site should call ErrorUtils.isError on the result and report an error if needed, in order to stop this type from reaching the codegen, which can't handle error types. Now we create a MockClassDescriptor instance instead. It's not an error class, so it'll be handled correctly in the codegen. Also its scope is empty and errors are reported on any non-trivial usage (see MissingDependencyClassChecker), so this approach is not worse than error classes #KT-16484 Fixed
This commit is contained in:
+6
-1
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses
|
||||
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
@@ -30,7 +31,11 @@ import org.jetbrains.kotlin.storage.getValue
|
||||
* If there's no Kotlin reflection implementation found in the classpath, checks that there are no usages
|
||||
* of reflection API which will fail at runtime.
|
||||
*/
|
||||
class JvmReflectionAPICallChecker(private val module: ModuleDescriptor, storageManager: StorageManager) : AbstractReflectionApiCallChecker(module, storageManager) {
|
||||
class JvmReflectionAPICallChecker(
|
||||
private val module: ModuleDescriptor,
|
||||
notFoundClasses: NotFoundClasses,
|
||||
storageManager: StorageManager
|
||||
) : AbstractReflectionApiCallChecker(module, notFoundClasses, storageManager) {
|
||||
override val isWholeReflectionApiAvailable by storageManager.createLazyValue {
|
||||
module.findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) != null
|
||||
}
|
||||
|
||||
+7
-2
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -34,12 +35,16 @@ private val ANY_MEMBER_NAMES = setOf("equals", "hashCode", "toString")
|
||||
/**
|
||||
* Checks that there are no usages of reflection API which will fail at runtime.
|
||||
*/
|
||||
abstract class AbstractReflectionApiCallChecker(private val module: ModuleDescriptor, storageManager: StorageManager) : CallChecker {
|
||||
abstract class AbstractReflectionApiCallChecker(
|
||||
private val module: ModuleDescriptor,
|
||||
private val notFoundClasses: NotFoundClasses,
|
||||
storageManager: StorageManager
|
||||
) : CallChecker {
|
||||
protected abstract val isWholeReflectionApiAvailable: Boolean
|
||||
protected abstract fun report(element: PsiElement, context: CallCheckerContext)
|
||||
|
||||
private val kPropertyClasses by storageManager.createLazyValue {
|
||||
val reflectionTypes = ReflectionTypes(module)
|
||||
val reflectionTypes = ReflectionTypes(module, notFoundClasses)
|
||||
setOf(reflectionTypes.kProperty0, reflectionTypes.kProperty1, reflectionTypes.kProperty2)
|
||||
}
|
||||
|
||||
|
||||
+2
-11
@@ -120,9 +120,6 @@ class DoubleColonExpressionResolver(
|
||||
val variance =
|
||||
if (result is DoubleColonLHS.Expression && !result.isObjectQualifier) Variance.OUT_VARIANCE else Variance.INVARIANT
|
||||
val kClassType = reflectionTypes.getKClassType(Annotations.EMPTY, type, variance)
|
||||
if (kClassType.isError) {
|
||||
c.trace.report(MISSING_DEPENDENCY_CLASS.on(expression.receiverExpression!!, KotlinBuiltIns.FQ_NAMES.kClass.toSafe()))
|
||||
}
|
||||
return dataFlowAnalyzer.checkType(createTypeInfo(kClassType, dataFlowInfo), expression, c)
|
||||
}
|
||||
}
|
||||
@@ -535,9 +532,6 @@ class DoubleColonExpressionResolver(
|
||||
checkReferenceIsToAllowedMember(descriptor, context.trace, expression)
|
||||
|
||||
val type = createKCallableTypeForReference(descriptor, lhs, reflectionTypes, context.scope.ownerDescriptor) ?: return null
|
||||
if (type.isError) {
|
||||
context.trace.report(MISSING_DEPENDENCY_CLASS.on(expression, KotlinBuiltIns.FQ_NAMES.kCallable.toSafe()))
|
||||
}
|
||||
|
||||
when (descriptor) {
|
||||
is FunctionDescriptor -> bindFunctionReference(expression, type, context)
|
||||
@@ -580,13 +574,10 @@ class DoubleColonExpressionResolver(
|
||||
/* isCoroutine = */ false
|
||||
)
|
||||
|
||||
val parameterTypes = if (type.isError) emptyList() else type.arguments.dropLast(1)
|
||||
val returnType = if (type.isError) null else type.arguments.last().type
|
||||
|
||||
functionDescriptor.initialize(
|
||||
null, null, emptyList(),
|
||||
createValueParametersForInvokeInFunctionType(functionDescriptor, parameterTypes),
|
||||
returnType,
|
||||
createValueParametersForInvokeInFunctionType(functionDescriptor, type.arguments.dropLast(1)),
|
||||
type.arguments.last().type,
|
||||
Modality.FINAL,
|
||||
Visibilities.PUBLIC
|
||||
)
|
||||
|
||||
+4
-2
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
class GeneratorContext(
|
||||
val configuration: Psi2IrConfiguration,
|
||||
@@ -31,6 +33,6 @@ class GeneratorContext(
|
||||
) : IrGeneratorContext(IrBuiltIns(moduleDescriptor.builtIns)) {
|
||||
val sourceManager = PsiSourceManager()
|
||||
|
||||
val reflectionTypes = ReflectionTypes(moduleDescriptor)
|
||||
// TODO: inject a correct StorageManager instance, or store NotFoundClasses inside ModuleDescriptor
|
||||
val reflectionTypes = ReflectionTypes(moduleDescriptor, NotFoundClasses(LockBasedStorageManager.NO_LOCKS, moduleDescriptor))
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -2,6 +2,7 @@ import kotlin.reflect.*
|
||||
|
||||
fun foo() {
|
||||
String::class.primaryConstructor
|
||||
Void::class
|
||||
::foo.isExternal
|
||||
listOf(42)
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
compiler/testData/cli/jvm/noStdlib.kt:1:8: error: unresolved reference: kotlin
|
||||
import kotlin.reflect.*
|
||||
^
|
||||
compiler/testData/cli/jvm/noStdlib.kt:4:5: error: cannot access class 'kotlin.reflect.KClass'. Check your module classpath for missing or conflicting dependencies
|
||||
compiler/testData/cli/jvm/noStdlib.kt:4:19: error: unresolved reference: primaryConstructor
|
||||
String::class.primaryConstructor
|
||||
^
|
||||
compiler/testData/cli/jvm/noStdlib.kt:5:5: error: cannot access class 'kotlin.reflect.KCallable'. Check your module classpath for missing or conflicting dependencies
|
||||
^
|
||||
compiler/testData/cli/jvm/noStdlib.kt:6:11: error: unresolved reference: isExternal
|
||||
::foo.isExternal
|
||||
^
|
||||
compiler/testData/cli/jvm/noStdlib.kt:6:5: error: unresolved reference: listOf
|
||||
^
|
||||
compiler/testData/cli/jvm/noStdlib.kt:7:5: error: unresolved reference: listOf
|
||||
listOf(42)
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
|
||||
Reference in New Issue
Block a user