[FIR2IR] Fix translating primitive array types

This commit fixes two issues in the existing implementation of translating primitive array types:

 * IrType.getArrayElementType throws an exception when the receiver is a primitive array type, because IR expects primitive array types use symbols defined in IrBuiltIns, but fir2ir translation doesn't;
 * IteratorNext.toCallable assumes all element types are boxed.

The first issue is fixed by changing the fir2ir type translation to use symbols in IrBuiltIns for primitive array types, and the second by not unboxing primitive types.
This commit is contained in:
Juan Chen
2019-12-20 10:39:38 -08:00
committed by Mikhail Glukhikh
parent 1cf582e9ed
commit 3dc58bc995
52 changed files with 65 additions and 71 deletions
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrType
@@ -25,6 +26,8 @@ import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
import org.jetbrains.kotlin.types.Variance
@@ -40,56 +43,82 @@ internal fun <T : IrElement> FirElement.convertWithOffsets(
internal fun createErrorType(): IrErrorType = IrErrorTypeImpl(null, emptyList(), Variance.INVARIANT)
fun FirTypeRef.toIrType(session: FirSession, declarationStorage: Fir2IrDeclarationStorage): IrType {
fun FirTypeRef.toIrType(session: FirSession, declarationStorage: Fir2IrDeclarationStorage, irBuiltIns: IrBuiltIns): IrType {
if (this !is FirResolvedTypeRef) {
return createErrorType()
}
return type.toIrType(session, declarationStorage)
return type.toIrType(session, declarationStorage, irBuiltIns)
}
fun ConeKotlinType.toIrType(session: FirSession, declarationStorage: Fir2IrDeclarationStorage, definitelyNotNull: Boolean = false): IrType {
fun ConeKotlinType.toIrType(
session: FirSession,
declarationStorage: Fir2IrDeclarationStorage,
irBuiltIns: IrBuiltIns,
definitelyNotNull: Boolean = false
): IrType {
return when (this) {
is ConeKotlinErrorType -> createErrorType()
is ConeLookupTagBasedType -> {
val firSymbol = this.lookupTag.toSymbol(session) ?: return createErrorType()
val irSymbol = firSymbol.toIrSymbol(session, declarationStorage)
val irSymbol = getPrimitiveArrayType(this.classId, irBuiltIns) ?: run {
val firSymbol = this.lookupTag.toSymbol(session) ?: return createErrorType()
firSymbol.toIrSymbol(session, declarationStorage)
}
// TODO: annotations
IrSimpleTypeImpl(
irSymbol, !definitelyNotNull && this.isMarkedNullable,
typeArguments.map { it.toIrTypeArgument(session, declarationStorage) },
typeArguments.map { it.toIrTypeArgument(session, declarationStorage, irBuiltIns) },
emptyList()
)
}
is ConeFlexibleType -> {
// TODO: yet we take more general type. Not quite sure it's Ok
upperBound.toIrType(session, declarationStorage, definitelyNotNull)
upperBound.toIrType(session, declarationStorage, irBuiltIns, definitelyNotNull)
}
is ConeCapturedType -> TODO()
is ConeDefinitelyNotNullType -> {
original.toIrType(session, declarationStorage, definitelyNotNull = true)
original.toIrType(session, declarationStorage, irBuiltIns, definitelyNotNull = true)
}
is ConeIntersectionType -> {
// TODO: add intersectionTypeApproximation
intersectedTypes.first().toIrType(session, declarationStorage, definitelyNotNull)
intersectedTypes.first().toIrType(session, declarationStorage, irBuiltIns, definitelyNotNull)
}
is ConeStubType -> createErrorType()
is ConeIntegerLiteralType -> getApproximatedType().toIrType(session, declarationStorage, definitelyNotNull)
is ConeIntegerLiteralType -> getApproximatedType().toIrType(session, declarationStorage, irBuiltIns, definitelyNotNull)
}
}
fun ConeKotlinTypeProjection.toIrTypeArgument(session: FirSession, declarationStorage: Fir2IrDeclarationStorage): IrTypeArgument {
private fun getPrimitiveArrayType(classId: ClassId?, irBuiltIns: IrBuiltIns): IrClassifierSymbol? {
val irType = when (classId) {
ClassId(FqName("kotlin"), FqName("BooleanArray"), false) -> irBuiltIns.booleanType
ClassId(FqName("kotlin"), FqName("ByteArray"), false) -> irBuiltIns.byteType
ClassId(FqName("kotlin"), FqName("CharArray"), false) -> irBuiltIns.charType
ClassId(FqName("kotlin"), FqName("DoubleArray"), false) -> irBuiltIns.doubleType
ClassId(FqName("kotlin"), FqName("FloatArray"), false) -> irBuiltIns.floatType
ClassId(FqName("kotlin"), FqName("IntArray"), false) -> irBuiltIns.intType
ClassId(FqName("kotlin"), FqName("LongArray"), false) -> irBuiltIns.longType
ClassId(FqName("kotlin"), FqName("ShortArray"), false) -> irBuiltIns.shortType
else -> null
}
return irType?.let { irBuiltIns.primitiveArrayForType.getValue(it) }
}
fun ConeKotlinTypeProjection.toIrTypeArgument(
session: FirSession,
declarationStorage: Fir2IrDeclarationStorage,
irBuiltIns: IrBuiltIns
): IrTypeArgument {
return when (this) {
ConeStarProjection -> IrStarProjectionImpl
is ConeKotlinTypeProjectionIn -> {
val irType = this.type.toIrType(session, declarationStorage)
val irType = this.type.toIrType(session, declarationStorage, irBuiltIns)
makeTypeProjection(irType, Variance.IN_VARIANCE)
}
is ConeKotlinTypeProjectionOut -> {
val irType = this.type.toIrType(session, declarationStorage)
val irType = this.type.toIrType(session, declarationStorage, irBuiltIns)
makeTypeProjection(irType, Variance.OUT_VARIANCE)
}
is ConeKotlinType -> {
val irType = toIrType(session, declarationStorage)
val irType = toIrType(session, declarationStorage, irBuiltIns)
makeTypeProjection(irType, Variance.INVARIANT)
}
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.descriptors.*
@@ -40,7 +41,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
class Fir2IrDeclarationStorage(
private val session: FirSession,
private val irSymbolTable: SymbolTable,
private val moduleDescriptor: FirModuleDescriptor
private val moduleDescriptor: FirModuleDescriptor,
private val irBuiltIns: IrBuiltIns
) {
private val firSymbolProvider = session.firSymbolProvider
@@ -90,6 +92,9 @@ class Fir2IrDeclarationStorage(
irSymbolTable.leaveScope(descriptor)
}
private fun FirTypeRef.toIrType(session: FirSession, declarationStorage: Fir2IrDeclarationStorage) =
toIrType(session, declarationStorage, irBuiltIns)
private fun getIrExternalPackageFragment(fqName: FqName): IrExternalPackageFragment {
return fragmentCache.getOrPut(fqName) {
// TODO: module descriptor is wrong here
@@ -75,7 +75,7 @@ class Fir2IrVisitor(
private val typeContext = session.typeContext
private val declarationStorage = Fir2IrDeclarationStorage(session, symbolTable, moduleDescriptor)
private val declarationStorage = Fir2IrDeclarationStorage(session, symbolTable, moduleDescriptor, irBuiltIns)
private val nothingType = session.builtinTypes.nothingType.toIrType(session, declarationStorage)
@@ -138,6 +138,9 @@ class Fir2IrVisitor(
return result
}
private fun FirTypeRef.toIrType(session: FirSession, declarationStorage: Fir2IrDeclarationStorage) =
toIrType(session, declarationStorage, irBuiltIns)
override fun visitElement(element: FirElement, data: Any?): IrElement {
TODO("Should not be here: ${element.render()}")
}
@@ -711,7 +714,7 @@ class Fir2IrVisitor(
private fun FirAnnotationCall.toIrExpression(): IrExpression {
val coneType = (annotationTypeRef as? FirResolvedTypeRef)?.type as? ConeLookupTagBasedType
val firSymbol = coneType?.lookupTag?.toSymbol(session) as? FirClassSymbol
val type = coneType?.toIrType(this@Fir2IrVisitor.session, declarationStorage)
val type = coneType?.toIrType(session, declarationStorage, irBuiltIns)
val symbol = type?.classifierOrNull
return convertWithOffsets { startOffset, endOffset ->
when (symbol) {
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
@@ -31,7 +32,8 @@ import org.jetbrains.org.objectweb.asm.Type
object IteratorNext : IntrinsicMethod() {
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
val type = AsmUtil.unboxType(signature.returnType)
// If the array element type is unboxed primitive, do not unbox. Otherwise AsmUtil.unbox throws exception
val type = if (AsmUtil.isBoxedPrimitiveType(signature.returnType)) AsmUtil.unboxType(signature.returnType) else signature.returnType
val newSignature = signature.newReturnType(type)
return IrIntrinsicFunction.create(expression, newSignature, context, AsmTypes.OBJECT_TYPE) {
val primitiveClassName = getKotlinPrimitiveClassName(type)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in BooleanArray(5)) {
if (x != false) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in ByteArray(5)) {
if (x != 0.toByte()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in CharArray(5)) {
if (x != 0.toChar()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in DoubleArray(5)) {
if (x != 0.toDouble()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in FloatArray(5)) {
if (x != 0.toFloat()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in IntArray(5)) {
if (x != 0) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in LongArray(5)) {
if (x != 0.toLong()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
for (x in ShortArray(5)) {
if (x != 0.toShort()) return "Fail $x"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = BooleanArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = ByteArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = ByteArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = CharArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = DoubleArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = FloatArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = IntArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = LongArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = LongArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val a = ShortArray(5)
val x = a.iterator()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun test(str: String): String {
var s = ""
for (i in 1..3) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
var s = "OK"
for (i in 1..3) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
var r = ""
for (i in 1..1) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box() : String {
val a = IntArray (5)
var i = 0
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
package demo2
fun print(o : Any?) {}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
package demo2
fun print(o : Any?) {}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
val r = 1.toLong()..2
var s = ""
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun idiv(a: Int, b: Int): Int =
if (b == 0) throw Exception("Division by zero") else a / b
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
fun test() {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
fun test() {
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperFinally
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
fun test() {
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperFinally
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
fun test() {
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun test1(): String {
var r = ""
for (i in 1..2) {
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun test1(): Boolean {
test1@ for(i in 1..2) {
continue@test1
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
operator fun Int.component1() = this + 1
operator fun Int.component2() = this + 2
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
operator fun Long.component1() = this + 1
operator fun Long.component2() = this + 2
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
operator fun Int.component1() = this + 1
operator fun Int.component2() = this + 2
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
operator fun Int.component1() = this + 1
operator fun Int.component2() = this + 2
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
operator fun Long.component1() = this + 1
operator fun Long.component2() = this + 2
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// KJS_WITH_FULL_RUNTIME
// Auto-generated by org.jetbrains.kotlin.generators.tests.GenerateRangesCodegenTestData. DO NOT EDIT!
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun test1(): Long {
var s = 0L
for (i in 1L..4) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// KJS_WITH_FULL_RUNTIME
// Auto-generated by org.jetbrains.kotlin.generators.tests.GenerateRangesCodegenTestData. DO NOT EDIT!
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
var result = 0
val intRange: IntProgression = 1..3
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
var result = 0
val intRange = 1..3
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box(): String {
var result = 0
for (i: Int? in 1..3) {
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun stringConcat(n : Int) : String? {
var string : String? = ""
for (i in 0..(n - 1))
@@ -135,7 +135,7 @@ public class GenerateRangesCodegenTestData {
out.printf("// IGNORE_BACKEND: %s%n%n", backendName);
}
private static void writeToFile(File file, String generatedBody, boolean isForUnsigned) {
private static void writeToFile(File file, String generatedBody, boolean isForUnsigned, boolean ignoreFrontendIR) {
PrintWriter out;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
@@ -145,7 +145,9 @@ public class GenerateRangesCodegenTestData {
throw new AssertionError(e);
}
out.println("// IGNORE_BACKEND_FIR: JVM_IR");
if (ignoreFrontendIR) {
out.println("// IGNORE_BACKEND_FIR: JVM_IR");
}
out.println("// KJS_WITH_FULL_RUNTIME");
out.println("// Auto-generated by " + GenerateRangesCodegenTestData.class.getName() + ". DO NOT EDIT!");
out.println("// WITH_RUNTIME");
@@ -225,10 +227,10 @@ public class GenerateRangesCodegenTestData {
}
String fileName = testFunName + ".kt";
writeToFile(new File(AS_LITERAL_DIR, fileName), asLiteralBody.toString(), false);
writeToFile(new File(AS_EXPRESSION_DIR, fileName), asExpressionBody.toString(), false);
writeToFile(new File(UNSIGNED_AS_LITERAL_DIR, fileName), unsignedAsLiteralBody.toString(), true);
writeToFile(new File(UNSIGNED_AS_EXPRESSION_DIR, fileName), unsignedAsExpressionBody.toString(), true);
writeToFile(new File(AS_LITERAL_DIR, fileName), asLiteralBody.toString(), false, !fileName.equals("emptyRange.kt"));
writeToFile(new File(AS_EXPRESSION_DIR, fileName), asExpressionBody.toString(), false, !fileName.equals("emptyRange.kt"));
writeToFile(new File(UNSIGNED_AS_LITERAL_DIR, fileName), unsignedAsLiteralBody.toString(), true, true);
writeToFile(new File(UNSIGNED_AS_EXPRESSION_DIR, fileName), unsignedAsExpressionBody.toString(), true, true);
}
}
}