Skip to content

Fix GH-17144: type inference narrowing on ZEND_FETCH_DIM_W#21674

Open
iliaal wants to merge 1 commit intophp:PHP-8.4from
iliaal:fix/gh-17144-fetch-dim-w-narrowing
Open

Fix GH-17144: type inference narrowing on ZEND_FETCH_DIM_W#21674
iliaal wants to merge 1 commit intophp:PHP-8.4from
iliaal:fix/gh-17144-fetch-dim-w-narrowing

Conversation

@iliaal
Copy link
Copy Markdown
Contributor

@iliaal iliaal commented Apr 8, 2026

Summary

FETCH_DIM_W stripped MAY_BE_ARRAY_EMPTY only inside a block guarded by key_type & (MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING). When the key operand's type widened across loop iterations (e.g. from MAY_BE_ARRAY to include scalar types), key_type went from 0 to non-zero, causing the flag to be stripped on the second pass but not the first. This violated monotonicity and triggered the narrowing assertion.

Strips MAY_BE_ARRAY_EMPTY for write opcodes (W, RW, LIST_W) regardless of key_type, since a dimension write makes the array non-empty.

Fixes #17144

Copy link
Copy Markdown
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution looks completely wrong.
The following PHP code doesn't modify $a (it throws exception) and $a is still empty.

$a = [];
$a[$a] = 1;

FETCH_DIM_W stripped MAY_BE_ARRAY_EMPTY when key_type had any valid
key bit set. As the key operand's type widened across iterations
(e.g. from MAY_BE_ARRAY to MAY_BE_ARRAY|MAY_BE_LONG), key_type
transitioned from 0 to non-zero, flipping the strip from inactive to
active. The resulting type lost MAY_BE_ARRAY_EMPTY mid-iteration,
tripping the narrowing assertion.

Stripping unconditionally is unsound: $a = []; $a[$a] = 1; throws
before the write, leaving the array empty. Strip only when the key
operand is guaranteed a valid array key, i.e. when op2 is IS_UNUSED
(append) or t2 contains no MAY_BE_ARRAY or MAY_BE_OBJECT bits. As t2
widens, this condition can only flip from true to false, so the
result type monotonically gains MAY_BE_ARRAY_EMPTY back rather than
losing it.

Test covers both the original narrowing reproducer and the invalid-key
throw path.

Closes phpGH-17144
@iliaal iliaal force-pushed the fix/gh-17144-fetch-dim-w-narrowing branch from cb5269e to f6f5b5b Compare April 13, 2026 23:52
@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented Apr 13, 2026

Confirmed: $a = []; $a[$a] = 1; throws before the write, so unconditional stripping was wrong. Pushed a corrected version that strips MAY_BE_ARRAY_EMPTY only when the key is guaranteed valid: either op2 is IS_UNUSED (append) or t2 has no MAY_BE_ARRAY|MAY_BE_OBJECT bits. Once t2 gains ARRAY or OBJECT, the strip stays inactive, so the result type can only gain MAY_BE_ARRAY_EMPTY across iterations, never lose it. Test now covers both the original narrowing reproducer and the invalid-key throw path.

@iliaal iliaal requested a review from dstogov April 14, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants