IR: do not infer array element types for non-annotation constants
While annotations restrict the set of allowed types to a few final built-ins, in arbitrary constants we can have array elements that are some subtype of the array's element type. #KT-48671 Fixed
This commit is contained in:
+6
@@ -818,6 +818,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
|
||||
+6
-7
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
|
||||
import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.pureEndOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.pureStartOffset
|
||||
import org.jetbrains.kotlin.psi2ir.isConstructorDelegatingToSuper
|
||||
@@ -378,7 +379,7 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
|
||||
descriptor, descriptor.type.toIrType(),
|
||||
(descriptor as? ValueParameterDescriptor)?.varargElementType?.toIrType(),
|
||||
name
|
||||
)
|
||||
)
|
||||
|
||||
private fun generateDefaultAnnotationParameterValue(
|
||||
valueExpression: KtExpression,
|
||||
@@ -387,11 +388,9 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
|
||||
val constantDefaultValue =
|
||||
ConstantExpressionEvaluator.getConstant(valueExpression, context.bindingContext)?.toConstantValue(valueParameterDescriptor.type)
|
||||
?: error("Constant value expected for default parameter value in annotation, got $valueExpression")
|
||||
return context.irFactory.createExpressionBody(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
context.constantValueGenerator.generateConstantValueAsExpression(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, constantDefaultValue, valueParameterDescriptor.varargElementType
|
||||
)
|
||||
)
|
||||
val converted = context.constantValueGenerator.generateAnnotationValueAsExpression(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, constantDefaultValue, valueParameterDescriptor
|
||||
) ?: error("Could not convert annotation default ${valueExpression.getElementTextWithContext()}")
|
||||
return context.irFactory.createExpressionBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, converted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,32 @@ abstract class ConstantValueGenerator(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
constantValue: ConstantValue<*>,
|
||||
varargElementType: KotlinType? = null
|
||||
): IrExpression =
|
||||
// Assertion is safe here because annotation calls and class literals are not allowed in constant initializers
|
||||
generateConstantOrAnnotationValueAsExpression(startOffset, endOffset, constantValue, null, varargElementType)!!
|
||||
generateConstantOrAnnotationValueAsExpression(startOffset, endOffset, constantValue, null, null)!!
|
||||
|
||||
/**
|
||||
* @return null if the constant value is an unresolved annotation or an unresolved class literal
|
||||
*/
|
||||
fun generateAnnotationValueAsExpression(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
constantValue: ConstantValue<*>,
|
||||
valueParameter: ValueParameterDescriptor,
|
||||
): IrExpression? =
|
||||
generateConstantOrAnnotationValueAsExpression(
|
||||
startOffset, endOffset, constantValue, valueParameter.type, valueParameter.varargElementType
|
||||
)
|
||||
|
||||
private fun generateConstantOrAnnotationValueAsExpression(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
constantValue: ConstantValue<*>,
|
||||
realType: KotlinType?,
|
||||
varargElementType: KotlinType? = null
|
||||
expectedType: KotlinType?,
|
||||
expectedArrayElementType: KotlinType?
|
||||
): IrExpression? {
|
||||
val constantValueType = constantValue.getType(moduleDescriptor)
|
||||
val constantKtType = realType ?: constantValueType
|
||||
val constantKtType = expectedType ?: constantValueType
|
||||
val constantType = constantKtType.toIrType()
|
||||
|
||||
return when (constantValue) {
|
||||
@@ -72,18 +81,27 @@ abstract class ConstantValueGenerator(
|
||||
is UShortValue -> IrConstImpl.short(startOffset, endOffset, constantType, constantValue.value)
|
||||
|
||||
is ArrayValue -> {
|
||||
val arrayElementType = varargElementType ?: constantValueType.getArrayElementType()
|
||||
// TODO: in `spreadOperatorInAnnotationArguments`, `@A(*arrayOf("a"), *arrayOf("b"))` is incorrectly
|
||||
// translated into `A(xs = [['a'], ['b']])` instead of `A(xs = ['a', 'b'])`. Not using `expectedType`
|
||||
// here masks that.
|
||||
val arrayElementType = expectedArrayElementType ?: constantValueType.getArrayElementType()
|
||||
IrVarargImpl(
|
||||
startOffset, endOffset,
|
||||
constantType,
|
||||
arrayElementType.toIrType(),
|
||||
constantValue.value.mapNotNull {
|
||||
generateConstantOrAnnotationValueAsExpression(startOffset, endOffset, it, arrayElementType)
|
||||
// For annotation arguments, the type of every subexpression can be inferred from the type of the parameter;
|
||||
// for arbitrary constants, we should always take the type inferred by the frontend.
|
||||
val newExpectedType = arrayElementType.takeIf { expectedType != null }
|
||||
generateConstantOrAnnotationValueAsExpression(startOffset, endOffset, it, newExpectedType, null)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is EnumValue -> {
|
||||
// TODO: in `annotationWithKotlinProperty`, `@Foo(KotlinClass.FOO_INT)` is parsed as if `KotlinClass.FOO_INT`
|
||||
// is an EnumValue when it's a read of a `const val` with an Int type. Not using `expectedType` somewhat masks
|
||||
// that - we silently fail to translate the argument because `enumEntryDescriptor` is an error class.
|
||||
val enumEntryDescriptor =
|
||||
constantValueType.memberScope.getContributedClassifier(constantValue.enumEntryName, NoLookupLocation.FROM_BACKEND)
|
||||
?: throw AssertionError("No such enum entry ${constantValue.enumEntryName} in $constantType")
|
||||
@@ -169,16 +187,9 @@ abstract class ConstantValueGenerator(
|
||||
for (valueParameter in substitutedConstructor.valueParameters) {
|
||||
val argumentIndex = valueParameter.index
|
||||
val argumentValue = annotationDescriptor.allValueArguments[valueParameter.name] ?: continue
|
||||
val irArgument = generateConstantOrAnnotationValueAsExpression(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
adjustAnnotationArgumentValue(argumentValue, valueParameter),
|
||||
valueParameter.type,
|
||||
valueParameter.varargElementType
|
||||
)
|
||||
if (irArgument != null) {
|
||||
irCall.putValueArgument(argumentIndex, irArgument)
|
||||
}
|
||||
val adjustedValue = adjustAnnotationArgumentValue(argumentValue, valueParameter)
|
||||
val irArgument = generateAnnotationValueAsExpression(UNDEFINED_OFFSET, UNDEFINED_OFFSET, adjustedValue, valueParameter)
|
||||
irCall.putValueArgument(argumentIndex, irArgument)
|
||||
}
|
||||
|
||||
return irCall
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
val x: Any = arrayOf<Any>(arrayOf("OK"))
|
||||
|
||||
fun box(): String = ((x as Array<Any>)[0] as Array<String>)[0]
|
||||
+6
@@ -746,6 +746,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
|
||||
+6
@@ -818,6 +818,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
|
||||
+5
@@ -668,6 +668,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+5
@@ -323,6 +323,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
Generated
+5
@@ -323,6 +323,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
Generated
+5
@@ -303,6 +303,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
+5
@@ -85,6 +85,11 @@ public class JsLegacyPrimitiveArraysBoxTestGenerated extends AbstractJsLegacyPri
|
||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
Generated
+5
@@ -293,6 +293,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/arrays/arrayPlusAssign.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constantArrayOfAny.kt")
|
||||
public void testConstantArrayOfAny() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/constantArrayOfAny.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachBooleanArray.kt")
|
||||
public void testForEachBooleanArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||
|
||||
Reference in New Issue
Block a user