From ba6e654e7d37b3ea40a39afc6a26c549ce916147 Mon Sep 17 00:00:00 2001 From: KorsarOfficial Date: Thu, 19 Mar 2026 00:53:46 +0400 Subject: [PATCH] fix: call mark_as_used() for classes referenced only in instanceof (#68) When a class appeared only in an `instanceof` expression and was never instantiated anywhere else, the code-gen pass tried to include its generated header but the header had never been written, causing a crash. The fix mirrors the identical pattern already applied to `op_catch`: call `mark_as_used()` so the class header gets generated even when the class is never used as a value. Also adds two regression tests: - tests/phpt/interfaces/135_instanceof_unused_class.php - tests/phpt/interfaces/136_instanceof_only_in_condition.php --- compiler/pipes/calc-func-dep.cpp | 5 ++++ .../135_instanceof_unused_class.php | 22 ++++++++++++++ .../136_instanceof_only_in_condition.php | 30 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 tests/phpt/interfaces/135_instanceof_unused_class.php create mode 100644 tests/phpt/interfaces/136_instanceof_only_in_condition.php diff --git a/compiler/pipes/calc-func-dep.cpp b/compiler/pipes/calc-func-dep.cpp index b6adbfa017..5bfdeb7f88 100644 --- a/compiler/pipes/calc-func-dep.cpp +++ b/compiler/pipes/calc-func-dep.cpp @@ -33,6 +33,11 @@ VertexPtr CalcFuncDepPass::on_enter_vertex(VertexPtr vertex) { } if (auto instanceof = vertex.try_as()) { + // mark_as_used() is required here for the same reason as in op_catch below: + // a class used only in instanceof may never be instantiated elsewhere, + // so without this call the code-gen pass would crash trying to include + // a header that was never generated (see #68) + instanceof->derived_class->mark_as_used(); current_function->class_dep.insert(instanceof->derived_class); } diff --git a/tests/phpt/interfaces/135_instanceof_unused_class.php b/tests/phpt/interfaces/135_instanceof_unused_class.php new file mode 100644 index 0000000000..2f064eb3c5 --- /dev/null +++ b/tests/phpt/interfaces/135_instanceof_unused_class.php @@ -0,0 +1,22 @@ +@ok +radius = $r; } +} + +class Square implements Shape { + public float $side; + public function __construct(float $s) { $this->side = $s; } +} + +// Triangle is declared but never instantiated — only appears in instanceof. +class Triangle implements Shape {} + +function describe(Shape $s): string { + if ($s instanceof Circle) return 'circle'; + if ($s instanceof Square) return 'square'; + if ($s instanceof Triangle) return 'triangle'; // Triangle: only in instanceof + return 'unknown'; +} + +var_dump(describe(new Circle(1.0))); // string(6) "circle" +var_dump(describe(new Square(2.0))); // string(6) "square"