Skip to content

A global whose initializer branches becomes a constructor (#462) - #474

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-global-optional-to-union
Oct 3, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-global-optional-to-union

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Fixes #462.

let g: Node | null | undefined = mkU(), where mkU returns Node | undefined, failed in every model with "'ts.Global' op expects region #0 to have 0 or 1 blocks". Every conditional global initializer, such as let x = on() ? 1 : 2, failed the same way.

Widening the optional into the union is a ts.If, just like a conditional. The affine lowering turns that into blocks, and the SingleBlockImplicitTerminator trait on ts.Global rejected them.

Changes

  • Drop the trait. MLIRGen still builds one block ending in ts.GlobalResult, and nothing reads the initializer as a single block after the affine lowering.
  • Make branching initializers constructors. GlobalOpLowering makes a global whose initializer has more than one block a constructor, because only a function can hold the branches.
  • rc: once the program compiled, the widened optional lost its object under rc:
    • The call runs before the ts.If, and the branch yields its value through views (ts.Value, a cast, ts.OptionalValue).
    • So the global never took the call's reference, which was given back at the end of the initializer.
    • claimGlobalInitializers (rc: a global takes the reference of an owned producer through a widening view (#459) #468) now follows a ts.If whose branches all yield either one owned producer from before it, through views, or a value that holds nothing (the empty optional, null, undefined).
    • The global then takes that producer's count.

Tests

  • New test: 00global_initializer_branches.ts, registered as compile and JIT tests and in the rc/none corpus. It failed to compile before the change. It covers:
    • an optional widened into a union, set and empty;
    • conditional number, string and object globals.
  • Own corpus: unchanged.
  • Full Release ctest: 3666/3667. The one failure is test-compile-gc-defaultlib-collector, the known local LNK2005 against the stale installed default library, which fails on main the same way.

🤖 Generated with Claude Code

`let g: Node | null | undefined = mkU()`, with mkU returning `Node | undefined`, failed in every
model with "'ts.Global' op expects region #0 to have 0 or 1 blocks", and so did any conditional
global initializer (`let x = on() ? 1 : 2`). The optional's widening into the union, like a
conditional, is a ts.If; the affine lowering turns it into blocks, and ts.Global's
SingleBlockImplicitTerminator trait rejected them.

The trait is dropped (MLIRGen still builds one block ending in ts.GlobalResult, and nothing reads
the initializer as one block after the affine lowering), and GlobalOpLowering makes a global
whose initializer has more than one block a constructor: only a function can hold the branches.

Under rc the widened optional then lost its object: the call runs before the ts.If, and the
branch yields it through views (ts.Value, a cast, ts.OptionalValue), so the global never took its
reference over and the call's was given back at the end of the initializer. claimGlobalInitializers
now follows a ts.If whose branches all yield one owned producer from before it through views, or
a value that holds nothing (the empty optional, null, undefined), and the global takes the count.

Test: 00global_initializer_branches (compile and JIT, and in the rc/none corpus): an optional
widened into a union (set and empty), and conditional number, string and object globals. It failed
to compile before the change. The own corpus is unchanged.

Fixes #462.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit f53e5fc into main Oct 3, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-global-optional-to-union branch October 3, 2026 20:38
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.

Global T | null | undefined initialised from a T | undefined call: 'ts.Global' op expects region #0 to have 0 or 1 blocks

1 participant