Skip to content

fix(codegen): stamp the canonical x86_64 heap magic through one shared helper (#482)#491

Open
mirchaemanuel wants to merge 1 commit into
illegalstudio:mainfrom
mirchaemanuel:fix/482-canonical-heap-magic
Open

fix(codegen): stamp the canonical x86_64 heap magic through one shared helper (#482)#491
mirchaemanuel wants to merge 1 commit into
illegalstudio:mainfrom
mirchaemanuel:fix/482-canonical-heap-magic

Conversation

@mirchaemanuel

Copy link
Copy Markdown
Contributor

Summary

Fixes #482. Twelve emitters hand-typed a transposed x86_64 heap magic (0x4548504c instead of the canonical 0x454C5048, ASCII ELPH) in their kind-6 throwable stamps: the runtime-raised ValueError/TypeError/LogicException/Error paths, JSON throw errors, SPL fixed-array / doubly-linked-list exceptions, the hash builtin error path, and static-property/math error objects. The x86_64 refcount and free helpers gate on the canonical magic, so every refcount operation on those objects was a silent no-op on linux-x86_64.

Fix

  • One shared, documented helper next to the sentinel constants: codegen_support::sentinels::x86_64_heap_kind_word(kind) — all 12 stamps now go through it (the emitted assembly changes only in the magic value).
  • The constant's layout is unit-tested, and a repository lint test scans src/ so a hand-typed transposed literal can never come back — the class of typo is closed, not just the instances (full disclosure: two of the twelve were mine, faithfully copied from the pre-existing ValueError emitter, which is exactly why a shared constant was the right fix).

Behavioral note

Neutral today: caught-exception releases land with the #448 fix in PR #477, and until then both increfs and decrefs on these objects were no-ops alike. Once #477 merges, throwables from these paths become genuinely freeable on x86_64, matching ARM64 (whose release path has no magic/kind gate). The two changes are independent and safe to merge in either order.

Verification

Suites green: 1614 codegen (enums/exceptions/spl/json/oop/types/static — every touched emitter's surface) + 993 error tests; sentinels unit + lint tests included; asm comment alignment verified on all rewritten lines.

…d helper

Fixes illegalstudio#482. Twelve emitters hand-typed a transposed heap magic
(0x4548504c vs the canonical 0x454C5048 'ELPH') in their kind-6 throwable
stamps: the runtime-raised ValueError/TypeError/LogicException/Error paths,
JSON throw errors, SPL fixed-array/doubly-linked-list exceptions, the hash
builtin error path, and static-property/math error objects. The x86_64
refcount and free helpers gate on the canonical magic, so every refcount
operation on those objects was a silent no-op on linux-x86_64.

All kind-word stamps now go through crate::codegen_support::sentinels::
x86_64_heap_kind_word(kind), documented next to the sentinel constants; the
constant's layout is unit-tested and a repository lint test scans src/ so the
hand-typed transposed literal cannot come back.

Behavior today is neutral (caught-exception releases land with the illegalstudio#448 fix
in PR illegalstudio#477; until then increfs and decrefs on these objects were both no-ops).
Once illegalstudio#477 merges, throwables from these paths become genuinely freeable on
x86_64 like on ARM64.

Suites green: 1614 codegen (enums/exceptions/spl/json/oop/types/static) +
993 error tests; sentinels unit + lint tests included.
nahime0 pushed a commit that referenced this pull request Jul 10, 2026
…type check)

An interface-typed constructor-promoted property (or param) with a concrete
default — `public ComponentResolverInterface $r = new NullComponentResolver()` —
was rejected: "Method parameter $r expects Object(I), got Object(N)". The default
is validated in the purely-syntactic schema pass, where the class/interface table
isn't populated yet, so type_accepts's class_implements_interface spuriously
returns false. Accept an object default for an object-typed param there (mirroring
the existing enum-case leniency); a genuine mismatch still surfaces at the call
site. Byte-parity regression test. AIC epic #483 / EC-8 (#491), the object-param
sub-part (~14 gaps: ward-forms AlteredForm/Field/... interface-typed promoted props).

(cherry picked from commit db17c17)
nahime0 pushed a commit that referenced this pull request Jul 10, 2026
…throw-guard

`if ($value === false) { throw; } return $value;` (ward-http StreamGuards::requireInt
and the string/stream guards) declared `: int` but was inferred as returning
Union([Int, Bool]) — type_guard_narrowing recognized is_*()/instanceof but not the
`$x === false` strict comparison, so the divergent-guard complement (which the if/else
narrowing already keeps after a diverging body) never stripped the false-ish member.
Recognize `$var === false` / `false === $var` (BinOp::StrictEq) → narrow to Bool in the
then-branch and strip it in the else-branch (int|false → int). Byte-parity regression test.
AIC epic #483 / EC-8 (#491), the return-over-widening sub-part.

(cherry picked from commit 7c8ac75)
nahime0 pushed a commit that referenced this pull request Jul 10, 2026
`$this->raw instanceof Message ? $this->raw : new Message($this->raw)`
with `private Message|string $rawMessage` failed to compile — ternary
inference applied zero narrowing, and guard narrowing keyed only
variables (ExprKind::Variable), never property receivers.

Add branch_guard_narrowing(): recognizes variable guards (delegating to
type_guard_narrowing) plus `$var->prop instanceof Class` /
`$this->prop instanceof Class`, keying the narrowed type under a
synthetic \x01-sigil env key that infer_property_access_type consults
before falling back to the declared field type. Both ternary inference
paths (pure + assignment-effects) narrow their branch envs with it. A
ternary is a single expression — no intervening writes — so the property
narrowing is write-invalidation-safe here by construction.

Byte-parity vs PHP 8.5 (hi:k) + codegen regression test. AIC EC-8 (#491):
clears the FieldViolation Message|string pattern across the Forms Field
family.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit fcff01d)
nahime0 pushed a commit that referenced this pull request Jul 10, 2026
…tement-level

Three narrowing gaps behind ~7 AIC Domain classes:
- `if (is_null($p)) { throw; }` did not narrow ?int → int (only
  `=== null` did): add is_null to the predicate map.
- `if (!$this->prop instanceof C) { throw; } return $this->prop;`
  (ward-forms StoreResult::ref) kept the property at ?C: statement-level
  guards now key simple property receivers via the synthetic
  \x01-sigil env key, riding the existing if-chain save/restore and
  divergent-complement machinery.
- The ternary property narrowing from the previous commit now shares the
  same single entry point.

Mechanics: fold type_guard_narrowing + branch_guard_narrowing into
guard_narrowing(); guard_receiver_and_type() extracts (receiver expr,
target) for all guard forms (is_* / is_null / instanceof / === false /
=== null) on variables AND simple property accesses.

Soundness: property narrowings are purged after any call (the callee may
mutate the object), after assignments writing through a property, and at
loop-body entry (a later iteration may observe an earlier write) —
purge_property_narrowings() in the effects walk. A call's own argument
checking still sees the narrowing (purge happens after the call's
inference).

Byte-parity vs PHP 8.5 (5 / x) + 2 codegen regression tests. Full suite
6177 passed / 1 env-only failure (IPv6-DNS network test). AIC EC-8 (#491).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 3c58b4e)
chadmandoo added a commit to chadmandoo/elephc that referenced this pull request Jul 10, 2026
…26.1

Re-ports four v0.26.0 checker-completeness fixes onto the clean v0.26.1
base, driven by the AIC survey (ward-forms Field method chain was the
deepest shared gap across 17 field classes). Survey 73% → 79% (224 →
241/307).

- COMPARISON OPERANDS (elephc #598 / R8): relational (`< > <= >=`) and
  spaceship (`<=>`) accept scalar operands — strings compare
  lexicographically, unions coerce at runtime — not just numerics. PHP
  semantics; v0.26.1 rejected `$a < $b` on Str/Union.
- ABSTRACT new static() (EC-3 / illegalstudio#486): a late-bound `new static()` only
  ever instantiates a CONCRETE subclass at runtime, so the abstract base
  declaring it is not a constructor target to validate
  (Field::with()). class_refs.rs filters abstract candidates.
- RETURN COLLECTOR narrowing (part of the same cluster): collect_return_infos
  re-applies if-guard narrowing so `return $value;` inside
  `if ($value instanceof Message)` reports Message, not the un-narrowed
  type (Field::messageOption()).
- MIXED/UNION BOUNDARY (EC-8 / illegalstudio#491): types_compatible accepts a `mixed`
  value into any narrower declared type (runtime-enforced PHP) and a
  union value with >=1 compatible member (int|false on its success
  path). Clears the listOption/renderOption/requireZero family.

Co-Authored-By: Claude Opus 4.8 <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.

x86_64 emitters stamp a transposed heap magic (0x4548504c vs 0x454C5048): refcount ops silently no-op

1 participant