Skip to content

Nested same-type State<T> handlers share a single global state cell #417

@aallan

Description

@aallan

Problem

Two independent handle[State<Int>] handlers in nested scope both read/write the same global WASM cell ($vera.state_get_Int / $vera.state_put_Int), so they do not have independent state. The inner handler overwrites the outer handler's state.

-- Expected: outer state = 10, inner state = 99 (independent)
-- Actual:   inner handler corrupts outer state
let @Int = handle[State<Int>](10) {
  let @Int = handle[State<Int>](99) {
    State.put(42)    -- should only affect inner handler
  };
  State.get()        -- returns 42, not 10
};

Root Cause

The current State effect implementation maps each State<T> operation to a single global WASM mutable global ($vera.state_Int). Nested handlers of the same type overwrite this shared cell rather than maintaining a separate cell per handler scope.

A correct implementation requires either:

  • A handler stack per type (push on enter, pop on exit), or
  • CPS/continuation-passing transformation of handlers so each handler closure captures its own cell

Workaround

Use different state types for nested handlers (e.g., State<Int> + State<Bool>), or restructure code to use sequential (non-nested) handlers of the same type.

Impact

Any program using nested handle[State<T>] of the same type produces incorrect results. This was discovered while writing tests/conformance/ch07_nested_handlers.vera, which avoids the pattern for this reason.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions