[FIR2IR] Support safe calls

This commit is contained in:
Mikhail Glukhikh
2020-03-20 15:57:39 +03:00
parent 2e8b22e4be
commit ca22e05acd
23 changed files with 307 additions and 143 deletions
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.defaultType
internal class CallGenerator(
@@ -43,7 +44,49 @@ internal class CallGenerator(
private fun ConeKotlinType.toIrType(): IrType = with(typeConverter) { toIrType() }
fun convertToIrCall(qualifiedAccess: FirQualifiedAccess, typeRef: FirTypeRef): IrExpression {
val type = typeRef.toIrType()
val explicitReceiver = qualifiedAccess.explicitReceiver
if (!qualifiedAccess.safe || explicitReceiver == null) {
return convertToUnsafeIrCall(qualifiedAccess, typeRef)
}
return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
val type = typeRef.toIrType()
val callableSymbol = (qualifiedAccess.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
val typeShouldBeNotNull = callableSymbol?.fir?.returnTypeRef?.coneTypeSafe<ConeKotlinType>()?.isNullable == false
val unsafeIrCall = convertToUnsafeIrCall(qualifiedAccess, typeRef, makeNotNull = typeShouldBeNotNull)
IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.SAFE_CALL).apply {
val receiver = visitor.convertToIrExpression(explicitReceiver)
val receiverVariable = declarationStorage.declareTemporaryVariable(receiver).apply {
parent = conversionScope.parentFromStack()
}
statements += receiverVariable
statements += IrWhenImpl(startOffset, endOffset, type).apply {
val variableSymbol = symbolTable.referenceValue(receiverVariable.descriptor)
val condition = IrCallImpl(
startOffset, endOffset, irBuiltIns.booleanType, irBuiltIns.eqeqSymbol, origin = IrStatementOrigin.EQEQ
).apply {
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, variableSymbol))
putValueArgument(1, IrConstImpl.constNull(startOffset, endOffset, irBuiltIns.nothingNType))
}
branches += IrBranchImpl(
condition, IrConstImpl.constNull(startOffset, endOffset, irBuiltIns.nothingNType)
)
val newReceiver = IrGetValueImpl(startOffset, endOffset, variableSymbol)
val replacedCall = unsafeIrCall.replaceReceiver(
newReceiver, isDispatch = explicitReceiver == qualifiedAccess.dispatchReceiver
)
branches += IrElseBranchImpl(
IrConstImpl.boolean(startOffset, endOffset, irBuiltIns.booleanType, true),
replacedCall
)
}
}
}
}
private fun convertToUnsafeIrCall(
qualifiedAccess: FirQualifiedAccess, typeRef: FirTypeRef, makeNotNull: Boolean = false
): IrExpression {
val type = typeRef.toIrType().let { if (makeNotNull) it.makeNotNull() else it }
val symbol = qualifiedAccess.calleeReference.toSymbol(
session,
classifierStorage,
@@ -293,6 +336,22 @@ internal class CallGenerator(
}
}
private fun IrExpression.replaceReceiver(newReceiver: IrExpression, isDispatch: Boolean): IrExpression {
when (this) {
is IrCallImpl -> {
if (!isDispatch) {
extensionReceiver = newReceiver
} else {
dispatchReceiver = newReceiver
}
}
is IrFieldExpressionBase -> {
receiver = newReceiver
}
}
return this
}
private fun generateErrorCallExpression(
startOffset: Int,
endOffset: Int,