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");
|
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
|
@Test
|
||||||
@TestMetadata("forEachBooleanArray.kt")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
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.ir.util.declareSimpleFunctionWithOverrides
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.pureEndOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.pureStartOffset
|
import org.jetbrains.kotlin.psi.psiUtil.pureStartOffset
|
||||||
import org.jetbrains.kotlin.psi2ir.isConstructorDelegatingToSuper
|
import org.jetbrains.kotlin.psi2ir.isConstructorDelegatingToSuper
|
||||||
@@ -378,7 +379,7 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
|
|||||||
descriptor, descriptor.type.toIrType(),
|
descriptor, descriptor.type.toIrType(),
|
||||||
(descriptor as? ValueParameterDescriptor)?.varargElementType?.toIrType(),
|
(descriptor as? ValueParameterDescriptor)?.varargElementType?.toIrType(),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun generateDefaultAnnotationParameterValue(
|
private fun generateDefaultAnnotationParameterValue(
|
||||||
valueExpression: KtExpression,
|
valueExpression: KtExpression,
|
||||||
@@ -387,11 +388,9 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
|
|||||||
val constantDefaultValue =
|
val constantDefaultValue =
|
||||||
ConstantExpressionEvaluator.getConstant(valueExpression, context.bindingContext)?.toConstantValue(valueParameterDescriptor.type)
|
ConstantExpressionEvaluator.getConstant(valueExpression, context.bindingContext)?.toConstantValue(valueParameterDescriptor.type)
|
||||||
?: error("Constant value expected for default parameter value in annotation, got $valueExpression")
|
?: error("Constant value expected for default parameter value in annotation, got $valueExpression")
|
||||||
return context.irFactory.createExpressionBody(
|
val converted = context.constantValueGenerator.generateAnnotationValueAsExpression(
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, constantDefaultValue, valueParameterDescriptor
|
||||||
context.constantValueGenerator.generateConstantValueAsExpression(
|
) ?: error("Could not convert annotation default ${valueExpression.getElementTextWithContext()}")
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, constantDefaultValue, valueParameterDescriptor.varargElementType
|
return context.irFactory.createExpressionBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, converted)
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,23 +36,32 @@ abstract class ConstantValueGenerator(
|
|||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
constantValue: ConstantValue<*>,
|
constantValue: ConstantValue<*>,
|
||||||
varargElementType: KotlinType? = null
|
|
||||||
): IrExpression =
|
): IrExpression =
|
||||||
// Assertion is safe here because annotation calls and class literals are not allowed in constant initializers
|
// 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
|
* @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(
|
private fun generateConstantOrAnnotationValueAsExpression(
|
||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
constantValue: ConstantValue<*>,
|
constantValue: ConstantValue<*>,
|
||||||
realType: KotlinType?,
|
expectedType: KotlinType?,
|
||||||
varargElementType: KotlinType? = null
|
expectedArrayElementType: KotlinType?
|
||||||
): IrExpression? {
|
): IrExpression? {
|
||||||
val constantValueType = constantValue.getType(moduleDescriptor)
|
val constantValueType = constantValue.getType(moduleDescriptor)
|
||||||
val constantKtType = realType ?: constantValueType
|
val constantKtType = expectedType ?: constantValueType
|
||||||
val constantType = constantKtType.toIrType()
|
val constantType = constantKtType.toIrType()
|
||||||
|
|
||||||
return when (constantValue) {
|
return when (constantValue) {
|
||||||
@@ -72,18 +81,27 @@ abstract class ConstantValueGenerator(
|
|||||||
is UShortValue -> IrConstImpl.short(startOffset, endOffset, constantType, constantValue.value)
|
is UShortValue -> IrConstImpl.short(startOffset, endOffset, constantType, constantValue.value)
|
||||||
|
|
||||||
is ArrayValue -> {
|
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(
|
IrVarargImpl(
|
||||||
startOffset, endOffset,
|
startOffset, endOffset,
|
||||||
constantType,
|
constantType,
|
||||||
arrayElementType.toIrType(),
|
arrayElementType.toIrType(),
|
||||||
constantValue.value.mapNotNull {
|
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 -> {
|
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 =
|
val enumEntryDescriptor =
|
||||||
constantValueType.memberScope.getContributedClassifier(constantValue.enumEntryName, NoLookupLocation.FROM_BACKEND)
|
constantValueType.memberScope.getContributedClassifier(constantValue.enumEntryName, NoLookupLocation.FROM_BACKEND)
|
||||||
?: throw AssertionError("No such enum entry ${constantValue.enumEntryName} in $constantType")
|
?: throw AssertionError("No such enum entry ${constantValue.enumEntryName} in $constantType")
|
||||||
@@ -169,16 +187,9 @@ abstract class ConstantValueGenerator(
|
|||||||
for (valueParameter in substitutedConstructor.valueParameters) {
|
for (valueParameter in substitutedConstructor.valueParameters) {
|
||||||
val argumentIndex = valueParameter.index
|
val argumentIndex = valueParameter.index
|
||||||
val argumentValue = annotationDescriptor.allValueArguments[valueParameter.name] ?: continue
|
val argumentValue = annotationDescriptor.allValueArguments[valueParameter.name] ?: continue
|
||||||
val irArgument = generateConstantOrAnnotationValueAsExpression(
|
val adjustedValue = adjustAnnotationArgumentValue(argumentValue, valueParameter)
|
||||||
UNDEFINED_OFFSET,
|
val irArgument = generateAnnotationValueAsExpression(UNDEFINED_OFFSET, UNDEFINED_OFFSET, adjustedValue, valueParameter)
|
||||||
UNDEFINED_OFFSET,
|
irCall.putValueArgument(argumentIndex, irArgument)
|
||||||
adjustAnnotationArgumentValue(argumentValue, valueParameter),
|
|
||||||
valueParameter.type,
|
|
||||||
valueParameter.varargElementType
|
|
||||||
)
|
|
||||||
if (irArgument != null) {
|
|
||||||
irCall.putValueArgument(argumentIndex, irArgument)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return irCall
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("forEachBooleanArray.kt")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
|
|||||||
+6
@@ -818,6 +818,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
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
|
@Test
|
||||||
@TestMetadata("forEachBooleanArray.kt")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
|
|||||||
+5
@@ -668,6 +668,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
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");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
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");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
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");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
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");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
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");
|
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")
|
@TestMetadata("forEachBooleanArray.kt")
|
||||||
public void testForEachBooleanArray() throws Exception {
|
public void testForEachBooleanArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
runTest("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user