Give a concrete example of a smart-cast or type-inference case that K2 handles better than the old compiler.
answer
- Consistency, not new syntax
- Smart cast through local val aliases
- Survives ||/&&/!/early-return guards
- Some unsound code now rejected
- Cause = unified FIR data-flow
basics
~20 sK2 tracks the types of values more carefully, so smart casts work in more places — for example after combining conditions with logical operators or across local variables — where the old compiler sometimes gave up.
solid answer
~50 sK2's unified FIR-based data-flow analysis tracks smart-cast information more precisely, so more correct casts are allowed and a few unsound ones are now rejected. Concrete wins: smart casts propagate through local val aliases (assign a nullable to a val, check that val, and the original is smart-cast too in cases the old compiler missed); smart casts survive across boolean combinations and inverted conditions (using `&&`, `||`, `!`, and early returns) more consistently; and smart casts on properties captured/checked across `is`/`!= null` branches behave uniformly. On the inference side, K2 improves builder-style inference and handling of `when` branch result types, and is stricter about genuinely ambiguous overloads. The right framing in an interview: K2 didn't add a new keyword — it made the existing `?.`, `is`, `!= null`, and smart-cast machinery behave consistently because all phases now read the same FIR data-flow graph.
code
kotlin · 10 linesfun firstChar(s: String?): Char? {
if (s == null || s.isEmpty()) return null
// smart cast to non-null String survives the combined guard
return s[0]
}
fun describe(x: Any?) {
val y = x
if (y is String) println(y.length) // y smart-cast to String
}go deeper
Recognizes smart casts and that K2 makes them work in more places.
Gives a concrete example (combined guards / val alias) and knows var/cross-module props aren't smart-castable.
Explains the cause (unified FIR data-flow) and that some unsound code is now correctly rejected with the right fix.
Plans a migration: audits newly-rejected sites, categorizes them (unsound cast vs ambiguous overload), and sets a remediation policy.
## Background terms - **Smart cast**: after you prove a value's type (e.g. `x is String` or `x != null`), Kotlin lets you use it *as* that type without an explicit cast. The compiler does this via **data-flow analysis** — tracking what is known about each value at each program point. - **Type inference**: the compiler computing types you didn't write (e.g. the result type of a `when`). K2 didn't introduce new syntax here. It made these *existing* mechanisms (`is`, `!= null`, `?.`, `as?`) behave **consistently**, because in K2 all phases read the same FIR data-flow graph instead of the old scattered structures. ## Example 1 — smart cast through a local val alias ```kotlin fun describe(x: Any?) { val y = x if (y is String) { // K2 reliably smart-casts y to String here println(y.length) } } ``` K2's data-flow tracking across simple aliases is more dependable; the old compiler had corner cases (especially with nullability and captured values) where it would refuse. ## Example 2 — smart casts across boolean logic and early return ```kotlin fun firstChar(s: String?): Char? { if (s == null || s.isEmpty()) return null // after the guard, s is smart-cast to non-null String return s[0] } ``` Combining conditions with `||`/`&&`, negation `!`, and early `return` and still keeping the smart cast on the following statements is handled uniformly in K2. ## Example 3 — `when` result-type inference ```kotlin fun pick(flag: Boolean): Number = when (flag) { true -> 1 // Int false -> 2L // Long } // inferred common supertype is consistent under K2 ``` K2 computes the common branch type more consistently. ## The flip side — newly rejected unsound code Because analysis is more correct, a handful of programs the old compiler *wrongly* allowed now fail to compile. Common categories: relying on a smart cast that wasn't actually sound (e.g. an `open`/`var` property that could change), or an overload that was genuinely **ambiguous** but happened to resolve before. These are surfaced bugs, not regressions; the fix is an explicit cast or disambiguation. ## What to say in an interview 1. K2 improves *consistency* of smart casts and inference, not new keywords. 2. The cause is the unified FIR data-flow model read by all phases. 3. Trade-off: a few previously-accepted unsound programs are now correctly rejected.
- Why might code that compiled on Kotlin 1.9 fail to compile on 2.0?K2 is more correct: it rejects unsound smart casts and genuinely ambiguous overloads the old compiler wrongly accepted. The fix is an explicit cast or disambiguation, not reverting K2.
- Does a smart cast work on a mutable `var` property from another module?No — it could change between the check and use, so it's not stably smart-castable; this is true in both compilers, and K2 enforces it consistently.
saying these in an interview costs you the question
- Claiming K2 added new smart-cast keywords or operators
- Saying smart casts now work on any mutable property
- Treating newly-rejected code as a K2 bug rather than a surfaced unsoundness
- Confusing smart cast with explicit `as` casting
- Asserting inference results changed arbitrarily rather than became consistent