Skip to content

feat: implement PHP clone — parser, checker, EIR shallow object copy#472

Open
chadmandoo wants to merge 1 commit into
illegalstudio:mainfrom
chadmandoo:upstream-pr/clone-operator
Open

feat: implement PHP clone — parser, checker, EIR shallow object copy#472
chadmandoo wants to merge 1 commit into
illegalstudio:mainfrom
chadmandoo:upstream-pr/clone-operator

Conversation

@chadmandoo

Copy link
Copy Markdown
Contributor

Implements clone $expr end-to-end:

  • PARSER: contextual recognition — an identifier spelled clone in prefix position followed by an operand-start token parses as a unary operator; clone stays fully usable as a method name and clone($x) lands on the operator like PHP.
  • AST: ExprKind::Clone with arms across every expression walker (resolver, optimizer passes, preludes, usage collectors, parser dependency walks); effects mirror ObjectNew.
  • CHECKER: the operand must be a statically-typed object; the result is the same class. Classes declaring __clone() are rejected explicitly ("__clone() hooks are not supported yet") rather than silently skipping the hook.
  • EIR: Op::ObjectCloneShallow — the backend allocates a same-class payload (heap-kind stamp + class-id word) and copies the statically-sized payload wordwise. Reference-property slots copy their CELL POINTER, matching PHP (a reference property stays shared with the original); dynamic-properties classes are rejected (a payload copy would share the dynamic-prop hash).

Byte-parity vs PHP 8.5: the PSR-7 withX() immutability round-trip (original untouched, clone mutated, object slot shared) and contextual-keyword probes. 2 regression tests.

`clone $expr` was rejected at parse ("Expected ';'"). Implement it
end-to-end:

- PARSER: contextual recognition — an identifier spelled `clone` in
  prefix position followed by an operand-start token (variable/$this/
  name/new/self/static/parent/paren) parses as a unary operator at the
  other prefix operators' binding power. `clone` stays fully usable as
  a method name; `clone($x)` lands on the operator like PHP.
- AST: ExprKind::Clone(Box<Expr>) + arms across every expression walker
  (resolver, optimizer fold/prune/propagate/effects, preludes, usage
  collectors, magic-constants, parser dependency walks). Effects mirror
  NewObject (allocation, may-throw).
- CHECKER: operand must be a statically-typed object; the result is the
  same class. Classes declaring __clone() are rejected ("__clone()
  hooks are not supported yet") rather than silently skipping the hook.
- EIR: Op::ObjectCloneShallow (operand: source object, immediate: class
  name) → backend allocates a same-class payload (heap kind 4, class-id
  word) and copies every 8-byte word of the statically-sized payload.
  Reference-property slots copy their CELL POINTER — PHP semantics (a
  reference property stays shared). Dynamic-properties classes are
  rejected (a payload copy would share the dynamic-prop hash).
  The frozen legacy backend marks Clone unreachable (EIR-only).

Byte-parity vs PHP 8.5: the PSR-7 withX() round-trip (original
untouched, clone mutated, object slot shared) and the contextual-keyword
probes. Full suite 6184 passed / 1 env-only failure. AIC EC-7 (illegalstudio#490):
ward-http Message/Request/Response/ServerRequest/Uri/RequestBehavior all
parse past clone (next gaps: interface covariant returns, EC-10).

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

Three v0.26.0 enum/parser fixes, survey-driven (79% → 85%, 241 → 262/307):

- ENUM-CASE PARAM DEFAULT (illegalstudio#468, declarations.rs): `E $x = E::Case` — an
  enum-case default has the enum object type, but syntactic inference types
  every `::` access as Str. When the declared type is Object(E) and the
  default is a scoped access naming E, accept it (the semantic pass validates
  the case exists). Clears the Badge/Button `$variant expects Object, got Str`
  family (9).
- KEYWORD ENUM-CASE NAMES (body.rs): PHP 8 allows semi-reserved keywords as
  enum-case names (`case Default;`, `case String;`, `case Float;`) — every one
  but `class` (reserved for `Foo::class`). Clears CardVariant/LogicalType/
  HeadAssetMode `Expected case name after 'case'` (10).
- USE CONST/FUNCTION IMPORTS (illegalstudio#473/EC-10/illegalstudio#493, namespace_use.rs): the lexer
  eagerly tokenizes `PHP_INT_MAX` etc., so the use-declaration parser must
  accept those dedicated tokens as import names (`use const PHP_INT_MAX;`).
  Clears RegionEntry `Expected imported name after 'use'` (2).

Deferred (not survey-visible on v0.26.1): illegalstudio#469 keyword-case access, illegalstudio#472 clone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chadmandoo added a commit to chadmandoo/elephc that referenced this pull request Jul 10, 2026
…opy (R9/#599, illegalstudio#472)

v0.26.1's parser rejected `clone $x` ("Expected ';'"), gapping every PSR-7
immutability wither (Message/Uri/RequestBehavior `$new = clone $this`). Re-ports
illegalstudio#472 (8485e9e) onto the post-EIR-refactor tree:

- PARSER: contextual `clone <expr>` prefix operator (clone is not a reserved
  token — `clone_operand_follows` guards it so a bare identifier spelled "clone"
  in a non-operator position still parses as a name). New ExprKind::Clone(Box<Expr>).
- CHECKER: `clone $x` infers as the type of `$x`.
- EIR: shallow object/array copy lowering (array_clone_shallow, x86 frame-safe).
- The new AST variant is threaded through every ExprKind match (name resolver,
  optimizer passes, magic-constant walker, usage/invalidation analysis, warnings).
  The legacy backend is gone in v0.26.1, so no legacy emit arm is needed.

clone codegen tests pass (round-trip + contextual-keyword). Survey 90% → 91%
(277 → 278/307); the "Expected ';'" gaps (Message ×4, Uri, RequestBehavior) clear.
Also completes R6's deferred illegalstudio#472.

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.

1 participant