diff --git a/src/Php/Entity/ClassDef.php b/src/Php/Entity/ClassDef.php index 9af717da..1698bead 100644 --- a/src/Php/Entity/ClassDef.php +++ b/src/Php/Entity/ClassDef.php @@ -32,6 +32,17 @@ class ClassDef extends ClassLikeDef public string $extends = ''; public bool $requireCtor = false; public bool $enum = false; + + /** + * Backing type for backed enums ('int' or 'string'), null for pure enums. + */ + public ?string $enumBackingType = null; + + /** + * Enum cases: case name => backing value (int/string for backed enums, null for pure enums). + * @var array + */ + public array $enumCases = []; public ?Trait_ $trait = null; /** diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index f2725fd6..39e46f17 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -438,6 +438,9 @@ class Preprocessor extends CompilerBase if ($class instanceof Node\Stmt\Enum_) { $this->classDef->enum = true; + if ($class->scalarType !== null) { + $this->classDef->enumBackingType = $class->scalarType->name; + } } if (!$class instanceof Node\Stmt\Trait_) { $this->classDef->implements = $this->parseImplements($class->implements); @@ -464,7 +467,10 @@ class Preprocessor extends CompilerBase $this->prepareTraitUse($v); break; case 'Stmt_Nop': + break; case 'Stmt_EnumCase': + $caseName = $this->parseIdentifier($v->name); + $this->classDef->enumCases[$caseName] = $v->expr?->value; break; case 'Stmt_ClassMethod': $this->prepareClassMethod($v, $class); diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 82c5c432..bbb79438 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -1518,6 +1518,17 @@ CODE; return $this->genCValue(constant($constName)); } } + // Resolve enum case references. Enum cases are runtime objects; their + // actual values in class constant arrays are set at runtime by + // genClassArrayConstants() via php::getEnumCase(). Return the backing + // value as a placeholder so gen_stub.php's ConstExprEvaluator can complete. + if ($this->hasClass($class)) { + $classDef = $this->getClass($class); + if ($classDef->enum && array_key_exists($name, $classDef->enumCases)) { + $caseValue = $classDef->enumCases[$name]; + return $this->genCValue($caseValue ?? $name); + } + } $this->fatalError($expr, "Class constant `{$class}::{$name}` not found"); } diff --git a/tests/aot/enum/3.phpt b/tests/aot/enum/3.phpt new file mode 100644 index 00000000..773b2b53 --- /dev/null +++ b/tests/aot/enum/3.phpt @@ -0,0 +1,41 @@ +--TEST-- +enum 2 +--FILE-- + ['d' => '', 't' => T::S], + '2' => ['d' => 1, 't' => T::I], + '3' => ['d' => 123, 't' => T::S], + ]; + + public static function E(): array + { + return self::F; + } +} + +function main() +{ + foreach (TestSpa::E() as $n => $f) { + + $ok = match ($f['t']) { + T::I => is_int($f['d']), + T::S => is_string($f['d']), + }; + + echo $ok? " pass {$n} => [{$f['d']}, {$f['t']->value}]\n": " fail {$n} 元组非法\n"; + } +} +?> +--EXPECT-- +pass 1 => [, string] + pass 2 => [1, int] + fail 3 元组非法 \ No newline at end of file diff --git a/tests/aot/enum/4.phpt b/tests/aot/enum/4.phpt new file mode 100644 index 00000000..9436eb43 --- /dev/null +++ b/tests/aot/enum/4.phpt @@ -0,0 +1,30 @@ +--TEST-- +enum 2 +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + enum(TestEnum::FOO) + [1]=> + enum(TestEnum::BAR) +} \ No newline at end of file diff --git a/tests/aot/enum/5.phpt b/tests/aot/enum/5.phpt new file mode 100644 index 00000000..7e30725a --- /dev/null +++ b/tests/aot/enum/5.phpt @@ -0,0 +1,31 @@ +--TEST-- +enum 2 +--FILE-- +value); +} +?> +--EXPECT-- +array(2) { + [0]=> + enum(TestEnum::FOO) + [1]=> + enum(TestEnum::BAR) +} +enum(TestEnum::FOO) +string(3) "foo" \ No newline at end of file