Home > database >  throw statement in Kotlin
throw statement in Kotlin

Time:12-10

in Kotlin expressions like

fun main() {
    throw throw throw RuntimeException("boom")
}

or

fun main() {
    throw throw return
}

are syntactically correct

I understand ideas behind it, but I wonder why there is no Warn (at least in Itellij) when writing such nonsense.

CodePudding user response:

This is reported as a minor problem in KT-22621 "throw throw Exception()": False negative UNREACHABLE_CODE warning. It should be noted that unreachable code is warned if you put parentheses:

throw (throw Exception())
// or
throw (return)

Also, return throws Exception() gives the warning too.

Unfortunately, it is still an open issue after 4 years.

After reading Kotlin's source code for a bit, I think this is unintentional. Let's consider ControlFlowProcessor.kt, visitThrowExpression:

override fun visitThrowExpression(expression: KtThrowExpression) {
    mark(expression)

    generateJumpsToCatchAndFinally()

    val thrownExpression = expression.thrownExpression ?: return
    generateInstructions(thrownExpression)

    val thrownValue = builder.getBoundValue(thrownExpression) ?: return
    builder.throwException(expression, thrownValue)
}

Consider throw throw Exception(). When the first throw is visited, generateInstructions(thrownExpression) generates the part of the CFG that corresponds to throw Exception(). This causes the second throw to be visited, generating the correct CFG for throw Exception().

The problem comes, when builder.getValue(thrownExpression) gets called. This gets a PseudoValue from the CFG builder that represents the thrown value. However, (I suspect that) this returns nil and builder.throwException is not called. This is because when building the CFG for throw Exception(), no value is bound. Compare this to what happens in visitParenthesizedExpression, or visitCallExpression, but you'd need to trace deeper.

This doesn't affect control flow analysis too much, since the part of the CFG generated for throw Exception() is correct. The CFG does end up missing a node to represent the outer throw though, which is what builder.throwException would have added. This node would have been marked as unreachable and a warning been issued, but this node doesn't even exist, so no warnings.

  • Related