Skip to content

Conversation

@robobun
Copy link
Collaborator

@robobun robobun commented Jan 27, 2026

Summary

  • Fix crash when macro returns object with circular reference
  • Add clear error message instead of segfault
  • Add regression tests for circular reference detection

Fixes #22552

Root Cause

When a macro returned an object with a circular reference, Bun would crash with a segmentation fault. This happened because:

  1. During JavaScript-to-AST conversion, the code would create placeholder AST nodes
  2. For circular references, the placeholder (with empty properties) would be returned
  3. Later AST traversal would infinitely recurse trying to visit the circular nodes

Solution

Use a sentinel value (Expr.empty with e_missing data) to mark objects being processed:

  • Before processing an object/array, store the sentinel in the visited map
  • When encountering a value already being processed (sentinel detected), emit a clear error
  • Move expression creation to after all children are processed, then store the final result

This prevents circular AST nodes from being created and provides a helpful error message.

Test plan

  • Added regression tests for direct circular references (obj.self = obj)
  • Added tests for indirect circular references (a.ref = b; b.ref = a)
  • Added tests for circular arrays
  • Verified non-circular nested objects still work
  • Existing macro tests pass

🤖 Generated with Claude Code

@robobun
Copy link
Collaborator Author

robobun commented Jan 27, 2026

Updated 1:16 AM PT - Jan 27th, 2026

❌ Your commit ed440fb4 has 1 failures in Build #35938 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 26486

That installs a local version of the PR into your bun-26486 executable, so you can run:

bun-26486 --bun

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

Walkthrough

Adds sentinel-based circular-reference detection during macro coercion for arrays and objects; defers creation of final Expr nodes until after all items/properties are processed; returns a circular-reference error when a cycle is detected. New regression tests verify behavior for direct, indirect, and array cycles and for non-circular nesting.

Changes

Cohort / File(s) Summary
Macro circular reference handling
src/ast/Macro.zig
Introduces a sentinel (Expr.empty) in visited entries to detect in-progress array/object construction. Defers final E.Array/E.Object creation until after recursive processing completes. Adds circular-reference detection and guards to avoid returning partially-constructed results; reorders logic/comments accordingly.
Regression tests for circular references
test/regression/issue/22552.test.ts
Adds tests exercising direct and indirect object cycles, circular arrays, and a non-circular nested object case; asserts graceful error reporting (exit code 1 and "circular reference") or success as appropriate.

Possibly related PRs

Suggested reviewers

  • Jarred-Sumner
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main fix: detecting circular references in macros and reporting a clear error instead of crashing.
Description check ✅ Passed The description is well-structured with summary, root cause analysis, solution, and test plan sections, exceeding the basic template requirements.
Linked Issues check ✅ Passed The PR successfully implements the core objective from #22552: fixing segfault crashes by detecting circular references and providing clear error messages with graceful failure.
Out of Scope Changes check ✅ Passed All changes are directly focused on the circular reference fix: macro processing logic and regression tests for the specific issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@test/regression/issue/22552.test.ts`:
- Around line 32-36: The test captures stdout into the unused variable `stdout`
via Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]) — remove
the unused `stdout` element from the array and corresponding destructuring, or
alternatively add an explicit assertion (e.g., expect(stdout).toBe('') or
expect(stdout).toHaveLength(0)) to document that nothing was written to stdout;
update the destructuring that currently references `stdout`, `stderr`, and
`exitCode` so it matches the chosen approach and keep the retrieval of
`proc.stderr.text()` and `proc.exited` unchanged.
- Around line 125-136: The test currently only asserts exitCode after spawning
the build process via Bun.spawn (proc) and awaiting proc.stdout.text(),
proc.stderr.text(), and proc.exited; update the test to assert on stdout and/or
stderr contents before or alongside the exitCode to provide better failure
messages and verify build output: read the captured stdout/stderr strings (from
the variables stdout and stderr) and add expectations that stdout contains the
expected build success markers (or that stderr is empty/does not contain error
text) prior to asserting expect(exitCode).toBe(0), referencing proc, stdout,
stderr, and exitCode to locate the relevant code.

Comment on lines 125 to 137
await using proc = Bun.spawn({
cmd: [bunExe(), "build", "./main.ts", "--outdir", "./out"],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});

const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

// Non-circular structures should work fine
expect(exitCode).toBe(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider verifying build output or adding stdout/stderr assertions.

Per coding guidelines, for tests expecting success, checking output before exit code provides more useful error messages on failure. The test could verify the build produced valid output.

♻️ Suggested enhancement for better debugging on failure
   const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

   // Non-circular structures should work fine
+  expect(stderr).toBe("");
   expect(exitCode).toBe(0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await using proc = Bun.spawn({
cmd: [bunExe(), "build", "./main.ts", "--outdir", "./out"],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Non-circular structures should work fine
expect(exitCode).toBe(0);
await using proc = Bun.spawn({
cmd: [bunExe(), "build", "./main.ts", "--outdir", "./out"],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Non-circular structures should work fine
expect(stderr).toBe("");
expect(exitCode).toBe(0);
🤖 Prompt for AI Agents
In `@test/regression/issue/22552.test.ts` around lines 125 - 136, The test
currently only asserts exitCode after spawning the build process via Bun.spawn
(proc) and awaiting proc.stdout.text(), proc.stderr.text(), and proc.exited;
update the test to assert on stdout and/or stderr contents before or alongside
the exitCode to provide better failure messages and verify build output: read
the captured stdout/stderr strings (from the variables stdout and stderr) and
add expectations that stdout contains the expected build success markers (or
that stderr is empty/does not contain error text) prior to asserting
expect(exitCode).toBe(0), referencing proc, stdout, stderr, and exitCode to
locate the relevant code.

When a macro returns an object with a circular reference (either direct
like `obj.self = obj` or indirect like `a.ref = b; b.ref = a`), Bun
would crash with a segmentation fault due to infinite recursion in the
AST visitor.

This fix adds proper cycle detection during JavaScript-to-AST conversion:
- Use a sentinel value (Expr.empty) to mark objects being processed
- When encountering a value already being processed, emit a clear error
  instead of allowing circular AST nodes to be created
- Move expression creation to after all children are processed

Fixes #22552

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@robobun robobun force-pushed the claude/fix-macro-circular-reference-crash branch from f19e9e5 to ed440fb Compare January 27, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Too many database requests in macro

2 participants