From fbaac125ae78f8ac97b6306e44297ce7adaea5bb Mon Sep 17 00:00:00 2001 From: yangweijie <917647288@qq.com> Date: Wed, 5 Aug 2026 14:02:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CompilerBase.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 8226e770..b285cfcc 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -138,6 +138,16 @@ class CompilerBase implements PropertyAccessContext use LoopVarOptimizer; use SsaPropOptimizer; + /** + * Variables (and parameters) within the currently compiled function that must + * be emitted as a runtime variant (php::Var). A variable is recorded here when + * it is assigned incompatible types at different points in the body — which is + * perfectly legal in dynamically typed PHP but would otherwise force a strict + * backend to either fatal ("Cannot re-assign") or silently miscompile. The set + * is populated by computeVariantVars() right before the function body is parsed. + */ + protected array $variantVars = []; + public const string DEFAULT_PHP_VERSION = '8.5'; protected const string NATIVE_PROPERTY_VALUE_VAR = 'var'; protected const string NATIVE_PROPERTY_VALUE_DYNAMIC = 'dynamic'; @@ -999,6 +1009,7 @@ class CompilerBase implements PropertyAccessContext $this->context = new FunctionContext(); $this->function = ''; $this->functionDef = null; + $this->variantVars = []; } protected function resetMethod(): void @@ -3946,6 +3957,33 @@ class CompilerBase implements PropertyAccessContext $this->fatalError($left, "Cannot re-assign $varName from `{$fromType}` to `{$toType}`"); } + /** + * Mirror of checkVarAssignExpr()'s compatibility rules without the fatal side + * effect. Returns true when a variable of type $existing may legally receive a + * value of type $new (used by computeVariantVars() to decide whether a variable + * must be promoted to a runtime variant). + */ + protected function areTypesAssignable(string $existing, string $new): bool + { + if ($existing === Type::VAR or $new === Type::VAR) { + return true; + } + if ($existing === Type::REF or $new === Type::REF) { + return true; + } + if ($existing === $new) { + return true; + } + if ($this->isNativeType($existing) and $this->isNativeType($new)) { + return true; + } + $bigTypes = [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT]; + if (in_array($existing, $bigTypes, true) or in_array($new, $bigTypes, true)) { + return true; + } + return false; + } + /** * Check a value against a composite PHP type when the value's static type * is precise enough to prove a mismatch. Composite declarations still use