Summary
Variable resolution can re-enter the exact same query while constructing the top-level scope used by PHP global declarations. The relevant path is still present on current main in src/type_engine/variable/resolution.rs.
The cycle is:
resolve_variable_types starts a forward walk.
resolve_variable_in_statements sees global in the file and starts a top-level scope walk.
- Resolving an RHS call argument requests the same variable resolution.
- That request starts another top-level scope walk, repeating the cycle.
Minimal fixture
<?php
function capture($input) {
global $shared;
$shared = transform($input);
}
$arg = source_value();
$old_global = factory($arg);
$value = consume($old_global);
$value->method();
A semantic consumer resolving the receiver of $value->method() reaches the cycle. The important shape is a file containing global plus top-level call arguments that require variable resolution.
Expected behavior
Resolution should terminate without disabling valid global inference. A re-entrant query cannot contribute information while its outer invocation is still incomplete, so only that exact cycle should be cut.
Possible implementation
Use thread-local RAII guards to track active resolution work:
resolve(key):
if key is already active:
return no additional types
mark key active
defer unmark key
perform resolution
The resolution key should include source identity, variable name, cursor offset, and current class. Top-level-scope construction needs a separate source-level guard because it can recursively initiate another resolution before the original scope is complete.
The guard must be ownership-aware: rejecting a nested acquisition must not remove the outer acquisition when the rejected guard is dropped.
Acceptance criteria
- The fixture terminates without stack growth or unbounded work.
- Existing
global member resolution remains intact.
- Unrelated variables and cursor positions are not suppressed.
- Guard state is released after normal return and unwinding.
- Repeated resolution on the same thread continues to work.
Summary
Variable resolution can re-enter the exact same query while constructing the top-level scope used by PHP
globaldeclarations. The relevant path is still present on currentmaininsrc/type_engine/variable/resolution.rs.The cycle is:
resolve_variable_typesstarts a forward walk.resolve_variable_in_statementsseesglobalin the file and starts a top-level scope walk.Minimal fixture
A semantic consumer resolving the receiver of
$value->method()reaches the cycle. The important shape is a file containingglobalplus top-level call arguments that require variable resolution.Expected behavior
Resolution should terminate without disabling valid
globalinference. A re-entrant query cannot contribute information while its outer invocation is still incomplete, so only that exact cycle should be cut.Possible implementation
Use thread-local RAII guards to track active resolution work:
The resolution key should include source identity, variable name, cursor offset, and current class. Top-level-scope construction needs a separate source-level guard because it can recursively initiate another resolution before the original scope is complete.
The guard must be ownership-aware: rejecting a nested acquisition must not remove the outer acquisition when the rejected guard is dropped.
Acceptance criteria
globalmember resolution remains intact.