Skip to content

fix(codegen): uniform heap variant representation (fixes mixed-rep match)#613

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/variant-rep-fix
Jun 21, 2026
Merged

fix(codegen): uniform heap variant representation (fixes mixed-rep match)#613
hyperpolymath merged 1 commit into
mainfrom
claude/variant-rep-fix

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Item 3b of the batch — the mixed-representation match half of #607. ⚠️ This changes the variant value ABI in the core-Wasm backend, so worth a careful review.

Bug

Zero-arg variants (None, plain enum cases) were emitted as raw i32 tags; arg-carrying variants (Some(x), Ok/Err) as heap pointers to [tag][fields]. A match over a value that can be either form (Option/Result) mis-read one as the other:

match None { Some(v) => v, None => d }   // returned garbage, not d

The Some(v) arm dereferenced the raw tag 1 as a pointer → wrong arm / garbage bind. (unwrap_or(None, 99) returned 0.)

Fix

Box zero-arg variants as a heap [tag] too, so every variant value is uniformly a pointer. The zero-arg match arm now dereferences [ptr+0] for the tag — symmetric with construction and with the existing args path. A small helper gen_box_zero_arg_variant is reused at the three construction sites (bare-ident ExprVar, ExprVariant ×2) and the zero-arg PatCon match (×2).

Verified (under node)

unwrap_or(None, 99)   = 99   ✅ (was 0)
unwrap_or(Some 7, 99) = 7    ✅
divide -> Ok/Err            ✅
plain enum Color{Red|Green|Blue} match/construct  ✅
  • dune test 461 green — no regression from the ABI change.
  • New runtime regression tests/codegen/mixed_variant_match (main()==9907), executed by run_codegen_wasm_tests.sh in the build job. (The .wasm is git-ignored and regenerated from the .affine by the harness, matching how the other fixtures run.)

Reviewer note

This is a representation change touching all variant construction + zero-arg matching. I validated the full suite + targeted runtime cases, but it's the kind of change worth a careful look. Trade-off: zero-arg variants now heap-allocate (4 bytes) like the args variants — consistent with the backend's existing bump-allocator model.

Closes the mixed-representation match item in #607.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8


Generated by Claude Code

…rep match

Zero-argument variants (None, plain enum cases) were emitted as raw i32 tags
while argument-carrying variants (Some(x), Ok/Err) were heap pointers to
[tag][fields]. A `match` over a value that can be either form (Option/Result)
mis-read one as the other: `match None { Some(v) => v, None => d }`
dereferenced the raw tag as a pointer in the Some(v) arm and returned garbage
instead of d (#607).

Box zero-arg variants as a heap [tag] too, so EVERY variant value is uniformly
a pointer; the zero-arg match arm now dereferences [ptr+0] for the tag,
symmetric with construction and with the args path. Touches the construction
sites (bare-ident ExprVar, ExprVariant) and the zero-arg PatCon match.

Verified under node: unwrap_or(None,99)=99 and unwrap_or(Some 7,99)=7 (the
None case was 0 before); Result Ok/Err and plain multi-constructor enums
unchanged. dune test 461 green; adds tests/codegen/mixed_variant_match — a
runtime regression (main()==9907) executed by run_codegen_wasm_tests.sh.

Closes the mixed-representation match item in #607.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8
@hyperpolymath hyperpolymath marked this pull request as ready for review June 21, 2026 00:41
@hyperpolymath hyperpolymath merged commit 2aa00ff into main Jun 21, 2026
13 checks passed
@hyperpolymath hyperpolymath deleted the claude/variant-rep-fix branch June 21, 2026 00:42
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 43 issues detected

Severity Count
🔴 Critical 2
🟠 High 25
🟡 Medium 16

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action denoland/setup-deno@v2 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in scorecard-enforcer.yml",
    "type": "scorecard_publish_with_run_step",
    "file": "scorecard-enforcer.yml",
    "action": "split_scorecard_publish_job",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in instant-sync.yml",
    "type": "secret_action_without_presence_gate",
    "file": "instant-sync.yml",
    "action": "peter-evans/repository-dispatch",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Shell execution -- validate input before passing to shell (1 occurrences, CWE-78)",
    "type": "js_exec_sync",
    "file": "/home/runner/work/affinescript/affinescript/packages/affinescript-cli/mod.js",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "Shell execution -- validate input before passing to shell (2 occurrences, CWE-78)",
    "type": "js_exec_sync",
    "file": "/home/runner/work/affinescript/affinescript/packages/affine-vscode/mod.js",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "Shell execution -- validate input before passing to shell (1 occurrences, CWE-78)",
    "type": "js_exec_sync",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-vite/src/affine-plugin-improved.js",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "expect() in hot path (32 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/affinescript/affinescript/affinescriptiser/src/codegen/wasm_gen.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (29 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/affinescript/affinescript/affinescriptiser/src/codegen/affine_gen.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unsafe block -- requires SAFETY comment (2 occurrences, CWE-676)",
    "type": "unsafe_block",
    "file": "/home/runner/work/affinescript/affinescript/runtime/src/panic.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unsafe block -- requires SAFETY comment (1 occurrences, CWE-676)",
    "type": "unsafe_block",
    "file": "/home/runner/work/affinescript/affinescript/runtime/src/alloc.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath added a commit that referenced this pull request Jun 21, 2026
## What

Merges current `main` into `feat/solo-core-metatheory-proofs` and
resolves all conflicts, so that **#614 stops being `mergeable_state:
dirty`**. While #614 is dirty, GitHub cannot build the merge commit, so
its entire CI suite is suppressed (only `lint-workflows` runs). Merging
this PR into the feature branch un-blocks #614's CI.

## Why it was needed

#614 branched off an old `main` (merge-base 61 commits back) and
conflicts with the standalone-CI + codegen work that has since landed
(#602, #603, #604, #606, #609, #610, #611, #612, #613).

## Conflict resolutions (5 files)

| File(s) | Resolution |
|---|---|
| `governance.yml`, `scorecard.yml`, `scorecard-enforcer.yml`,
`hypatia-scan.yml` | Take `main`'s **standalone** versions. The branch
re-adopted the estate `standards` reusables that `main` deliberately
dropped (#603/#604) to stop run-creation `startup_failure`s. `main`'s
`hypatia-scan.yml` also restores the permissions Hypatia needs
(`security-events: write`, `pull-requests: write`, `secrets: inherit`)
and the `MPL-2.0` SPDX id the Palimpsest license doc mandates for
tooling. |
| `docs/PROOF-NEEDS.md` | Drop the branch's stale 103-line `.md`; keep
`main`'s canonical 359-line `.adoc` (#609). Also satisfies DOC-FORMAT. |
| `docs/history/MODULE-SYSTEM-PROGRESS` | Keep the branch's
`.md`→`.adoc` migration; **port** `main`'s additive #138
codegen-follow-up note + status-table row into the `.adoc` so `main`'s
work is preserved. |

`spark-theatre-gate.yml` and `mirror.yml` were identical to `main`.

## Verification (merged tree)

- `dune build` — clean
- `dune test` — **534/534 pass** (incl. `cross-module constructor
linking, Wasm (#138)`, `Wasm nested tuple patterns`, `Deno-ESM / JS no
duplicate Option/Result constructor`)
- wasm-runtime harness (`tools/run_codegen_wasm_tests.sh`) — all pass
- workflow scan — no `startup_failure` risk introduced

## How to use

Merge this into `feat/solo-core-metatheory-proofs`. #614 then becomes
mergeable and its full CI runs.

> Routed via this branch because the environment only permits pushes to
`claude/inspiring-newton-dg5wov`, not directly to the feature branch.

Un-blocks #614.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8)_

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hyperpolymath <paraordinate@yahoo.co.uk>
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.

2 participants