[IR] Forbid unconditional interpretation of IrGetObjectValue
Earlier we always allowed to interpret `IrGetObjectValue` because this value is used in const val getter. But now we do a special check for such getter avoiding visit of `IrGetObjectValue` node. #KT-59775 Fixed
This commit is contained in:
+6
@@ -28623,6 +28623,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
|||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("thisPlusStringWithObject.kt")
|
||||||
|
public void testThisPlusStringWithObject() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusStringWithObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unsignedConst.kt")
|
@TestMetadata("unsignedConst.kt")
|
||||||
public void testUnsignedConst() throws Exception {
|
public void testUnsignedConst() throws Exception {
|
||||||
|
|||||||
+6
@@ -28623,6 +28623,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
|||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("thisPlusStringWithObject.kt")
|
||||||
|
public void testThisPlusStringWithObject() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusStringWithObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unsignedConst.kt")
|
@TestMetadata("unsignedConst.kt")
|
||||||
public void testUnsignedConst() throws Exception {
|
public void testUnsignedConst() throws Exception {
|
||||||
|
|||||||
@@ -305,11 +305,16 @@ internal fun IrClass.getSingleAbstractMethod(): IrFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun IrExpression?.isAccessToNotNullableObject(): Boolean {
|
internal fun IrExpression?.isAccessToNotNullableObject(): Boolean {
|
||||||
if (this !is IrGetValue) return false
|
return when (this) {
|
||||||
val owner = this.symbol.owner
|
is IrGetObjectValue -> !this.type.isNullable()
|
||||||
val expectedClass = this.type.classOrNull?.owner
|
is IrGetValue -> {
|
||||||
if (expectedClass == null || !expectedClass.isObject || this.type.isNullable()) return false
|
val owner = this.symbol.owner
|
||||||
return owner.origin == IrDeclarationOrigin.INSTANCE_RECEIVER || owner.name.asString() == "<this>"
|
val expectedClass = this.type.classOrNull?.owner
|
||||||
|
if (expectedClass == null || !expectedClass.isObject || this.type.isNullable()) return false
|
||||||
|
owner.origin == IrDeclarationOrigin.INSTANCE_RECEIVER || owner.name.asString() == "<this>"
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun IrFunction.isAccessorOfPropertyWithBackingField(): Boolean {
|
internal fun IrFunction.isAccessorOfPropertyWithBackingField(): Boolean {
|
||||||
|
|||||||
+8
-8
@@ -58,17 +58,18 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean {
|
override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean {
|
||||||
|
val owner = expression.symbol.owner
|
||||||
|
|
||||||
|
if (expression.dispatchReceiver.isAccessToNotNullableObject() && expression.isGetterToConstVal()) {
|
||||||
|
return visitBodyIfNeeded(owner, data)
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.mode.canEvaluateExpression(expression)) return false
|
if (!data.mode.canEvaluateExpression(expression)) return false
|
||||||
|
|
||||||
val owner = expression.symbol.owner
|
|
||||||
if (!data.mode.canEvaluateFunction(owner)) return false
|
if (!data.mode.canEvaluateFunction(owner)) return false
|
||||||
|
|
||||||
if (expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()) return true
|
if (expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()) return true
|
||||||
|
|
||||||
if (expression.dispatchReceiver.isAccessToNotNullableObject()) {
|
|
||||||
return expression.isGetterToConstVal()
|
|
||||||
}
|
|
||||||
|
|
||||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true
|
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true
|
||||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true
|
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true
|
||||||
if (!visitValueArguments(expression, data)) return false
|
if (!visitValueArguments(expression, data)) return false
|
||||||
@@ -146,8 +147,7 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue, data: IrInterpreterCheckerData): Boolean {
|
override fun visitGetObjectValue(expression: IrGetObjectValue, data: IrInterpreterCheckerData): Boolean {
|
||||||
// to get object value we need nothing, but it will contain only fields with compile time annotation
|
return data.mode.canEvaluateExpression(expression)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetEnumValue(expression: IrGetEnumValue, data: IrInterpreterCheckerData): Boolean {
|
override fun visitGetEnumValue(expression: IrGetEnumValue, data: IrInterpreterCheckerData): Boolean {
|
||||||
@@ -185,7 +185,7 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
|
|||||||
expression.receiver == null -> property?.isConst == true && owner.initializer?.accept(this, data) == true
|
expression.receiver == null -> property?.isConst == true && owner.initializer?.accept(this, data) == true
|
||||||
owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && property?.isConst == true -> {
|
owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && property?.isConst == true -> {
|
||||||
val receiverComputable = (expression.receiver?.accept(this, data) ?: true)
|
val receiverComputable = (expression.receiver?.accept(this, data) ?: true)
|
||||||
|| expression.isAccessToNotNullableObject()
|
|| expression.receiver.isAccessToNotNullableObject()
|
||||||
val initializerComputable = owner.initializer?.accept(this, data) ?: false
|
val initializerComputable = owner.initializer?.accept(this, data) ?: false
|
||||||
receiverComputable && initializerComputable
|
receiverComputable && initializerComputable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// TARGET_BACKEND: NATIVE
|
// TARGET_BACKEND: NATIVE
|
||||||
// TARGET_BACKEND: JS_IR
|
// TARGET_BACKEND: JS_IR
|
||||||
|
// WITH_STDLIB
|
||||||
fun <T> T.id() = this
|
fun <T> T.id() = this
|
||||||
|
|
||||||
const val someStr = <!EVALUATED("123")!>"123"<!>
|
const val someStr = <!EVALUATED("123")!>"123"<!>
|
||||||
const val otherStr = <!EVALUATED("other")!>"other"<!>
|
const val otherStr = <!EVALUATED("other")!>"other"<!>
|
||||||
|
|
||||||
const val oneVal = <!EVALUATED("1")!>1<!>
|
const val oneVal = <!EVALUATED("1")!>1<!>
|
||||||
|
const val oneUnsignedVal = <!EVALUATED("1")!>1u<!>
|
||||||
|
|
||||||
const val plus1 = someStr.<!EVALUATED("123other")!>plus(otherStr)<!>
|
const val plus1 = someStr.<!EVALUATED("123other")!>plus(otherStr)<!>
|
||||||
const val plus2 = someStr.<!EVALUATED("1231")!>plus(oneVal)<!>
|
const val plus2 = someStr.<!EVALUATED("1231")!>plus(oneVal)<!>
|
||||||
|
const val plus3 = someStr.<!EVALUATED("1231")!>plus(oneUnsignedVal)<!>
|
||||||
|
|
||||||
const val length1 = someStr.<!EVALUATED("3")!>length<!>
|
const val length1 = someStr.<!EVALUATED("3")!>length<!>
|
||||||
const val length2 = otherStr.<!EVALUATED("5")!>length<!>
|
const val length2 = otherStr.<!EVALUATED("5")!>length<!>
|
||||||
@@ -31,6 +34,7 @@ const val toString1 = someStr.<!EVALUATED("123")!>toString()<!>
|
|||||||
fun box(): String {
|
fun box(): String {
|
||||||
if (plus1.id() != "123other") return "Fail 1.1"
|
if (plus1.id() != "123other") return "Fail 1.1"
|
||||||
if (plus2.id() != "1231") return "Fail 1.2"
|
if (plus2.id() != "1231") return "Fail 1.2"
|
||||||
|
if (plus3.id() != "1231") return "Fail 1.3"
|
||||||
|
|
||||||
if (length1.id() != 3) return "Fail 2.1"
|
if (length1.id() != 3) return "Fail 2.1"
|
||||||
if (length2.id() != 5) return "Fail 2.2"
|
if (length2.id() != 5) return "Fail 2.2"
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
// TODO enable for JS, Native when const lowering is applied in corresponding backends
|
||||||
|
|
||||||
|
object Test
|
||||||
|
|
||||||
|
fun test1(): String {
|
||||||
|
val a = "test 1: " + Test
|
||||||
|
|
||||||
|
val test = Test
|
||||||
|
val b = "test 1: " + test
|
||||||
|
|
||||||
|
if (a != b) return "<!EVALUATED("Fail 1: "")!>Fail 1: \"<!>$a<!EVALUATED("" != "")!>\" != \"<!>$b<!EVALUATED(""")!>\"<!>"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2(): String {
|
||||||
|
val a = "test 2: " + Test.toString()
|
||||||
|
|
||||||
|
val test = Test
|
||||||
|
val b = "test 2: " + test.toString()
|
||||||
|
|
||||||
|
if (a != b) return "<!EVALUATED("Fail 2: "")!>Fail 2: \"<!>$a<!EVALUATED("" != "")!>\" != \"<!>$b<!EVALUATED(""")!>\"<!>"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3(): String {
|
||||||
|
val a = "<!EVALUATED("test 3: ")!>test 3: <!>$Test"
|
||||||
|
|
||||||
|
val test = Test
|
||||||
|
val b = "<!EVALUATED("test 3: ")!>test 3: <!>$test"
|
||||||
|
|
||||||
|
if (a != b) return "<!EVALUATED("Fail 3: "")!>Fail 3: \"<!>$a<!EVALUATED("" != "")!>\" != \"<!>$b<!EVALUATED(""")!>\"<!>"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val test1Result = test1()
|
||||||
|
if (test1Result != "OK") return test1Result
|
||||||
|
|
||||||
|
val test2Result = test2()
|
||||||
|
if (test2Result != "OK") return test2Result
|
||||||
|
|
||||||
|
val test3Result = test2()
|
||||||
|
if (test3Result != "OK") return test3Result
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+6
@@ -28623,6 +28623,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("thisPlusStringWithObject.kt")
|
||||||
|
public void testThisPlusStringWithObject() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusStringWithObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unsignedConst.kt")
|
@TestMetadata("unsignedConst.kt")
|
||||||
public void testUnsignedConst() throws Exception {
|
public void testUnsignedConst() throws Exception {
|
||||||
|
|||||||
+6
@@ -28623,6 +28623,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
|
|||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("thisPlusStringWithObject.kt")
|
||||||
|
public void testThisPlusStringWithObject() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusStringWithObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unsignedConst.kt")
|
@TestMetadata("unsignedConst.kt")
|
||||||
public void testUnsignedConst() throws Exception {
|
public void testUnsignedConst() throws Exception {
|
||||||
|
|||||||
+5
@@ -24139,6 +24139,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusString.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisPlusStringWithObject.kt")
|
||||||
|
public void testThisPlusStringWithObject() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/thisPlusStringWithObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsignedConst.kt")
|
@TestMetadata("unsignedConst.kt")
|
||||||
public void testUnsignedConst() throws Exception {
|
public void testUnsignedConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/unsignedConst.kt");
|
runTest("compiler/testData/codegen/box/involvesIrInterpreter/unsignedConst.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user