Skip to content

require: cached module result lost after deep require chain (assert:register nil) #244

@davydog187

Description

@davydog187

Summary

After requiring a module whose body issues several nested requires and defines many local functions that close over the required value, the local binding ends up with a value that lacks the required module's fields. The result behaves as if package.loaded[modname] was read while still holding the true sentinel that parse_and_execute_module writes before executing the module body.

Discovered while bumping lua to 9344c7cc9835dfa15f1240e6a9de5c9e102b146a (which includes the ? → modname dot-to-slash fix) in tv-labs/platform/sidecar, which ships luassert under priv/lua/.

Repro

From tv-labs/platform/sidecar on dave/sidecar-bump-lua-1.0.0-rc.0 with mix.exs pinned to commit 9344c7cc9835dfa15f1240e6a9de5c9e102b146a:

priv = :code.priv_dir(:sidecar)
paths = "#{priv}/lua/?.lua;#{priv}/lua/?/init.lua"

lua = Lua.new(exclude: [[:require], [:package]])
lua = Lua.set_lua_paths(lua, paths)

Lua.eval!(lua, "require('luassert.assertions'); return 'ok'")

Errors with:

Lua runtime error: Runtime Type Error
  at -no-source-:307:
  attempt to call a nil value (method 'register' on local 'assert')

luassert/assertions.lua:307 is the first assert:register(...) after local assert = require('luassert.assert') at line 9.

Observations

  • require('luassert.modifiers') works. Same shape (local assert = require('luassert.assert') then assert:register(...)) but only 19 lines, and the assert:register calls are right after the require — no intermediate local function defs that close over assert.
  • require('luassert.assertions') fails at line 307.
  • require('luassert.array') fails at line 66.
  • require('luassert.spy') fails at line 182.

All three failing modules have the same pattern: local assert = require('luassert.assert') at the top, ~50+ local function definitions in between (whose bodies do assert(...) via the metatable's __call), then the first assert:register(...) is what trips.

At the failing line, assert is non-nil but assert.register is nil. Reference Lua returns the same obj table both times. The value the local ends up holding looks like the true sentinel that parse_and_execute_module writes to package.loaded[modname] before executing the module body — as if the post-execution overwrite at line 724 isn't seen by the outer state.

Related (also fixed in this branch)

  • Original blocker: find_module_file (lib/lua/vm/stdlib.ex:754) didn't convert . to / in modname before substituting into the pattern. Fixed in 9344c7c. This new bug is separate.

Sidecar bump tracking

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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