Do not require backing fields for extension properties

This has no effect on correct code because extension properties cannot
have a backing field anyway and that is checked separately. But this
function is used in psi2ir to determine whether or not to create a
backing field for a property, and in case the code where the property is
declared is unreachable like in KT-44496 and has no explicit getter or
setter, it would previously return true for extension properties, which
on JVM would result in an actual field in the class file, which made no
sense.

After this change, the compiler will actually crash with an exception in
the IR validaton step because the symbol for the field is unbound. That
is a bit better than proceeding to generate potentially invalid
bytecode, but of course a proper fix would be to report an error in the
frontend.

 #KT-44496
This commit is contained in:
Alexander Udalov
2021-03-08 19:40:40 +01:00
parent 81baf4fae5
commit 247efb220c
4 changed files with 26 additions and 0 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
fun PropertyDescriptor.hasBackingField(bindingContext: BindingContext?): Boolean = when {
kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE -> overriddenDescriptors.any { it.hasBackingField(bindingContext) }
extensionReceiverParameter != null -> false
source is KotlinSourceElement && bindingContext != null -> bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, this) ?: false
compileTimeInitializer != null -> true
// backingField != null -> true // TODO: in case of DeserializedPropetyDescriptor this function returns incorrect result for FO field
@@ -0,0 +1,7 @@
// KT-44496
class C {
val todo: String = TODO()
val String.noGetterExtensionProperty: Int
}
@@ -0,0 +1,8 @@
// KT-44496
class C {
val todo: String = TODO()
var String.noSetterExtensionProperty: Int
get() = 42
}
@@ -696,6 +696,16 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
compileKotlin("main.kt", tmpdir, listOf(library), additionalOptions = features)
}
fun testUnreachableExtensionVarPropertyDeclaration() {
val (output, exitCode) = compileKotlin("source.kt", tmpdir, expectedFileName = null)
assertEquals("Output:\n$output", ExitCode.INTERNAL_ERROR, exitCode)
}
fun testUnreachableExtensionValPropertyDeclaration() {
val (output, exitCode) = compileKotlin("source.kt", tmpdir, expectedFileName = null)
assertEquals("Output:\n$output", ExitCode.INTERNAL_ERROR, exitCode)
}
// If this test fails, then bootstrap compiler most likely should be advanced
fun testPreReleaseFlagIsConsistentBetweenBootstrapAndCurrentCompiler() {
val bootstrapCompiler = JarFile(PathUtil.kotlinPathsForCompiler.compilerPath)