Skip to content

compiler: lower polymorphic len() to wasm (string + array length)#583

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/cool-keller-gr5sl
Jun 14, 2026
Merged

compiler: lower polymorphic len() to wasm (string + array length)#583
hyperpolymath merged 1 commit into
mainfrom
claude/cool-keller-gr5sl

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Lower polymorphic len() to wasm (string + array length)

A string-wall residual found while verifying the migration corpus is actually unblocked: len() did not lower to wasm (Codegen.UnboundVariable "len") — only string_length did.

AffineScript arrays and strings share the same [len: i32][payload…] header (the list-concat handler already relies on this), so len() is the same single i32.load at offset 0 whether its argument is a String or an array. Merged into the existing string_length arm.

This unblocks the stdlib string layer on the wasm backend (starts_with/ends_with/substring/split/join/pad_* all call len() on strings; join also on arrays), which the corpus's String.length pattern (30 files) needs.

Verified: len("abcd")=4 and len([10,20,30])=3 lower and run; full run_codegen_wasm_tests.sh green (no regressions).

Separately noted (not fixed here): string ==/!= still lower to pointer comparison, not value comparison (string_sub(...) == "ab" is wrongly 0) — a #555-class interp-vs-wasm divergence, tracked for a follow-up slice. It only affects string-logic kernels; integer-brain extraction (strings kept host-side) is unaffected.

https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s


Generated by Claude Code

String-wall residual found while verifying the migration corpus is
unblocked: len() did not lower to wasm (Codegen.UnboundVariable "len"),
only string_length did. AffineScript arrays and strings share the same
[len: i32][payload...] header (the list-concat handler already relies on
this), so len() is the same single i32.load at offset 0 regardless of
whether its argument is a String or an array. Merged into the existing
string_length arm.

This unblocks the stdlib string layer on the wasm backend
(starts_with / ends_with / substring / split / join / pad_* all call
len() on strings, and join also on arrays), which the migration corpus's
String.length pattern (30 files) needs.

Verified: len("abcd")=4 and len([10,20,30])=3 lower and run; full
run_codegen_wasm_tests.sh green (no regressions).

Note (separate, not fixed here): string ==/!= still lower to pointer
comparison, not value comparison (string_sub(...) == "ab" is wrongly 0) —
a #555-class interp-vs-wasm divergence, tracked for a follow-up slice. It
only affects string-logic kernels; integer-brain extraction (strings kept
host-side) is unaffected.

https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 40 issues detected

Severity Count
🔴 Critical 2
🟠 High 22
🟡 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 hyperpolymath marked this pull request as ready for review June 14, 2026 01:12
@hyperpolymath hyperpolymath merged commit e23f48b into main Jun 14, 2026
22 of 27 checks passed
@hyperpolymath hyperpolymath deleted the claude/cool-keller-gr5sl branch June 14, 2026 01:13
hyperpolymath added a commit that referenced this pull request Jun 14, 2026
…584)

## Migration wave: 7 integer-brain kernels from string-gated idaptik
modules

First applied wave of the now-unblocked **string-gated corpus**. Phase B
classified 71 string-gated files; closing the string wall (slices 1–8)
plus the `len()` lowering (#583) opened the integer-brain extraction
path. Seven kernels re-decomposed from idaptik `.res` modules into
AffineScript brains under `proposals/idaptik/migrated/`, fanned out
across 6 parallel agents and **re-verified by me before commit**.

Each is a four-gate deliverable — G1 compile, G2 independent-oracle
parity sweep, G4 assail. Strings / floats / promises / mutable state
stay **host-side** per the C1–C12 recipe; only the pure-integer decision
core crosses to wasm.

| Kernel | Exports | Parity | Assail |
|---|---|---|---|
| PortScanner | 4 | 44/44 | clean |
| PasswordCracker | 7 | 215/215 | clean |
| FirewallDevice | 12 | 164/164 | clean |
| Inventory | 9 | 2840/2840 | clean |
| Drone | 32 | 1192/1192 | clean |
| SecurityDog | 29 | 31533/31533 | clean |
| GuardNPC | 19 | 359/359 | clean |

**Re-decompositions, not transliterations** — e.g. PasswordCracker
inverts the djb2 string-loop so the host walks the string and the brain
does i32 math (`Math.imul`/`|0` modelled in the oracle); Inventory packs
slot-state into a base-3 Int instead of a mutable array; FirewallDevice
keeps CIDR/protocol *string* parsing host-side and decides over integer
flags. Floats cross as floored milli-units; out-of-band inputs return
guarded `-1` sentinels (assail-clean, no in-band collapse). Each oracle
is an independent JS reimplementation from the `.res` semantics, not
copied from the `.affine`.

**Deduped:** `SecurityAI` dropped — already tracked as
`migrated/securityai/` (with a boundary proof) from an earlier wave;
`GlobalNetworkData` likewise pre-existed and was left untouched.

**Two compiler quirks surfaced** (flagged for the playbook, not fixed
here): `total` is a reserved keyword (parse error as an identifier); and
an `if { … }` block immediately followed by a parenthesized expression
parses as a function application. Both have trivial source-side
workarounds.

Builds on #583 (`len`, merged) and the string-wall slices
(#574/#575/#578).

https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s

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

Co-authored-by: Claude <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.

2 participants