? operator only guards the head of an access chain; nested required access never errors #19

Open
opened 2026-08-07 13:33:02 +02:00 by Hemera · 0 comments
Owner

The README contract says: "Per default, trying to access a value that is not defined will cause an error. This can be changed with the ? operator." and for {{= user?.profile.description? }}: "This will give an error if user.profile is not defined."

That contract only holds for the first identifier of a chain. Object-key lookups (IndexSlotToSlot, src/eval/mod.rs:291) never fail — a missing key silently becomes Undefined and renders as empty string, ? or not.

Verified behaviour:

  • {{= user.profile }} with context { "user": {} } → renders "", no error, even though there is no ?.
  • {{= user?.profile.description? }} with user present but profile missing → renders "". Per the README this must be an error ("if there is a user, it must have a profile value").

Root cause: FailIfUndefined (src/eval/mod.rs:117) is never emitted. The only emit site (src/compiler/mod.rs:621) is inside an AccessOperation arm of inner_access_op that the parser never constructs (a ./? infix rhs is always a plain operand, never an AccessOperation), so it is dead code. ? beyond the first dot is effectively a no-op for enforcement.

Minimal fix: in inner_access_op, the VariableAccess arm (src/compiler/mod.rs:591-598) should emit FailIfUndefined after its IndexSlotToSlot. This yields the documented semantics: a.b, a?.b, a.b.c all error on a missing key; a?.b only skips when a itself is missing.

The README contract says: *"Per default, trying to access a value that is not defined will cause an error. This can be changed with the `?` operator."* and for `{{= user?.profile.description? }}`: *"This will give an error if `user.profile` is not defined."* That contract only holds for the **first identifier** of a chain. Object-key lookups (`IndexSlotToSlot`, `src/eval/mod.rs:291`) never fail — a missing key silently becomes `Undefined` and renders as empty string, `?` or not. Verified behaviour: - `{{= user.profile }}` with context `{ "user": {} }` → renders `""`, **no error**, even though there is no `?`. - `{{= user?.profile.description? }}` with `user` present but `profile` missing → renders `""`. Per the README this must be an error ("if there _is_ a user, it must have a profile value"). Root cause: `FailIfUndefined` (`src/eval/mod.rs:117`) is never emitted. The only emit site (`src/compiler/mod.rs:621`) is inside an `AccessOperation` arm of `inner_access_op` that the parser never constructs (a `.`/`?` infix rhs is always a plain operand, never an `AccessOperation`), so it is dead code. `?` beyond the first dot is effectively a no-op for enforcement. Minimal fix: in `inner_access_op`, the `VariableAccess` arm (`src/compiler/mod.rs:591-598`) should emit `FailIfUndefined` after its `IndexSlotToSlot`. This yields the documented semantics: `a.b`, `a?.b`, `a.b.c` all error on a missing key; `a?.b` only skips when `a` itself is missing.
Sign in to join this conversation.
No labels
automated-🤖
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Hemera/nomo#19
No description provided.