JVM_IR: handle Nothing and Unit more consistently.
* In blocks, discard the result of any statement that has a return type other than void. This was previously done by wrapping each statement into an "implicit Unit conversion" that was actually compiled down to a stack pop instead. If an expression happened to already have type Unit, however, such a conversion was not inserted, resulting in a stray reference on the stack. These conversions are now redundant and should probably be removed. * In assignments and non-exhaustive conditionals, materialize a Unit on the stack to avoid depth mismatches that trip up the bytecode validator. Because such expressions are generally used at block level (and, indeed, the frontend will reject a non-exhaustive conditional used as an expression), combined with the above change this results in no additional GETSTATIC opcodes, as they are immediately removed by the peephole optimizer.
This commit is contained in:
+80
-89
@@ -49,6 +49,8 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
|||||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
|
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
@@ -127,16 +129,13 @@ class ExpressionCodegen(
|
|||||||
val result = irFunction.body!!.accept(this, info)
|
val result = irFunction.body!!.accept(this, info)
|
||||||
markFunctionLineNumber()
|
markFunctionLineNumber()
|
||||||
val returnType = typeMapper.mapReturnType(irFunction.descriptor)
|
val returnType = typeMapper.mapReturnType(irFunction.descriptor)
|
||||||
if (irFunction.body is IrExpressionBody) {
|
val body = irFunction.body!!
|
||||||
|
// If this function has an expression body, return the result of that expression.
|
||||||
|
// Otherwise, if it does not end in a return statement, it must be void-returning,
|
||||||
|
// and an explicit return instruction at the end is still required to pass validation.
|
||||||
|
if (body !is IrStatementContainer || body.statements.lastOrNull() !is IrReturn) {
|
||||||
|
coerce(result.type, returnType, mv)
|
||||||
mv.areturn(returnType)
|
mv.areturn(returnType)
|
||||||
//TODO merge branch inside next one
|
|
||||||
} else if (!endsWithReturn(irFunction.body!!)) {
|
|
||||||
if (returnType == Type.VOID_TYPE) {
|
|
||||||
mv.areturn(returnType)
|
|
||||||
} else {
|
|
||||||
StackValue.none().put(returnType, null, mv)
|
|
||||||
mv.areturn(returnType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writeLocalVariablesInTable(info)
|
writeLocalVariablesInTable(info)
|
||||||
writeParameterInLocalVariableTable(startLabel)
|
writeParameterInLocalVariableTable(startLabel)
|
||||||
@@ -194,19 +193,9 @@ class ExpressionCodegen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun endsWithReturn(body: IrBody): Boolean {
|
private fun StackValue.discard(): StackValue {
|
||||||
val lastStatement = if (body is IrStatementContainer) {
|
coerce(type, Type.VOID_TYPE, mv)
|
||||||
body.statements.lastOrNull() ?: body
|
return none()
|
||||||
} else body
|
|
||||||
return lastStatement is IrReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
|
||||||
return body.statements.fold(none()) { _, exp ->
|
|
||||||
exp.accept(this, data).also {
|
|
||||||
(exp as? IrExpression)?.markEndOfStatementIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitBlock(expression: IrBlock, data: BlockInfo): StackValue {
|
override fun visitBlock(expression: IrBlock, data: BlockInfo): StackValue {
|
||||||
@@ -244,13 +233,21 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo): StackValue {
|
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo): StackValue {
|
||||||
val result = expression.statements.fold(none()) { _, exp ->
|
return container.statements.fold(none()) { prev, exp ->
|
||||||
//coerceNotToUnit(r.type, Type.VOID_TYPE)
|
prev.discard()
|
||||||
exp.accept(this, data)
|
gen(exp, data).also {
|
||||||
|
(exp as? IrExpression)?.markEndOfStatementIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Blocks with nothing type do not generate a value on the stack.
|
}
|
||||||
if (expression.type.isNothing()) return none()
|
|
||||||
|
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
||||||
|
return visitStatementContainer(body, data).discard()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo): StackValue {
|
||||||
|
val result = visitStatementContainer(expression, data)
|
||||||
return coerceNotToUnit(result.type, result.kotlinType, expression.type.toKotlinType())
|
return coerceNotToUnit(result.type, result.kotlinType, expression.type.toKotlinType())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,19 +455,21 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
|
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
|
||||||
val expressionValue = expression.value
|
val expressionValue = expression.value
|
||||||
if (irFunction is IrConstructor && irFunction.isPrimary) {
|
// Do not add redundant field initializers that initialize to default values.
|
||||||
// Do not add redundant field initializers that initialize to default values.
|
// "expression.origin == null" means that the field is initialized when it is declared,
|
||||||
// "expression.origin == null" means that the field is initialized when it is declared,
|
// i.e., not in an initializer block or constructor body.
|
||||||
// i.e., not in an initializer block or constructor body.
|
val skip = irFunction is IrConstructor && irFunction.isPrimary &&
|
||||||
if (expression.origin == null && expressionValue is IrConst<*> && isDefaultValueForType(
|
expression.origin == null && expressionValue is IrConst<*> &&
|
||||||
expression.symbol.owner.type, expressionValue
|
isDefaultValueForType(expression.symbol.owner.type, expressionValue)
|
||||||
)
|
if (!skip) {
|
||||||
) return none()
|
expression.markLineNumber(startOffset = true)
|
||||||
|
val fieldValue = generateFieldValue(expression, data)
|
||||||
|
fieldValue.store(expressionValue.accept(this, data), mv)
|
||||||
}
|
}
|
||||||
expression.markLineNumber(startOffset = true)
|
// Assignments can be used as expressions, so return a value. Redundant pushes
|
||||||
val fieldValue = generateFieldValue(expression, data)
|
// will be eliminated by the peephole optimizer.
|
||||||
fieldValue.store(expressionValue.accept(this, data), mv)
|
putUnitInstance(mv)
|
||||||
return none()
|
return onStack(AsmTypes.UNIT_TYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -538,7 +537,10 @@ class ExpressionCodegen(
|
|||||||
expression.value.markLineNumber(startOffset = true)
|
expression.value.markLineNumber(startOffset = true)
|
||||||
val value = expression.value.accept(this, data)
|
val value = expression.value.accept(this, data)
|
||||||
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
||||||
return none()
|
// Assignments can be used as expressions, so return a value. Redundant pushes
|
||||||
|
// will be eliminated by the peephole optimizer.
|
||||||
|
putUnitInstance(mv)
|
||||||
|
return onStack(AsmTypes.UNIT_TYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): StackValue {
|
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): StackValue {
|
||||||
@@ -681,57 +683,46 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
||||||
expression.markLineNumber(startOffset = true)
|
expression.markLineNumber(startOffset = true)
|
||||||
val switch = SwitchGenerator(expression, data, this).generate()
|
SwitchGenerator(expression, data, this).generate()?.let { return it }
|
||||||
return switch ?: genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List<IrBranch>): StackValue {
|
val type = expression.type.toKotlinType()
|
||||||
// True or false conditions known at compile time need not be generated.
|
|
||||||
val shouldGenerateCondition = !branch.condition.isFalseConst() && !branch.condition.isTrueConst()
|
|
||||||
// Body of an always-false-condition need not be generated.
|
|
||||||
val shouldGenerateBody = !branch.condition.isFalseConst()
|
|
||||||
// Don't generate the tail if it doesn't exist or isn't reachable.
|
|
||||||
val shouldGenerateTail = !otherBranches.isEmpty() && !branch.condition.isTrueConst()
|
|
||||||
|
|
||||||
val elseLabel = Label()
|
|
||||||
val endLabel = Label()
|
val endLabel = Label()
|
||||||
|
var exhaustive = false
|
||||||
if (shouldGenerateCondition) {
|
for (branch in expression.branches) {
|
||||||
genConditionalJumpWithOptimizationsIfPossible(branch.condition, data, elseLabel)
|
val elseLabel = Label()
|
||||||
} else {
|
if (branch.condition.isFalseConst() || branch.condition.isTrueConst()) {
|
||||||
// Even when a condition isn't generated, a linenumber and nop is still required so that a debugger can break on the line of the
|
// True or false conditions known at compile time need not be generated. A linenumber and nop are still required
|
||||||
// condition, except for the explicit "else".
|
// for a debugger to break on the line of the condition.
|
||||||
if (branch !is IrElseBranch) {
|
if (branch !is IrElseBranch) {
|
||||||
branch.condition.markLineNumber(startOffset = true)
|
branch.condition.markLineNumber(startOffset = true)
|
||||||
mv.nop()
|
mv.nop()
|
||||||
|
}
|
||||||
|
if (branch.condition.isFalseConst())
|
||||||
|
continue // The branch body is dead code.
|
||||||
|
} else {
|
||||||
|
genConditionalJumpWithOptimizationsIfPossible(branch.condition, data, elseLabel)
|
||||||
}
|
}
|
||||||
}
|
gen(branch.result, data).let {
|
||||||
|
coerceNotToUnit(it.type, it.kotlinType, type)
|
||||||
val resultFromBody = if (shouldGenerateBody) {
|
}
|
||||||
val thenBranch = branch.result
|
if (branch.condition.isTrueConst()) {
|
||||||
val result = thenBranch.run {
|
exhaustive = true
|
||||||
val stackValue = gen(this, data)
|
break // The rest of the expression is dead code.
|
||||||
coerceNotToUnit(stackValue.type, stackValue.kotlinType, type)
|
|
||||||
}
|
}
|
||||||
mv.goTo(endLabel)
|
mv.goTo(endLabel)
|
||||||
mv.mark(elseLabel)
|
mv.mark(elseLabel)
|
||||||
result
|
|
||||||
} else {
|
|
||||||
none()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val resultFromTail = if (shouldGenerateTail) {
|
if (!exhaustive) {
|
||||||
val nextBranch = otherBranches.first()
|
// TODO: make all non-exhaustive `if`/`when` return Nothing.
|
||||||
genIfWithBranches(nextBranch, data, type, otherBranches.drop(1))
|
if (type.isUnit())
|
||||||
} else {
|
putUnitInstance(mv)
|
||||||
none()
|
else if (!type.isNothing())
|
||||||
|
throw AssertionError("non-exhaustive `if`/`when` wants to return $type")
|
||||||
}
|
}
|
||||||
|
|
||||||
// endLabel is only used to jump from end-of-then-body to the end of the whole if cascade.
|
mv.mark(endLabel)
|
||||||
if (shouldGenerateBody)
|
return if (type.isNothing()) none() else expression.onStack
|
||||||
mv.mark(endLabel)
|
|
||||||
|
|
||||||
return if (shouldGenerateBody) resultFromBody else resultFromTail
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun genConditionalJumpWithOptimizationsIfPossible(
|
private fun genConditionalJumpWithOptimizationsIfPossible(
|
||||||
@@ -814,8 +805,7 @@ class ExpressionCodegen(
|
|||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||||
val result = expression.argument.accept(this, data)
|
val result = expression.argument.accept(this, data)
|
||||||
expression.argument.markEndOfStatementIfNeeded()
|
expression.argument.markEndOfStatementIfNeeded()
|
||||||
coerce(result.type, Type.VOID_TYPE, mv)
|
return result.discard()
|
||||||
return none()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTypeOperator.IMPLICIT_CAST -> {
|
IrTypeOperator.IMPLICIT_CAST -> {
|
||||||
@@ -931,8 +921,8 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
with(LoopInfo(loop, continueLabel, endLabel)) {
|
with(LoopInfo(loop, continueLabel, endLabel)) {
|
||||||
data.addInfo(this)
|
data.addInfo(this)
|
||||||
loop.body?.apply {
|
loop.body?.let {
|
||||||
gen(this, data)
|
gen(it, data).discard()
|
||||||
}
|
}
|
||||||
data.removeInfo(this)
|
data.removeInfo(this)
|
||||||
}
|
}
|
||||||
@@ -991,8 +981,8 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
with(LoopInfo(loop, continueLabel, endLabel)) {
|
with(LoopInfo(loop, continueLabel, endLabel)) {
|
||||||
data.addInfo(this)
|
data.addInfo(this)
|
||||||
loop.body?.apply {
|
loop.body?.let {
|
||||||
gen(this, data)
|
gen(it, data).discard()
|
||||||
}
|
}
|
||||||
data.removeInfo(this)
|
data.removeInfo(this)
|
||||||
}
|
}
|
||||||
@@ -1229,7 +1219,8 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
internal fun coerceNotToUnit(fromType: Type, fromKotlinType: KotlinType?, toKotlinType: KotlinType): StackValue {
|
internal fun coerceNotToUnit(fromType: Type, fromKotlinType: KotlinType?, toKotlinType: KotlinType): StackValue {
|
||||||
val asmToType = toKotlinType.asmType
|
val asmToType = toKotlinType.asmType
|
||||||
if (asmToType != AsmTypes.UNIT_TYPE || TypeUtils.isNullableType(toKotlinType)) {
|
// A void should still be materialized as a Unit to avoid stack depth mismatches.
|
||||||
|
if (asmToType != AsmTypes.UNIT_TYPE || fromType == Type.VOID_TYPE || TypeUtils.isNullableType(toKotlinType)) {
|
||||||
coerce(fromType, fromKotlinType, asmToType, toKotlinType, mv)
|
coerce(fromType, fromKotlinType, asmToType, toKotlinType, mv)
|
||||||
return onStack(asmToType, toKotlinType)
|
return onStack(asmToType, toKotlinType)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-20
@@ -28,8 +28,6 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
|
|
||||||
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
||||||
fun generate(): StackValue? {
|
fun generate(): StackValue? {
|
||||||
val endLabel = Label()
|
|
||||||
var defaultLabel = endLabel
|
|
||||||
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
||||||
var elseExpression: IrExpression? = null
|
var elseExpression: IrExpression? = null
|
||||||
val callToLabels = ArrayList<CallToLabel>()
|
val callToLabels = ArrayList<CallToLabel>()
|
||||||
@@ -38,7 +36,6 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
for (branch in expression.branches) {
|
for (branch in expression.branches) {
|
||||||
if (branch is IrElseBranch) {
|
if (branch is IrElseBranch) {
|
||||||
elseExpression = branch.result
|
elseExpression = branch.result
|
||||||
defaultLabel = Label()
|
|
||||||
} else {
|
} else {
|
||||||
val conditions = matchConditions(branch.condition) ?: return null
|
val conditions = matchConditions(branch.condition) ?: return null
|
||||||
val thenLabel = Label()
|
val thenLabel = Label()
|
||||||
@@ -73,8 +70,6 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
areConstIntComparisons(calls) ->
|
areConstIntComparisons(calls) ->
|
||||||
IntSwitch(
|
IntSwitch(
|
||||||
subject,
|
subject,
|
||||||
defaultLabel,
|
|
||||||
endLabel,
|
|
||||||
elseExpression,
|
elseExpression,
|
||||||
expressionToLabels,
|
expressionToLabels,
|
||||||
cases
|
cases
|
||||||
@@ -82,8 +77,6 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
areConstStringComparisons(calls) ->
|
areConstStringComparisons(calls) ->
|
||||||
StringSwitch(
|
StringSwitch(
|
||||||
subject,
|
subject,
|
||||||
defaultLabel,
|
|
||||||
endLabel,
|
|
||||||
elseExpression,
|
elseExpression,
|
||||||
expressionToLabels,
|
expressionToLabels,
|
||||||
cases
|
cases
|
||||||
@@ -197,11 +190,12 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
|
|
||||||
abstract inner class Switch(
|
abstract inner class Switch(
|
||||||
val subject: IrGetValue,
|
val subject: IrGetValue,
|
||||||
val defaultLabel: Label,
|
|
||||||
val endLabel: Label,
|
|
||||||
val elseExpression: IrExpression?,
|
val elseExpression: IrExpression?,
|
||||||
val expressionToLabels: ArrayList<ExpressionToLabel>
|
val expressionToLabels: ArrayList<ExpressionToLabel>
|
||||||
) {
|
) {
|
||||||
|
protected val endLabel = Label()
|
||||||
|
protected val defaultLabel = Label()
|
||||||
|
|
||||||
open fun shouldOptimize() = false
|
open fun shouldOptimize() = false
|
||||||
|
|
||||||
open fun genOptimizedIfEnoughCases(): StackValue? {
|
open fun genOptimizedIfEnoughCases(): StackValue? {
|
||||||
@@ -251,14 +245,13 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun genElseExpression(): StackValue {
|
protected fun genElseExpression(): StackValue {
|
||||||
|
mv.visitLabel(defaultLabel)
|
||||||
return if (elseExpression == null) {
|
return if (elseExpression == null) {
|
||||||
// There's no else part. No stack value will be generated.
|
// There's no else part. Generate Unit if needed.
|
||||||
StackValue.putUnitInstance(mv)
|
coerceNotToUnit(Type.VOID_TYPE, null, expression.type.toKotlinType())
|
||||||
onStack(Type.VOID_TYPE)
|
|
||||||
} else {
|
} else {
|
||||||
// Generate the else part.
|
// Generate the else part.
|
||||||
mv.visitLabel(defaultLabel)
|
val stackValue = gen(elseExpression, data)
|
||||||
val stackValue = elseExpression.run { gen(this, data) }
|
|
||||||
coerceNotToUnit(stackValue.type, stackValue.kotlinType, expression.type.toKotlinType())
|
coerceNotToUnit(stackValue.type, stackValue.kotlinType, expression.type.toKotlinType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,12 +259,10 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
|
|
||||||
inner class IntSwitch(
|
inner class IntSwitch(
|
||||||
subject: IrGetValue,
|
subject: IrGetValue,
|
||||||
defaultLabel: Label,
|
|
||||||
endLabel: Label,
|
|
||||||
elseExpression: IrExpression?,
|
elseExpression: IrExpression?,
|
||||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||||
private val cases: List<ValueToLabel>
|
private val cases: List<ValueToLabel>
|
||||||
) : Switch(subject, defaultLabel, endLabel, elseExpression, expressionToLabels) {
|
) : Switch(subject, elseExpression, expressionToLabels) {
|
||||||
|
|
||||||
// IF is more compact when there are only 1 or fewer branches, in addition to else.
|
// IF is more compact when there are only 1 or fewer branches, in addition to else.
|
||||||
override fun shouldOptimize() = cases.size > 1
|
override fun shouldOptimize() = cases.size > 1
|
||||||
@@ -326,12 +317,10 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
|
|
||||||
inner class StringSwitch(
|
inner class StringSwitch(
|
||||||
subject: IrGetValue,
|
subject: IrGetValue,
|
||||||
defaultLabel: Label,
|
|
||||||
endLabel: Label,
|
|
||||||
elseExpression: IrExpression?,
|
elseExpression: IrExpression?,
|
||||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||||
private val cases: List<ValueToLabel>
|
private val cases: List<ValueToLabel>
|
||||||
) : Switch(subject, defaultLabel, endLabel, elseExpression, expressionToLabels) {
|
) : Switch(subject, elseExpression, expressionToLabels) {
|
||||||
|
|
||||||
private val hashToStringAndExprLabels = HashMap<Int, ArrayList<ValueToLabel>>()
|
private val hashToStringAndExprLabels = HashMap<Int, ArrayList<ValueToLabel>>()
|
||||||
private val hashAndSwitchLabels = ArrayList<ValueToLabel>()
|
private val hashAndSwitchLabels = ArrayList<ValueToLabel>()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// Even before any IR lowerings, the type of `when` is determined to be
|
||||||
|
// Unit even though the outer `if` still returns `Int?`. This results
|
||||||
|
// in a ClassCastException when that Unit is converted into a Number.
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
fun test(
|
fun test(
|
||||||
b: Boolean,
|
b: Boolean,
|
||||||
i: Int
|
i: Int
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
interface Callable {
|
interface Callable {
|
||||||
fun call(b: Boolean)
|
fun call(b: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
class A(val p: String) {
|
class A(val p: String) {
|
||||||
val prop: String = throw RuntimeException()
|
val prop: String = throw RuntimeException()
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// CHECK_CASES_COUNT: function=test1 count=2
|
// CHECK_CASES_COUNT: function=test1 count=2
|
||||||
// CHECK_IF_COUNT: function=test1 count=0
|
// CHECK_IF_COUNT: function=test1 count=0
|
||||||
// CHECK_BREAKS_COUNT: function=test1 count=1
|
// CHECK_BREAKS_COUNT: function=test1 count=1
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// NO_CHECK_LAMBDA_INLINING
|
// NO_CHECK_LAMBDA_INLINING
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
package test
|
package test
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
package test
|
package test
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
package test
|
package test
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// PROPERTY_NOT_USED: p1
|
// PROPERTY_NOT_USED: p1
|
||||||
// PROPERTY_NOT_READ_FROM: p2
|
// PROPERTY_NOT_READ_FROM: p2
|
||||||
// PROPERTY_NOT_WRITTEN_TO: p3
|
// PROPERTY_NOT_WRITTEN_TO: p3
|
||||||
|
|||||||
@@ -18,3 +18,6 @@ inline fun inlineCall(predicate: (String?) -> Boolean): Boolean {
|
|||||||
// 0 LINENUMBER 7
|
// 0 LINENUMBER 7
|
||||||
// 0 LINENUMBER 8
|
// 0 LINENUMBER 8
|
||||||
// 1 LINENUMBER 9
|
// 1 LINENUMBER 9
|
||||||
|
|
||||||
|
// Not actually inlined, so there is a LINENUMBER 7 because the if's body is not considered dead.
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun z() {}
|
fun z() {}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ fun foo(x: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 3 4 5 6 +8 9 10 11 8 13
|
// 2 3 4 5 6 +8 9 10 11 8 13
|
||||||
|
|
||||||
|
// JVM_IR also generates a LINENUMBER 12, which seems consistent with the fact that
|
||||||
|
// there is a LINENUMBER 6, but still fails the test.
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
|
|||||||
Reference in New Issue
Block a user