Skip to content

import: module TOP-LEVEL assignment binds through to a same-named importer global (module state hijacked) #589

Description

@InauguralPhysicist

Found building EigenOS M13-2 (the .eigs protocol tower): a hosted twin harness defined sent is [] (a frame-capture list), then import icmp — a module whose top level declares sent is 0 as its own counter. The module's top-level assignment did NOT create module state: it rebound the IMPORTER's sent, and every later use of sent inside the module's functions resolved to the importer's variable.

Minimal repro (current main, aa8638a):

# cmod.eigs
counter is 0
define bump() as:
    counter is counter + 1
    return counter
# main.eigs
counter is [9, 9, 9]
import cmod
print of (type of counter)      # num  <- importer's list clobbered by module init
print of (cmod.bump of [])      # 1   <- module state lives in the importer's scope
print of (type of counter)      # num

Expected: a module body executes in its own scope; counter is 0 at module top level creates the module's binding regardless of what the importer's scope holds. SPEC's "Module write boundary" fixed module FUNCTIONS (#373: a function's bare write can never bind through), and its parenthetical "(Top-level statements in the loaded file still execute directly in the current scope.)" is documented for load_file — but the same leak through import breaks module isolation: whether a module's state is module-scoped depends on the importer's incidental variable names. With common names (sent, count, state) this silently cross-wires two unrelated states; the failure I hit surfaced as cannot apply '+' to list and num two layers away, and the quiet variant (both numbers) would be silently wrong instead.

Suggested fix: module top-level bare assignment should bind in the module scope (never walk past the module boundary), mirroring the #373 rule one level up. load_file keeps its documented current-scope semantics.

Workaround used in EigenOS meanwhile: collision-proof harness names (t_sent, ...).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions