Summary
The result of ?. optional chaining type-checks and compares correctly when its type is inferred, but does not unify with an explicitly written Option[T] annotation — type mismatch in binding. The chained Option appears to be a structurally-equal-but-distinct type from the canonical Option[T].
Reproduction
type A { city: Option[str] }
type P { address: Option[A] }
// OK — inferred, and compares to Some(...)
fn inferred(p: P):
let c = p.address?.city
assert(c == Some("x"))
// ERR — same expression, with the annotation it should have
fn annotated(p: P):
let c: Option[str] = p.address?.city // error: type mismatch in binding
let _ = c
error: type mismatch in binding
--> x.w:9:5
let c: Option[str] = p.address?.city
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Notes
- The §10.3 spec test (
test/spec/spec_ss10_3_optional_chaining.w) passes because it uses the inferred form (let city = profile.address?.city; assert(city == Some(...))). Adding the matching annotation breaks it.
- This is the same shape as the canonicalization gap seen with compiler-backed
HashMap.get returning Option[enum] vs a written Option[enum]: the synthesized Option type isn't the same TypeId as the annotated one.
- A related observation: with an
Option-typed head (find_user(id)?.address?.city where find_user -> Option[User]) the result is mis-typed entirely (a later let _ = even warns about cancelling a Task) — likely the same non-canonical-Option root cause, worth checking together.
Found while updating docs/with-idiomatic-guide.md.
Env: with v0.13.1-g7104bb5d7 (origin/main 9865cbb), darwin-aarch64.
Summary
The result of
?.optional chaining type-checks and compares correctly when its type is inferred, but does not unify with an explicitly writtenOption[T]annotation —type mismatch in binding. The chained Option appears to be a structurally-equal-but-distinct type from the canonicalOption[T].Reproduction
Notes
test/spec/spec_ss10_3_optional_chaining.w) passes because it uses the inferred form (let city = profile.address?.city; assert(city == Some(...))). Adding the matching annotation breaks it.HashMap.getreturningOption[enum]vs a writtenOption[enum]: the synthesized Option type isn't the sameTypeIdas the annotated one.Option-typed head (find_user(id)?.address?.citywherefind_user -> Option[User]) the result is mis-typed entirely (a laterlet _ =even warns about cancelling aTask) — likely the same non-canonical-Option root cause, worth checking together.Found while updating docs/with-idiomatic-guide.md.
Env:
with v0.13.1-g7104bb5d7(origin/main 9865cbb), darwin-aarch64.