Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions docs/capabilities-syntax.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
Wrote /compiler/docs/capabilities-syntax.md (56 constructs).
# Language construct capability matrix

Auto-generated by `script/capability-syntax.php`. Do not edit by hand.
Expand All @@ -11,27 +10,28 @@ Tracking issues: [#58](https://github.com/PurHur/php-compiler/issues/58), [#145]
| Construct | VM | JIT | AOT | Issue | Notes |
|-----------|:--:|:---:|:---:|-------|-------|
| `class` / `new` | yes | yes | yes | [#58](https://github.com/PurHur/php-compiler/issues/58) | compliance PHPT; bootstrap AOT |
| Anonymous class `new class { }` | yes | yes | yes | [#1233](https://github.com/PurHur/php-compiler/issues/1233) | php-cfg inline Stmt\Class_ in parseExpr_New; synthetic AnonymousClass@line name |
| Enum declarations `enum Foo: string { case Bar = 'x'; }` | yes | yes | yes | [#1356](https://github.com/PurHur/php-compiler/issues/1356) | Backed enum cases as class constants; `Foo::Bar` const-like fetch; `enum_exists` registry; `implements` metadata (#2299); static methods (#2299) |
| Anonymous class `new class { }` | yes | yes | no | [#1233](https://github.com/PurHur/php-compiler/issues/1233) | php-cfg inline Stmt\Class_ in parseExpr_New; synthetic AnonymousClass@line name |
| Enum declarations `enum Foo: string { case Bar = 'x'; }` | yes | yes | yes | [#1356](https://github.com/PurHur/php-compiler/issues/1356) | Backed enum case objects with `->name` / `->value`; string context coerces to backed scalar (#3518); `Foo::Bar` singleton fetch; `enum_exists` registry; `implements` interface list + instance methods + `instanceof` (#3373); static methods (#2299); `Enum::cases()` VM (#3308) |
| Instance methods (`ClassMethod` / `Expr_MethodCall`) | yes | yes | yes | [#58](https://github.com/PurHur/php-compiler/issues/58) | MiniWebApp `$router->dispatch()` (#2059); compliance PHPT; bootstrap AOT |
| Static methods on user classes (`Expr_StaticCall`) | yes | yes | yes | [#2209](https://github.com/PurHur/php-compiler/issues/2209) | Public static methods without $this; `Router::fromConfig()` factory (#2059); Late static `static::method()` tracked separately (#1231) |
| Constructors (`__construct`) | yes | yes | yes | [#145](https://github.com/PurHur/php-compiler/issues/145) | Router `__construct(array $config)` (#2059); compliance PHPT; bootstrap AOT |
| `clone` + `__clone()` magic method | yes | yes | yes | [#3170](https://github.com/PurHur/php-compiler/issues/3170) | Zend zend_std_clone_object: shallow copy then __clone when defined; VM invokePhpFunction; JIT invokeCloneMagicIfPresent after cloneObject; compliance PHPT |
| Private methods | yes | yes | yes | [#145](https://github.com/PurHur/php-compiler/issues/145) | Router private `render*` paths (#2059); compliance PHPT; bootstrap AOT |
| Property fetch `$this->x` | yes | yes | yes | [#58](https://github.com/PurHur/php-compiler/issues/58) | compliance PHPT; bootstrap AOT |
| Constructor property promotion | yes | yes | yes | [#1359](https://github.com/PurHur/php-compiler/issues/1359) | Promoted params declare property + assign in __construct |
| Method return types (`: string` / `: void`) | yes | yes | yes | [#55](https://github.com/PurHur/php-compiler/issues/55) | #55 native `: string`/`: int`/`: bool`/`: float`/`: array`/`: ?T` LLVM returns; MCJIT execute #2055 |
| Method return types (`: string` / `: void`) | yes | yes | yes | [#55](https://github.com/PurHur/php-compiler/issues/55) | #55 native `: string`/`: int`/`: bool`/`: float`/`: array`/`: ?T` LLVM returns; MCJIT execute #2055; compliance PHPT; bootstrap AOT |
| Dynamic property access `$obj->$name` | yes | yes | yes | [#1227](https://github.com/PurHur/php-compiler/issues/1227) | JIT compares runtime name to declared properties; unknown names abort at runtime; compliance PHPT |
| Variable function call `$fn()` | yes | yes | yes | [#56](https://github.com/PurHur/php-compiler/issues/56) | VM resolves callee at runtime; JIT when callee name is compile-time string in variable (#56) |
| Native user-class link (`phpc build --project`) | yes | yes | yes | [#764](https://github.com/PurHur/php-compiler/issues/764) | AOT link yes (#568 closed); native execute ✅ (#764 closed); compliance PHPT; bootstrap AOT |
| `instanceof` | yes | yes | yes | [#138](https://github.com/PurHur/php-compiler/issues/138) | compliance PHPT; bootstrap AOT |
| `instanceof` union RHS `(A\|B)` | yes | yes | yes | [#3461](https://github.com/PurHur/php-compiler/issues/3461) | php-cfg parses BitwiseOr of class names from php-parser; VM + JIT union OR (#3461); compliance PHPT |
| `instanceof` union RHS `(A|B)` | yes | yes | yes | [#3461](https://github.com/PurHur/php-compiler/issues/3461) | php-cfg parses BitwiseOr of class names from php-parser; VM + JIT union OR (#3461) |
| `match` expression | yes | yes | yes | [#143](https://github.com/PurHur/php-compiler/issues/143) | Lowered in php-cfg to === / jump-if / assign (#143); Wave-3 literal-arm subset (#2398); acceptance PHPT (#2428); compliance PHPT; bootstrap AOT |
| Closures `function () { }` / `use ($var)` | yes | yes | no | [#72](https://github.com/PurHur/php-compiler/issues/72) | VM ClosureState + __invoke; use() by-value and by-ref (#3081, #3108); array_map/filter/usort callbacks (#3086); JIT ClosureHelper: TYPE_CLOSURE + use() value/ref IR (#3092, #3108); indirect $arr[0]() via __closure_target (#3089, #3092); bootstrap / self-host AOT stubs null; bin/jit.php MCJIT execute still probe-dependent (#98); compliance PHPT |
| Arrow functions `fn () =>` | yes | yes | no | [#142](https://github.com/PurHur/php-compiler/issues/142) | Desugars to TYPE_CLOSURE (#142); VM + JIT same as closures (#3092, #3108); compliance PHPT |
| Generators (`yield` / `foreach`) | yes | no | no | [#167](https://github.com/PurHur/php-compiler/issues/167) | VM GeneratorState + foreach; keyed yield (#3085); bin/jit.php VM fallback via Block::requiresVmLowering (#3085); see docs/generators-jit-aot.md; `yield from` generator path supported on VM (#167); compliance PHPT; VM-only lowering |
| `ClassName::class` / `static::class` | yes | yes | yes | [#740](https://github.com/PurHur/php-compiler/issues/740) | Compile-time class name string; related to __CLASS__ (#199); compliance PHPT; bootstrap AOT |
| Class member constants `public` / `private` / `protected const` | yes | yes | yes | [#2199](https://github.com/PurHur/php-compiler/issues/2199) | MiniWebApp `Router::DEFAULT_CONTACT_NAME_MAX` (#2059); compliance PHPT; bootstrap AOT |
| PHP 8.3 typed class constants (`const array X = [1,2];`, `const string S = 'a';`) | yes | yes | yes | [#3592](https://github.com/PurHur/php-compiler/issues/3592) | Compile-time literal fold for array/scalar const values; typed mismatch is compile-time TypeError; JIT lowers immutable array constants (#3592) |
| Late static binding `static::method()` / `static::class` | yes | yes | yes | [#1231](https://github.com/PurHur/php-compiler/issues/1231) | VM/JIT called-class propagation; parent::method/class/$prop and static:: LSB (#1858, #3093) |
| `new static()` and `: static` return type (late-bound class) | yes | yes | yes | [#3412](https://github.com/PurHur/php-compiler/issues/3412) | VM resolveClassScopeName on TYPE_NEW; returnTypeStatic verify (#3412) |
| Magic constants `__CLASS__`, `__METHOD__`, `__FUNCTION__` | yes | yes | yes | [#199](https://github.com/PurHur/php-compiler/issues/199) | Lowered at parse time via php-cfg MagicStringResolver; compliance PHPT |
Expand All @@ -44,29 +44,29 @@ Tracking issues: [#58](https://github.com/PurHur/php-compiler/issues/58), [#145]
| Static property `Class::$prop` | yes | yes | yes | [#1225](https://github.com/PurHur/php-compiler/issues/1225) | Class-scoped storage; `self::` / `static::`; literal property names in JIT |
| Error-control operator `@` on expressions | yes | yes | yes | [#3546](https://github.com/PurHur/php-compiler/issues/3546) | php-cfg ErrorSuppressBlock + Simplifier preserve (#3546); VM masks error_reporting; JIT/AOT no-op until native silence |
| `unset()` on variables, array offsets, and object properties | yes | yes | yes | [#2273](https://github.com/PurHur/php-compiler/issues/2273) | Locals, `$this->prop`, public properties, string/int keys (#1224); Static properties via TYPE_STATIC_PROPERTY_UNSET (#2256) |
| Function-local `static $var` / `static $var = <literal>` | yes | yes | yes | [#2286](https://github.com/PurHur/php-compiler/issues/2286) | Literal int/string init only in v1; VM + JIT + AOT; uninitialized `static $x;` with isset guard — Zend parity (#3533); `static &$x` is not valid PHP syntax (php-src `static_var` grammar) |
| Function-local `static $var` / `static $var = <literal>` | yes | yes | yes | [#2286](https://github.com/PurHur/php-compiler/issues/2286) | Literal int/string init only in v1; VM + JIT + AOT; Uninitialized `static $x;` with isset guard — Zend parity (#3533); `static &$x` is not valid PHP syntax (php-src `static_var` grammar) |
| Keyed array destructuring (`["a" => $x]`) | yes | yes | yes | [#1234](https://github.com/PurHur/php-compiler/issues/1234) | Skip string-key CFG split for fetch+assign destructuring pairs (#1234) |
| `goto` / labels (function scope) | yes | yes | yes | [#1228](https://github.com/PurHur/php-compiler/issues/1228) | php-cfg lowers labels to CFG Jump; VM avoids frame nesting on same-block back-edges; compliance PHPT |
| `declare(strict_types=1)` scalar parameter checks | yes | yes | yes | [#1229](https://github.com/PurHur/php-compiler/issues/1229) | VM #156; JIT enforces at user call sites via JIT\TypeCheck + Native::compileArg weak casts |
| PHP 8.3+ generic array types `list<T>` / `array<K,V>` (parameters and properties) | yes | yes | yes | [#3705](https://github.com/PurHur/php-compiler/issues/3705) | Source rewrite to magic identifier types for php-parser v4; VM list shape check (#3705); Zend generic-array RFC not merged in php-src 8.4 — parity target is proposed list/array forms |
| Variable variables (`$$name`) | yes | yes | yes | [#1226](https://github.com/PurHur/php-compiler/issues/1226) | php-cfg nests Operand\Variable name; VM resolves runtime local by name; JIT compile-time name fold (#1226); compliance PHPT |
| Variable function calls (`$fn()`) | yes | yes | yes | [#56](https://github.com/PurHur/php-compiler/issues/56) | VM resolves callee at runtime; compiler folds literal assignment; JIT uses compile-time string |
| Invokable objects (`$obj()` / `__invoke`) | yes | yes | yes | [#1232](https://github.com/PurHur/php-compiler/issues/1232) | Object-typed FuncCall lowered to __invoke method dispatch; VM runtime fallback |
| First-class callable syntax (`foo(...)`, `Class::m(...)`) | yes | yes | yes | [#1363](https://github.com/PurHur/php-compiler/issues/1363) | php-cfg Expr_FirstClassCallable (#1230); VM stores string or [obj, method] array; JIT folds strlen(...) / Class::m(...) via compileTimeString assign chains (#1363); php-types TypeReconstructor patch for Expr_FirstClassCallable (#2315) |
| `use function` / `use const` imports | yes | yes | yes | [#2325](https://github.com/PurHur/php-compiler/issues/2325) | php-cfg resolves imported names at parse time; no Stmt_Use lowering required |
| Namespace group use (`use Foo\{Bar, Baz}`) | yes | yes | yes | [#2443](https://github.com/PurHur/php-compiler/issues/2443) | PhpParser Stmt_GroupUse; NameResolver registers aliases; GroupUseStripper before PHPCfg |
| Flexible heredoc/nowdoc indentation stripping (PHP 7.3+) | yes | yes | yes | [#3636](https://github.com/PurHur/php-compiler/issues/3636) | php-parser Emulative FlexibleDocStringEmulator + parseDocString stripIndentation (#3636); Indented closing label sets docIndentation column; basic heredoc/nowdoc #178; Zend/zend_language_scanner.l flexible heredoc/nowdoc parity; compliance PHPT |
| `never` return type | yes | yes | yes | [#1358](https://github.com/PurHur/php-compiler/issues/1358) | php-cfg Op\Type\Never_; any `return` in body is a compile error; normal completion via throw/exit; `never` as a parameter type is a compile error ([#3506](https://github.com/PurHur/php-compiler/issues/3506)) |
| `never` return type | yes | yes | yes | [#1358](https://github.com/PurHur/php-compiler/issues/1358) | php-cfg Op\Type\Never_; any `return` in body is a compile error; normal completion via throw/exit |
| Intersection types (`A&B`) | yes | yes | yes | [#1357](https://github.com/PurHur/php-compiler/issues/1357) | php-cfg Op\Type\Intersection; VM checks object implements each interface at call |
| `trait` declarations with method bodies | yes | yes | yes | [#2312](https://github.com/PurHur/php-compiler/issues/2312) | VM registers traits in class table with isTrait; trait use in classes is #144; interface_exists/trait_exists distinguish from class_exists |
| Simple `use Trait;` in class body | yes | no | no | [#2314](https://github.com/PurHur/php-compiler/issues/2314) | php-cfg-trait-use.patch; VM merges trait methods into class; TraitUseAdaptation (alias/insteadof) is #144; VM-only lowering |
| Simple `use Trait;` in class body | yes | no | no | [#2314](https://github.com/PurHur/php-compiler/issues/2314) | php-cfg-trait-use.patch; VM merges trait methods into class; TraitUseAdaptation (alias/insteadof) is #144; Horizontal trait method collision fatals at compile time (#3416); VM-only lowering |
| Array/argument unpack `...$x` | yes | yes | yes | [#1361](https://github.com/PurHur/php-compiler/issues/1361) | php-cfg spread.patch (#141); VM HashTable::spreadFrom; JIT HashTableHelper::spreadInto + mergeCallArgEntries; compliance PHPT |
| `__serialize` / `__unserialize` magic methods | yes | yes | yes | [#1365](https://github.com/PurHur/php-compiler/issues/1365) | serialize()/unserialize() call __serialize/__unserialize when present; VM via VmSerialize (#3368) |
| Multi-type catch `catch (A|B $e)` | yes | yes | yes | [#1362](https://github.com/PurHur/php-compiler/issues/1362) | php-cfg records union types per catch; VM filters TYPE_CATCH via OpCode.catchTypes; compliance PHPT |
| `try` / `catch` / `throw` | yes | yes | yes | [#57](https://github.com/PurHur/php-compiler/issues/57) | throw lowering #195; php-cfg TryCatch overlay (#2084); VM TYPE_TRY/CATCH/THROW/FINALLY; VM finally-before-catch + return-through-finally (#3081, #3106); TryCatchComplianceTest (10 tests); JIT TryCatchHelper IR verify (#3107); bin/jit.php VM fallback via requiresVmLowering (#2114); MCJIT execute unsafe; compliance PHPT; bootstrap AOT |
| `try` / `catch` / `throw` | yes | yes | yes | [#57](https://github.com/PurHur/php-compiler/issues/57) | throw lowering #195; php-cfg TryCatch overlay (#2084); VM TYPE_TRY/CATCH/THROW/FINALLY; VM finally-before-catch + return-through-finally (#3081, #3106); TryCatchComplianceTest (10 tests); JIT TryCatchHelper IR verify (#3107); bin/jit.php VM fallback via requiresVmLowering (#2114); MCJIT execute probe TryCatchJitExecuteTest; compliance PHPT; bootstrap AOT |
| readonly classes | yes | yes | yes | [#1360](https://github.com/PurHur/php-compiler/issues/1360) | php-cfg Class_::flags MODIFIER_READONLY; VM rejects instance property writes after __construct |
| Property hooks (`get` / `set` on properties) | yes | no | no | [#3145](https://github.com/PurHur/php-compiler/issues/3145) | SourcePreprocessor lowers hooks for php-parser v4; VM dispatches set/get via `__phpc_property_*` methods; php-src `Zend/zend_compile.c` |
| PHP 8 attributes — `ReflectionClass` / `ReflectionMethod` metadata | yes | yes | yes | [#1936](https://github.com/PurHur/php-compiler/issues/1936) | php-cfg preserves `attrGroups`; compiler stores attribute class names on TYPE_DECLARE_CLASS / TYPE_DECLARE_METHOD; VM reflection reads from VM ClassEntry; JIT/AOT mirror class+method attribute tables into VMContext for reflection; Read path: `getAttributes()` count + `ReflectionAttribute::getName()`; no `newInstance()` or parameter attributes |
| WeakReference / WeakMap | yes | no | yes | [#3282](https://github.com/PurHur/php-compiler/issues/3282) | VM: WeakReference::create/get via indirect target slot; unset clears get immediately; GC-backed weak refs via WeakRefRegistry — referent collected by gc_collect_cycles() clears get(); WeakMap uses object-id string keys; entries removed when key object is collected; JIT may compile references but method bodies are VM-only |
| WeakReference / WeakMap | yes | no | yes | [#3282](https://github.com/PurHur/php-compiler/issues/3282) | VM: WeakReference::create/get via indirect target slot; unset clears get immediately; GC-backed weak refs via WeakRefRegistry — referent collected by gc_collect_cycles() clears get(); WeakMap uses object-id string keys; entries removed when key object is collected; JIT may compile references but method bodies are VM-only; VM-only lowering |
| DateTime / DateTimeZone OOP | yes | no | no | [#3072](https://github.com/PurHur/php-compiler/issues/3072) | VM builtins: DateTime::__construct/format/getTimestamp/setTimezone; DateTimeZone::__construct; Host PHP date extension for parsing/formatting; UTC and named timezone subset (php-src ext/date/php_datetime.c); JIT/AOT method bodies VM-only in phase 1; VM-only lowering |

_Syntax AOT column reflects `Runtime::MODE_AOT` compile probes unless a row pins AOT (e.g. native user-class link)._
Expand Down
8 changes: 6 additions & 2 deletions script/capability-syntax-lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ function syntaxRowDefinitions(): array
'construct' => 'Enum declarations `enum Foo: string { case Bar = \'x\'; }`',
'opcodes' => ['TYPE_DECLARE_ENUM', 'TYPE_DECLARE_CLASS_CONST', 'TYPE_CLASS_CONST_FETCH'],
'issue' => 1356,
'notes' => ['Backed enum case objects with `->name` / `->value`; string context coerces to backed scalar (#3518); `Foo::Bar` singleton fetch; `enum_exists` registry; `implements` metadata (#2299); static methods (#2299); `Enum::cases()` VM (#3308)'],
'probe' => 'enum Status: string { case Ok = \'ok\'; public static function tag(): string { return \'ok\'; } } echo Status::Ok; echo Status::tag();',
'notes' => [
'Backed enum case objects with `->name` / `->value`; string context coerces to backed scalar (#3518)',
'`Foo::Bar` singleton fetch; `enum_exists` registry; `implements` interface list + instance methods + `instanceof` (#3373)',
'static methods (#2299); `Enum::cases()` VM (#3308)',
],
'probe' => 'interface L { public function n(): string; } enum S: string implements L { case A = "a"; public function n(): string { return $this->name; } } echo S::A->n();',
],
[
'id' => 'instance_methods',
Expand Down
1 change: 1 addition & 0 deletions test/compliance/EnumVMTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static function providePHPTests(): \Generator
'enum_cases.phpt',
'enum_instanceof.phpt',
'enum_implements_metadata.phpt',
'enum_implements_interface.phpt',
'enum_static_method.phpt',
'unit_enum_basic.phpt',
'unit_enum_case_name.phpt',
Expand Down
26 changes: 26 additions & 0 deletions test/compliance/cases/language/enum_implements_interface.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Language: backed enum implements interface — instance methods + instanceof (#3373)
--FILE--
<?php
interface HasName {
public function label(): string;
}

enum Status: string implements HasName {
case Open = 'open';
case Closed = 'closed';

public function label(): string {
return $this->name;
}
}

echo Status::Open->label();
echo "\n";
echo Status::Open instanceof HasName ? '1' : '0';
echo "\n";
echo Status::Open;
--EXPECT--
Open
1
open
40 changes: 40 additions & 0 deletions test/unit/VmEnumImplementsInterfaceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

use PHPUnit\Framework\TestCase;

/** VM enum case objects with interface implementation (#3373). */
final class VmEnumImplementsInterfaceTest extends TestCase
{
public function testEnumCaseInstanceMethodAndInstanceof(): void
{
$code = <<<'PHP'
<?php
interface HasName {
public function label(): string;
}

enum Status: string implements HasName {
case Open = 'open';

public function label(): string {
return $this->name;
}
}

echo Status::Open->label();
echo Status::Open instanceof HasName ? '1' : '0';
echo Status::Open;
PHP;
$runtime = new Runtime();
$block = $runtime->parseAndCompile($code, 'enum_iface.php');
ob_start();
$runtime->run($block);
$output = ob_get_clean();

$this->assertSame('Open1open', $output);
}
}