Skip to content

Use hygienic names for the generated call's where-parameters - #158

Merged
ChrisRackauckas merged 2 commits into
SciML:masterfrom
ChrisRackauckas-Claude:fix-34-id-hygiene
Sep 26, 2026
Merged

ChrisRackauckas merged 2 commits into
SciML:masterfrom
ChrisRackauckas-Claude:fix-34-id-hygiene

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

init(mod) evals a @generated generated_callfunc method into the user's module whose where-parameters were named argnames, cache_tag, and id. Those bindings are in scope inside the generated body, so a user body referencing a global named id silently received the function's internal NTuple{5,UInt32} id instead of the module global (or an UndefVarError). The parameters are renamed to ˍ₋rgf₋argnames, ˍ₋rgf₋cache_tag, and ˍ₋rgf₋id, in the same hygienic style as the existing ˍ₋rgf₋args splat; init's mod.eval is the only place a generated_callfunc method is defined. A regression test checks that id/argnames/cache_tag resolve to context-module globals when defined and throw UndefVarError when not. The same collision also broke arguments with those names — on the parent, :((id,) -> id + 1) fails to compile with syntax: local variable name "id" conflicts with a static parameter — so the testset covers the argument-name cases too.

Fixes #34.

Test evidence

The new testset was run verbatim against the parent src (ddf352d) and with the fix applied:

Unfixed:

Internal binding names stay hygienic: Error During Test
  Expression: InternalNameGlobals.f_id(2) == 42
  MethodError: no method matching +(::NTuple{5, UInt32}, ::Int64)
...
Internal binding names stay hygienic: Test Failed
  Expression: InternalNameUndefGlobals.f_id(2)
    Expected: UndefVarError
  No exception thrown
Internal binding names stay hygienic: Error During Test
  Expression: @RuntimeGeneratedFunction(:((id,) -> id + 1))(2) == 3
  syntax: local variable name "id" conflicts with a static parameter
Test Summary:                        | Fail  Error  Total  Time
Internal binding names stay hygienic |    3      6      9

Fixed:

Test Summary:                        | Pass  Total  Time
Internal binding names stay hygienic |    9      9  0.4s

The original reproduction :(x -> id) now throws UndefVarError(:id, ..., Main) instead of returning (0x7596…, …).

Groups run locally (Julia 1.12.4)

  • GROUP=Core julia --project -e 'using Pkg; Pkg.test()' — passed. Tail:
    Test Summary:                        | Pass  Total  Time
    Internal binding names stay hygienic |    9      9  0.2s
    PrecompileTools 1.2 workload         |    1      1  12.6s
    Embedded objects are identified by type and content |   10     10  0.3s
    Equal expressions share one compiled body           |   13     13  3.2s
         Testing RuntimeGeneratedFunctions tests passed
    
  • GROUP=QA julia --project -e 'using Pkg; Pkg.test()' — passed: QA | 20 20 51.4s.
  • julia --project=@runic -m Runic --inplace on both changed files; typos clean.

Not verified: julia lts (1.10) and pre, non-Linux platforms, and the 32-bit lane — CI covers those. A user argument named ˍ₋rgf₋args could in principle still collide with the splat name, but that is the pre-existing hygiene convention and not introduced here.

Reviewer pushback welcome on: whether generated_callfunc_body's own parameter names should also be hygienic for consistency (they are internal and not in scope of the generated body), and whether the testset placement next to the other globals tests in core_tests.jl is right.

Please ignore this draft until reviewed by @ChrisRackauckas.

Risk assessment

  • Risk: low
  • Blast radius: the static-parameter names of the @generated generated_callfunc that init(mod) evals into each user module (argnames, cache_tag and id become ˍ₋rgf₋*). The method signature type is unchanged and there is no public API change; precompile caches invalidate on a version bump as usual.
  • Evidence: the new testset fails on the parent src (3 fail, 3 error) and passes on the PR (6/6). The issue's :(x -> id) now throws UndefVarError instead of returning the internal id. Symbolics 7.41.1 build_function smoke tests pass (scalar, out-of-place, in-place). The Core and QA groups pass locally.
  • Independent review: Claude Code (Opus 5.5) rated it low with high confidence: a hygienic rename only, discrimination demonstrated, downstream smoke test OK. Verdict MERGE. It suggested adding a test for argument names like id (which previously failed with a syntax error) and a shorter comment; both were sent back to the author.
  • Merge: auto-merge candidate once CI is green. Not verified locally: the 1.10 LTS lane and ModelingToolkit downstream.

🤖 Generated with Devin CLI 3000.11.3 (model: swe-2-high), transcript /home/crackauc/sandbox/goals/issue-backlog/jobs/fix-RuntimeGeneratedFunctions.jl-34/log.txt on amdci2.julia.csail.mit.edu

Risk assessment and review coordination by 🤖 Claude Code (model: claude-opus-5-5[1m]), https://claude.ai/code/session_01NdFVhANTF4nepTbqYxjfRP

ChrisRackauckas and others added 2 commits September 26, 2026 03:57
The `where`-parameters `argnames`, `cache_tag`, and `id` of the
`generated_callfunc` method that `init` evals into the user's module are
in scope inside the generated body, so a user global named `id` resolved
to the function's internal id tuple instead of the module global.
Rename them in the `ˍ₋rgf₋args` style so such globals resolve in the
context module's scope, or throw UndefVarError when undefined.

Fixes SciML#34.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: swe-2-high
Agent-Session: local session, transcript at /home/crackauc/sandbox/goals/issue-backlog/jobs/fix-RuntimeGeneratedFunctions.jl-34/log.txt on amdci2.julia.csail.mit.edu
Arguments named `id`, `argnames`, or `cache_tag` hit the same collision
as globals did: on the parent, `:((id,) -> id + 1)` fails to compile
with `syntax: local variable name "id" conflicts with a static
parameter`. Cover those cases in the hygiene testset and condense the
explanatory comment to one line.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: swe-2-high
Agent-Session: local session, transcript at /home/crackauc/sandbox/goals/issue-backlog/jobs/fix-RuntimeGeneratedFunctions.jl-34/log.txt on amdci2.julia.csail.mit.edu
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review September 26, 2026 09:32
@ChrisRackauckas
ChrisRackauckas merged commit fa8816d into SciML:master Sep 26, 2026
19 checks passed
ChrisRackauckas referenced this pull request Sep 26, 2026
Agent-Harness: Claude Code
Agent-Model: claude-opus-5-5[1m]
Agent-Session: local Claude Code session 3cd6500a-1f81-46b5-ac0b-c466e15b6a53 on Chris's Mac (session ID, no URL)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

id variable is set to a NTuple of UInt inside of the function body

2 participants