Skip to content

Commit 2e814a7

Browse files
Kotlin: anchor synthetic exhaustive-when throw to the when
An exhaustive `when` expression (one with no explicit `else` that the compiler proves total) gets a synthetic `throw NoWhenBranchMatchedException(...)` as its fallback branch. The two frontends locate that synthetic call differently: * K2 gives the call the source offsets of the enclosing `when` expression, so the throw (and the `new NoWhenBranchMatchedException`) are located at the `when` block, e.g. `6:3:9:3`. * K1 leaves the synthetic call with undefined offsets, so the extractor emits a `0:0:0:0` (no-source) location for both nodes. A real source location is strictly more useful than none, and anchoring the implicit fallback to the `when` it belongs to is the intuitive choice, so we adopt the K2 behaviour for both frontends. `extractCall` now records the enclosing `when`'s location while extracting its branches (`currentSyntheticWhenLocation`) and uses it as the fallback for the `noWhenBranchMatchedException` builtin whenever the synthetic call itself has undefined offsets. This only changes K1: under K2 the call already carries valid offsets, so `tw.getLocation(c)` is used unchanged and K2 output is byte-identical. Relearned both suites: all 3333 tests pass and the only changed row is test-kotlin1/library-tests/no-when-branch-found, which now matches test-kotlin2 exactly (`0:0:0:0` -> `6:3:9:3`). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent edaa710 commit 2e814a7

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ open class KotlinFileExtractor(
9696
// argument of that property's forwarding get/set, which under K1 gets a bogus location.
9797
private var currentLocalDelegatedProperty: IrLocalDelegatedProperty? = null
9898

99+
// Set only while extracting the branches of an `IrWhen`. Holds that `when`'s source
100+
// location, used as a fallback location for the compiler-generated
101+
// `throw NoWhenBranchMatchedException(...)` of an exhaustive `when`. Under K2 that
102+
// synthetic call carries the `when`'s offsets, but under K1 it has undefined offsets
103+
// (yielding a `0:0:0:0` location); this makes both frontends anchor it to the `when`.
104+
private var currentSyntheticWhenLocation: Label<DbLocation>? = null
105+
99106
private inline fun <T> with(kind: String, element: IrElement, f: () -> T): T {
100107
val name =
101108
when (element) {
@@ -5042,7 +5049,12 @@ open class KotlinFileExtractor(
50425049
}
50435050
isBuiltinCallInternal(c, "noWhenBranchMatchedException") -> {
50445051
kotlinNoWhenBranchMatchedConstructor?.let {
5045-
val locId = tw.getLocation(c)
5052+
// Under K1 this synthetic call has undefined offsets (a `0:0:0:0`
5053+
// location); fall back to the enclosing `when`'s location, matching K2.
5054+
val locId =
5055+
if (c.startOffset == UNDEFINED_OFFSET || c.endOffset == UNDEFINED_OFFSET)
5056+
currentSyntheticWhenLocation ?: tw.getLocation(c)
5057+
else tw.getLocation(c)
50465058
val thrownType = useSimpleTypeClass(it.parentAsClass, listOf(), false)
50475059
val stmtParent = stmtExprParent.stmt(c, callable)
50485060
val throwId = tw.getFreshIdLabel<DbThrowstmt>()
@@ -6834,7 +6846,13 @@ open class KotlinFileExtractor(
68346846
tw.writeStmts_whenbranch(bId, id, i, callable)
68356847
tw.writeHasLocation(bId, bLocId)
68366848
extractExpressionExpr(b.condition, callable, bId, 0, bId)
6837-
extractExpressionStmt(b.result, callable, bId, 1)
6849+
val prevSyntheticWhenLocation = currentSyntheticWhenLocation
6850+
currentSyntheticWhenLocation = locId
6851+
try {
6852+
extractExpressionStmt(b.result, callable, bId, 1)
6853+
} finally {
6854+
currentSyntheticWhenLocation = prevSyntheticWhenLocation
6855+
}
68386856
if (b is IrElseBranch) {
68396857
tw.writeWhen_branch_else(bId)
68406858
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
| test.kt:0:0:0:0 | throw ... | test.kt:0:0:0:0 | new NoWhenBranchMatchedException(...) |
1+
| test.kt:6:3:9:3 | throw ... | test.kt:6:3:9:3 | new NoWhenBranchMatchedException(...) |

0 commit comments

Comments
 (0)