psi2ir: Consistently use type unit for statements
These changes allow us to accurately distinguish between statements and expressions in the IR. This also fixes the types of non-exhaustive conditional statements.
This commit is contained in:
committed by
Dmitry Petrov
parent
0da4b06074
commit
af74fd047a
+12
-14
@@ -599,7 +599,10 @@ class ExpressionCodegen(
|
||||
SwitchGenerator(expression, data, this).generate()?.let { return it }
|
||||
|
||||
val endLabel = Label()
|
||||
val exhaustive = expression.branches.any { it.condition.isTrueConst() }
|
||||
val exhaustive = expression.branches.any { it.condition.isTrueConst() } && !expression.type.isUnit()
|
||||
assert(exhaustive || expression.type.isUnit()) {
|
||||
"non-exhaustive conditional should return Unit: ${expression.dump()}"
|
||||
}
|
||||
for (branch in expression.branches) {
|
||||
val elseLabel = Label()
|
||||
if (branch.condition.isFalseConst() || branch.condition.isTrueConst()) {
|
||||
@@ -614,26 +617,21 @@ class ExpressionCodegen(
|
||||
} else {
|
||||
branch.condition.accept(this, data).coerceToBoolean().jumpIfFalse(elseLabel)
|
||||
}
|
||||
val result = branch.result.accept(this, data).coerce(expression.type).materialized
|
||||
val result = branch.result.accept(this, data)
|
||||
if (!exhaustive) {
|
||||
result.discard()
|
||||
} else if (branch.condition.isTrueConst()) {
|
||||
// The rest of the expression is dead code.
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
} else {
|
||||
val materializedResult = result.coerce(expression.type).materialized
|
||||
if (branch.condition.isTrueConst()) {
|
||||
// The rest of the expression is dead code.
|
||||
mv.mark(endLabel)
|
||||
return materializedResult
|
||||
}
|
||||
}
|
||||
mv.goTo(endLabel)
|
||||
mv.mark(elseLabel)
|
||||
}
|
||||
mv.mark(endLabel)
|
||||
// NOTE: using a non-exhaustive if/when as an expression is invalid, so it should theoretically
|
||||
// always return Unit. However, with the current frontend this is not always the case.
|
||||
// Most notably, 1. when all branches return/break/continue, the type is Nothing;
|
||||
// 2. the frontend may sometimes infer Any instead of Unit, probably due to a bug
|
||||
// (see compiler/testData/codegen/box/controlStructures/ifIncompatibleBranches.kt).
|
||||
// It should still be safe to produce a soon-to-be-discarded Unit. (What is not ok is
|
||||
// inserting *any* code here, though, as its line number will be that of the last line
|
||||
// of the last branch.)
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
|
||||
+18
-17
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.psi2ir.generators
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.buildStatement
|
||||
import org.jetbrains.kotlin.ir.builders.irIfThenMaybeElse
|
||||
@@ -38,8 +37,6 @@ import org.jetbrains.kotlin.utils.SmartList
|
||||
class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
|
||||
|
||||
fun generateIfExpression(expression: KtIfExpression): IrExpression {
|
||||
val resultType = getInferredTypeWithImplicitCastsOrFail(expression).toIrType()
|
||||
|
||||
var ktLastIf: KtIfExpression = expression
|
||||
val irBranches = SmartList<IrBranch>()
|
||||
var irElseBranch: IrExpression? = null
|
||||
@@ -61,7 +58,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
|
||||
}
|
||||
}
|
||||
|
||||
return createIrWhen(expression, irBranches, irElseBranch, resultType)
|
||||
return createIrWhen(expression, irBranches, irElseBranch, getExpressionTypeWithCoercionToUnitOrFail(expression).toIrType())
|
||||
}
|
||||
|
||||
private fun generateEmptyBlockForMissingBranch(ktLastIf: KtIfExpression) =
|
||||
@@ -100,16 +97,10 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
|
||||
fun generateWhenExpression(expression: KtWhenExpression): IrExpression {
|
||||
val irSubject = generateWhenSubject(expression)
|
||||
|
||||
val inferredType = getInferredTypeWithImplicitCastsOrFail(expression)
|
||||
|
||||
val resultType = when {
|
||||
// Non-exhaustive when can only be used as statement.
|
||||
expression.isExhaustiveWhen() -> inferredType.toIrType()
|
||||
KotlinBuiltIns.isNothing(inferredType) -> inferredType.toIrType()
|
||||
else -> context.irBuiltIns.unitType
|
||||
}
|
||||
|
||||
val irWhen = IrWhenImpl(expression.startOffsetSkippingComments, expression.endOffset, resultType, IrStatementOrigin.WHEN)
|
||||
val irWhen = IrWhenImpl(
|
||||
expression.startOffsetSkippingComments, expression.endOffset,
|
||||
getExpressionTypeWithCoercionToUnitOrFail(expression).toIrType(), IrStatementOrigin.WHEN
|
||||
)
|
||||
|
||||
for (ktEntry in expression.entries) {
|
||||
if (ktEntry.isElse) {
|
||||
@@ -170,12 +161,22 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
|
||||
private fun generateWhenBody(expression: KtWhenExpression, irSubject: IrVariable?, irWhen: IrWhen): IrExpression =
|
||||
if (irSubject == null) {
|
||||
if (irWhen.branches.isEmpty())
|
||||
IrBlockImpl(expression.startOffsetSkippingComments, expression.endOffset, context.irBuiltIns.unitType, IrStatementOrigin.WHEN)
|
||||
IrBlockImpl(
|
||||
expression.startOffsetSkippingComments,
|
||||
expression.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
IrStatementOrigin.WHEN
|
||||
)
|
||||
else
|
||||
irWhen
|
||||
} else {
|
||||
if (irWhen.branches.isEmpty()) {
|
||||
val irBlock = IrBlockImpl(expression.startOffsetSkippingComments, expression.endOffset, context.irBuiltIns.unitType, IrStatementOrigin.WHEN)
|
||||
val irBlock = IrBlockImpl(
|
||||
expression.startOffsetSkippingComments,
|
||||
expression.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
IrStatementOrigin.WHEN
|
||||
)
|
||||
irBlock.statements.add(irSubject)
|
||||
irBlock
|
||||
} else {
|
||||
@@ -253,4 +254,4 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
|
||||
context.bindingContext[BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, ktExpression]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ class ClassGenerator(
|
||||
delegatedMembers: List<CallableMemberDescriptor>
|
||||
) {
|
||||
val ktDelegateExpression = ktEntry.delegateExpression!!
|
||||
val delegateType = getInferredTypeWithImplicitCastsOrFail(ktDelegateExpression)
|
||||
val delegateType = getTypeInferredByFrontendOrFail(ktDelegateExpression)
|
||||
val superType = getOrFail(BindingContext.TYPE, ktEntry.typeReference!!)
|
||||
val superTypeConstructorDescriptor = superType.constructor.declarationDescriptor
|
||||
val superClass = superTypeConstructorDescriptor as? ClassDescriptor
|
||||
|
||||
+1
-1
@@ -306,7 +306,7 @@ class DelegatedPropertyGenerator(declarationGenerator: DeclarationGenerator) : D
|
||||
return if (provideDelegateResolvedCall != null)
|
||||
provideDelegateResolvedCall.resultingDescriptor.returnType!!
|
||||
else
|
||||
getInferredTypeWithImplicitCastsOrFail(ktDelegate.expression!!)
|
||||
getTypeInferredByFrontendOrFail(ktDelegate.expression!!)
|
||||
}
|
||||
|
||||
private fun generateInitializerForLocalDelegatedPropertyDelegate(
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ class ErrorExpressionGenerator(statementGenerator: StatementGenerator) : Stateme
|
||||
}
|
||||
|
||||
private fun getErrorExpressionType(ktExpression: KtExpression) =
|
||||
getInferredTypeWithImplicitCasts(ktExpression) ?: ErrorUtils.createErrorType("")
|
||||
getTypeInferredByFrontend(ktExpression) ?: ErrorUtils.createErrorType("")
|
||||
|
||||
fun generateErrorSimpleName(ktName: KtSimpleNameExpression): IrExpression = generateErrorExpression(ktName) {
|
||||
val type = getErrorExpressionType(ktName).toIrType()
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.builders.IrGenerator
|
||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorWithScope
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -44,11 +45,20 @@ fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K): V =
|
||||
inline fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K, message: (K) -> String): V =
|
||||
context.bindingContext[slice, key] ?: throw RuntimeException(message(key))
|
||||
|
||||
fun Generator.getInferredTypeWithImplicitCasts(key: KtExpression): KotlinType? =
|
||||
fun Generator.getTypeInferredByFrontend(key: KtExpression): KotlinType? =
|
||||
context.bindingContext.getType(key)
|
||||
|
||||
fun Generator.getInferredTypeWithImplicitCastsOrFail(key: KtExpression): KotlinType =
|
||||
getInferredTypeWithImplicitCasts(key) ?: throw RuntimeException("No type for expression: ${key.text}")
|
||||
fun Generator.getTypeInferredByFrontendOrFail(key: KtExpression): KotlinType =
|
||||
getTypeInferredByFrontend(key) ?: throw RuntimeException("No type for expression: ${key.text}")
|
||||
|
||||
fun Generator.getExpressionTypeWithCoercionToUnit(key: KtExpression): KotlinType? =
|
||||
if (key.isUsedAsExpression(context.bindingContext))
|
||||
getTypeInferredByFrontend(key)
|
||||
else
|
||||
context.builtIns.unitType
|
||||
|
||||
fun Generator.getExpressionTypeWithCoercionToUnitOrFail(key: KtExpression): KotlinType =
|
||||
getExpressionTypeWithCoercionToUnit(key) ?: throw RuntimeException("No type for expression: ${key.text}")
|
||||
|
||||
fun Generator.getResolvedCall(key: KtElement): ResolvedCall<out CallableDescriptor>? =
|
||||
key.getResolvedCall(context.bindingContext)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class LocalClassGenerator(statementGenerator: StatementGenerator) : StatementGen
|
||||
fun generateObjectLiteral(ktObjectLiteral: KtObjectLiteralExpression): IrStatement {
|
||||
val startOffset = ktObjectLiteral.startOffsetSkippingComments
|
||||
val endOffset = ktObjectLiteral.endOffset
|
||||
val objectLiteralType = getInferredTypeWithImplicitCastsOrFail(ktObjectLiteral).toIrType()
|
||||
val objectLiteralType = getTypeInferredByFrontendOrFail(ktObjectLiteral).toIrType()
|
||||
val irBlock = IrBlockImpl(startOffset, endOffset, objectLiteralType, IrStatementOrigin.OBJECT_LITERAL)
|
||||
|
||||
val irClass = DeclarationGenerator(statementGenerator.context).generateClassOrObjectDeclaration(ktObjectLiteral.objectDeclaration)
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ class LocalFunctionGenerator(statementGenerator: StatementGenerator) : Statement
|
||||
|
||||
fun generateLambda(ktLambda: KtLambdaExpression): IrStatement {
|
||||
val ktFun = ktLambda.functionLiteral
|
||||
val lambdaExpressionType = getInferredTypeWithImplicitCastsOrFail(ktLambda).toIrType()
|
||||
val lambdaExpressionType = getTypeInferredByFrontendOrFail(ktLambda).toIrType()
|
||||
val irLambdaFunction = FunctionGenerator(context).generateLambdaFunctionDeclaration(ktFun)
|
||||
|
||||
return IrFunctionExpressionImpl(
|
||||
@@ -43,7 +43,7 @@ class LocalFunctionGenerator(statementGenerator: StatementGenerator) : Statement
|
||||
val irFun = generateFunctionDeclaration(ktFun)
|
||||
if (ktFun.name != null) return irFun
|
||||
|
||||
val funExpressionType = getInferredTypeWithImplicitCastsOrFail(ktFun).toIrType()
|
||||
val funExpressionType = getTypeInferredByFrontendOrFail(ktFun).toIrType()
|
||||
return IrFunctionExpressionImpl(
|
||||
ktFun.startOffset, ktFun.endOffset,
|
||||
funExpressionType,
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
fun generateClassLiteral(ktClassLiteral: KtClassLiteralExpression): IrExpression {
|
||||
val ktArgument = ktClassLiteral.receiverExpression!!
|
||||
val lhs = getOrFail(BindingContext.DOUBLE_COLON_LHS, ktArgument)
|
||||
val resultType = getInferredTypeWithImplicitCastsOrFail(ktClassLiteral).toIrType()
|
||||
val resultType = getTypeInferredByFrontendOrFail(ktClassLiteral).toIrType()
|
||||
|
||||
return if (lhs is DoubleColonLHS.Expression && !lhs.isObjectQualifier) {
|
||||
IrGetClassImpl(
|
||||
@@ -69,7 +69,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
).call { dispatchReceiverValue, extensionReceiverValue ->
|
||||
generateCallableReference(
|
||||
ktCallableReference,
|
||||
getInferredTypeWithImplicitCastsOrFail(ktCallableReference),
|
||||
getTypeInferredByFrontendOrFail(ktCallableReference),
|
||||
callBuilder.descriptor,
|
||||
callBuilder.typeArguments
|
||||
).also { irCallableReference ->
|
||||
|
||||
+3
-3
@@ -161,7 +161,7 @@ class StatementGenerator(
|
||||
val isBlockBody = expression.parent is KtDeclarationWithBody && expression.parent !is KtFunctionLiteral
|
||||
if (isBlockBody) throw AssertionError("Use IrBlockBody and corresponding body generator to generate blocks as function bodies")
|
||||
|
||||
val returnType = getInferredTypeWithImplicitCasts(expression) ?: context.builtIns.unitType
|
||||
val returnType = getExpressionTypeWithCoercionToUnitOrFail(expression)
|
||||
val irBlock = IrBlockImpl(expression.startOffsetSkippingComments, expression.endOffset, returnType.toIrType())
|
||||
|
||||
expression.statements.forEach {
|
||||
@@ -226,14 +226,14 @@ class StatementGenerator(
|
||||
context.constantValueGenerator.generateConstantValueAsExpression(
|
||||
expression.startOffsetSkippingComments,
|
||||
expression.endOffset,
|
||||
constant.toConstantValue(getInferredTypeWithImplicitCastsOrFail(expression))
|
||||
constant.toConstantValue(getTypeInferredByFrontendOrFail(expression))
|
||||
)
|
||||
|
||||
override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: Nothing?): IrStatement {
|
||||
val startOffset = expression.startOffsetSkippingComments
|
||||
val endOffset = expression.endOffset
|
||||
|
||||
val resultType = getInferredTypeWithImplicitCastsOrFail(expression).toIrType()
|
||||
val resultType = getTypeInferredByFrontendOrFail(expression).toIrType()
|
||||
val entries = expression.entries.map { it.genExpr() }.postprocessStringTemplateEntries()
|
||||
|
||||
return when (entries.size) {
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
class TryCatchExpressionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
|
||||
fun generateTryCatch(ktTry: KtTryExpression): IrExpression {
|
||||
val resultType = getInferredTypeWithImplicitCastsOrFail(ktTry).toIrType()
|
||||
val resultType = getExpressionTypeWithCoercionToUnitOrFail(ktTry).toIrType()
|
||||
val irTryCatch = IrTryImpl(ktTry.startOffsetSkippingComments, ktTry.endOffset, resultType)
|
||||
|
||||
irTryCatch.tryResult = ktTry.tryBlock.genExpr()
|
||||
|
||||
Reference in New Issue
Block a user