K2: add explicit cast to Any for Any function calls on stub types

This commit solves a stub type inconsistency problem.
As a part of KT-59369 fix we decided (see commit 299d2799),
that ConeStubTypeForChainInference has a scope of Any,
so we can safely resolve only to equals/hashCode/toString.
However, later we can replace a stub type with some inferred type,
which can have its own equals/hashCode/toString implementation,
while the call still refers Any member.
In this situation FIR2IR decides that we are calling a fake override,
which is not true, in fact we are calling an overriding method.
This leads to a crash in Native backend.

To solve this situation, we provide an explicit cast of a dispatch
receiver with a stub type (ConeStubTypeForChainInference) to Any,
thus confirming directly we are calling Any method and nothing else.

#KT-63932 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-12-11 18:13:06 +01:00
committed by Space Team
parent a0deaea8fe
commit dbca7358af
13 changed files with 196 additions and 2 deletions
@@ -23,7 +23,7 @@ FILE: kt36220.kt
}
)
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.serialize: R|kotlin/Unit|>|(<L> = serialize@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Any?| <inline=NoInline> {
^ R|<local>/it|.R|kotlin/Any.toString|()
^ (R|<local>/it| as R|kotlin/Any|).R|kotlin/Any.toString|()
}
)
}
@@ -0,0 +1,44 @@
FILE: stubCallOnReceiver.kt
public final class TypeDefinition<KotlinType : R|kotlin/Any|> : R|kotlin/Any| {
public constructor<KotlinType : R|kotlin/Any|>(): R|TypeDefinition<KotlinType>| {
super<R|kotlin/Any|>()
}
public final fun parse(parser: R|(@R|kotlin/ParameterName|(name = String(serializedValue)) kotlin/String) -> KotlinType?|): R|kotlin/Unit| {
^parse R|kotlin/TODO|()
}
public final fun serialize(parser: R|KotlinType.() -> kotlin/Any?|): R|kotlin/Unit| {
^serialize R|kotlin/TODO|()
}
}
public final fun <KotlinType : R|kotlin/Any|> defineType(definition: R|TypeDefinition<KotlinType>.() -> kotlin/Unit|): R|kotlin/Unit| {
^defineType R|kotlin/TODO|()
}
public final fun foo(): R|kotlin/Unit| {
R|/defineType|<R|kotlin/Int|>(<L> = defineType@fun R|TypeDefinition<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.parse: R|kotlin/Unit|>|(<L> = parse@fun <anonymous>(it: R|@R|kotlin/ParameterName|(name = String(serializedValue)) kotlin/String|): R|kotlin/Int?| <inline=NoInline> {
^ R|<local>/it|.R|kotlin/text/toInt|()
}
)
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.serialize: R|kotlin/Unit|>|(<L> = serialize@fun R|kotlin/Int|.<anonymous>(): R|kotlin/Any?| <inline=NoInline> {
^ (this@R|special/anonymous| as R|kotlin/Any|).R|kotlin/Any.toString|()
}
)
}
)
}
public final fun bar(): R|kotlin/Unit| {
R|/defineType|<R|kotlin/Int|>(<L> = defineType@fun R|TypeDefinition<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.parse: R|kotlin/Unit|>|(<L> = parse@fun <anonymous>(it: R|@R|kotlin/ParameterName|(name = String(serializedValue)) kotlin/String|): R|kotlin/Int?| <inline=NoInline> {
^ R|<local>/it|.R|kotlin/text/toInt|()
}
)
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.serialize: R|kotlin/Unit|>|(<L> = serialize@fun R|kotlin/Int|.<anonymous>(): R|kotlin/Any?| <inline=NoInline> {
^ (this@R|special/anonymous| as R|kotlin/Any|).R|kotlin/Any.toString|()
}
)
}
)
}
@@ -0,0 +1,24 @@
// FIR_IDENTICAL
// FIR_DUMP
// Similar to kt36220.kt, but with receivers instead of it
class TypeDefinition<KotlinType : Any> {
fun parse(parser: (serializedValue: String) -> KotlinType?): Unit = TODO()
fun serialize(parser: KotlinType.() -> Any?): Unit = TODO()
}
fun <KotlinType : Any> defineType(definition: TypeDefinition<KotlinType>.() -> Unit): Unit = TODO()
fun foo() {
defineType {
parse { it.toInt() }
serialize { toString() }
}
}
fun bar() {
defineType {
parse { it.toInt() }
serialize { this.toString() }
}
}
@@ -0,0 +1,32 @@
FILE: stubCallOnVariable.kt
public final class TypeDefinition<KotlinType : R|kotlin/Any|> : R|kotlin/Any| {
public constructor<KotlinType : R|kotlin/Any|>(): R|TypeDefinition<KotlinType>| {
super<R|kotlin/Any|>()
}
public final fun parse(parser: R|(@R|kotlin/ParameterName|(name = String(serializedValue)) kotlin/String) -> KotlinType?|): R|kotlin/Unit| {
^parse R|kotlin/TODO|()
}
public final fun serialize(parser: R|(@R|kotlin/ParameterName|(name = String(value)) KotlinType) -> kotlin/Any?|): R|kotlin/Unit| {
^serialize R|kotlin/TODO|()
}
}
public final fun <KotlinType : R|kotlin/Any|> defineType(definition: R|TypeDefinition<KotlinType>.() -> kotlin/Unit|): R|kotlin/Unit| {
^defineType R|kotlin/TODO|()
}
public final fun main(): R|kotlin/Unit| {
R|/defineType|<R|kotlin/Int|>(<L> = defineType@fun R|TypeDefinition<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.parse: R|kotlin/Unit|>|(<L> = parse@fun <anonymous>(it: R|@R|kotlin/ParameterName|(name = String(serializedValue)) kotlin/String|): R|kotlin/Int?| <inline=NoInline> {
^ R|<local>/it|.R|kotlin/text/toInt|()
}
)
this@R|special/anonymous|.R|SubstitutionOverride</TypeDefinition.serialize: R|kotlin/Unit|>|(<L> = serialize@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Any?| <inline=NoInline> {
lval i: R|kotlin/Int| = R|<local>/it|
^ (R|<local>/i| as R|kotlin/Any|).R|kotlin/Any.toString|()
}
)
}
)
}
@@ -0,0 +1,19 @@
// FIR_IDENTICAL
// FIR_DUMP
class TypeDefinition<KotlinType : Any> {
fun parse(parser: (serializedValue: String) -> KotlinType?): Unit = TODO()
fun serialize(parser: (value: KotlinType) -> Any?): Unit = TODO()
}
fun <KotlinType : Any> defineType(definition: TypeDefinition<KotlinType>.() -> Unit): Unit = TODO()
fun main() {
defineType {
parse { it.toInt() }
serialize {
val i = it
i.toString()
}
}
}