if x == null { return null } is not narrowed in the final return of an optional result (rustc: no field on &Option<T>)Open and answer this issue on GitHub.
ready · opened 2026-10-31 · updated 2026-10-31 · 0 comments
Found by the builder moving crates/nikaia/src/proofs.rs (known), probed by the architect on target/fast/nikaia at b45fbcc8.
What happens. An optional parameter narrowed by if held == null { return null } (ADR-350, ADR-382) is read as narrowed in every statement after the test (held.as_ref().unwrap().key), except in the function’s final return when the result is optional: there held.key / held.asked is lowered on the Option itself, std::convert::identity(held.key.to_string()), and rustc refuses *No field `key` on type `&Option
Probe (nikaia run p.nika; wanted k, true):
struct Asked { yes: bool }
struct Known { key: String, asked: Asked }
fn tail(held: Known?) -> String? {
if held == null {
return null
}
return held.key.clone()
}
fn known(held: Known?) -> Asked? {
if held == null {
return null
}
let k = held.key.clone()
return held.asked.clone()
}
fn main() {
println(tail(Known { key: "k", asked: Asked { yes: true } }) ?? "none")
let a = known(Known { key: "k", asked: Asked { yes: true } }) ?? Asked { yes: false }
println(a.yes)
}
Lowered today: let k = held.as_ref().unwrap().key.to_string(); then std::convert::identity(held.asked.clone()). Rows that run and must stay: the same with -> Asked (not optional) is not reachable; the ?? return null spelling; a narrowed read in a middle statement.
The builder also saw held.key lowered without & in this shape; the architect did not reproduce that part (a read in a middle statement lowers right): the builder re-probes it on the fix.
return (and a body’s last expression) of an optional result reads a narrowed name as narrowed, as the statements before it do, and wraps the answer (Some(...)), not the Option; both rows print.tests/compiler/tests/a_narrowed_optional_in_the_final_return.nika + .stdout, refused by rustc on the unfixed binary.Generated by Claude Code