Skip to content

Magician#451

Draft
nahime0 wants to merge 1188 commits into
mainfrom
feat/eval
Draft

Magician#451
nahime0 wants to merge 1188 commits into
mainfrom
feat/eval

Conversation

@nahime0

@nahime0 nahime0 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Just magic

@nahime0 nahime0 force-pushed the feat/eval branch 3 times, most recently from 664b2fa to d5d53d7 Compare July 9, 2026 12:22
nahime0 added 29 commits July 11, 2026 00:36
- links-magician-once uses dynamic eval strings (literals compile AOT)
- requires-eval-bridge uses an indexed array write; read-only foreach
  now goes through the scope-read AOT path
- lowers_examples_corpus gets the 180s slow-timeout override
declared_params doubles as the invoker boxed-ABI marker, so untyped
params widened to Mixed carry a true flag; require the source type
expression too. Replaces the reverted declared_params change that
broke callback invokers.
The checker seeds $args as Array<Never>; eval-only magic calls never
specialize it, and a Never element lowers every $args[N] read to an
empty constant. Keep the (Str, Array) contract only for Array<Mixed>.
The runtime vtables dispatch stream_open/stream_read/filter/... with
raw fixed-ABI arguments; widening their untyped params to boxed Mixed
desynchronized dispatcher and body.
The single-args override broke ReflectionMethod::invokeArgs dispatch;
only the Mixed return type needs patching.
Class-level attribute capture lacked the ClassConstant arm the module
capture already had, and newInstance dropped named-argument keys.
Untyped ctor params widen to boxed Mixed while the checker infers the
slot as a concrete object; unbox on store (tag 6), null sentinel for
non-object payloads, matching the other Mixed property coercions.
Existence still folds from the literal class name; the demand walker
already treats non-literal flags as false.
The path feeds eval Reflection hooks only; gate it on the eval
features. Anchor the optimizer string-helper asserts to main's section
(synthetic Reflection bodies legitimately call them) and update the two
eval-fixture asserts to the literal-AOT markers.
- direct-store and local-scalar sync targets accept slots the native
  code already writes; the store emitters release the previous
  refcounted value before overwriting
- read/write targets accept parameter-initialized slots (by-value
  closure captures)
- the lowering-side direct-store classifier now checks scalar kinds
  against existing slot types, so type-changing stores keep the barrier
Track corpus, allowed divergences, fix policy, and non-goals so main and
magician parsers stay aligned without premature parser unification.
…ame case

- keep the __call args contract for any call-site-specialized element
  type; only the raw Array<Never> seed (codegen_repr hides it as Void)
  falls back to the boxed Mixed widening
- reflection hasType: non-Mixed declared params are always genuine
  (builtin sigs and variadics carry no type expressions); only declared
  Mixed params need the source hint check
- the name resolver no longer lowercases static method names: lookups
  fold case themselves and __callStatic receives the as-written name
…free AOT evals

Track that an eval() was lowered even when its fragment avoids the scope
barrier; dynamic constant exists/fetch probes must consult the eval
context either way, so declare the context local before emitting them.
Named and spread call plans re-materialize the optional mode default even
when the source omits it, tripping the unary count contract in codegen.
Prune mode:0 at the AST level and drop a trailing constant-zero mode
operand after signature lowering.
Compute the next auto key from all int-normalizing key forms (bools,
integral floats, canonical numeric strings, negated literals) and let the
first integer-like key seed the cursor so leading negative keys continue
from there instead of restarting at zero.
Narrowing a returned Heap(Mixed) result to a raw scalar constant breaks
the return ABI of internal eval AOT functions.
Dispatch on the runtime tag: hashes probe __rt_hash_get, indexed arrays
reuse the int-key bounds helper, other payloads answer false. Also accept
float keys via integer-cast normalization in both key materializers.
Unbox object/array/hash payload pointers when copying a scope cell back
into a native local, and fall back to the null pointer when eval removed
the entry.
Both predicates have complete Mixed-tag lowerings and registry/legacy
first-class signatures; only the dispatch allowlist excluded them.
The checker resolves a bare array param to a string-keyed map, rejecting
newInstanceArgs(["Q", "R"]); type the param iterable so both array
shapes pass.
emit_mixed_array allocates a hash, but nested values were boxed with the
indexed-array tag, so Mixed readers dispatched the payload to the wrong
container helpers and returned garbage elements.
…sigs

Registry by-ref params are typed Mixed by generality; marking them
declared made the checker demand boxed storage from every argument
variable, regressing sort(...) over plain arrays. Reflection hasType is
unchanged: Mixed params without a type expression already report false.
print_r now prints the complete Array block and var_dump the PHP-style
object dump; normalize runtime object ids before comparing.
A literal eval storing a different scalar type into an existing local
(e.g. $x = 1; eval('$x = "changed";')) previously fell back to the
scope path, whose write-back cast the value through the stale slot type.
Widen the slot to boxed Mixed storage and keep the native direct-store
path; codegen re-lowers every load/store with the final slot type.
…rrides

PHP resolves $obj->m() from class scope S to S's own private instance
method when S declares one and the receiver is an instance of S, even if
a subclass overrides m publicly. The interpreter resolved metadata from
the receiver's runtime class, found the override, and sent its class as
the bridge scope — masking the ambient scope the AOT caller had pushed.
Check the calling scope first and dispatch with it directly; the native
bridge's hidden private shadow slots already select the right symbol
from the scope string. Applies to direct calls and callable arrays.
Extend eval result casting (False lowers as Bool) and native type-spec
formatting ("false") for the literal-false type introduced by main's
narrowing work.
class_exists() accepts a dynamic autoload flag (never an AOT autoload
demand; class_parents keeps the literal-flag coverage) and stacked
parameter attributes now persist in the AST as ordered groups.
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.

1 participant