Skip to content

Commit 9bc886c

Browse files
committed
Update tasks
1 parent d2ff21a commit 9bc886c

2 files changed

Lines changed: 83 additions & 3 deletions

File tree

docs/todo.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ contributor even though it's short.
3737
| B80 | [`max()`/`min()` argument-dependent return type is a malformed union](todo/bugs.md#b80-maxmin-argument-dependent-return-type-is-a-malformed-union) | Medium | Medium |
3838
| B78 | [A standalone `@var` cast above `return` is ignored](todo/bugs.md#b78-a-standalone-var-cast-above-return-is-ignored) | Medium | Medium |
3939
| B82 | [A nullsafe chain compared `===` to a non-nullable value does not narrow the receiver](todo/bugs.md#b82-a-nullsafe-chain-compared--to-a-non-nullable-value-does-not-narrow-the-receiver) | Medium | Medium |
40+
| B83 | [A `match (true)` arm's condition does not narrow inside the arm's result](todo/bugs.md#b83-a-match-true-arms-condition-does-not-narrow-inside-the-arms-result) | Medium | Medium |
41+
| B84 | [Return-position compatibility ignores an array shape's value types](todo/bugs.md#b84-return-position-compatibility-ignores-an-array-shapes-value-types) | Medium | Medium |
4042
| B79 | [`array_filter()` without a callback keeps `null` on values that share the array with a `?bool`](todo/bugs.md#b79-array_filter-without-a-callback-keeps-null-on-values-that-share-the-array-with-a-bool) | Low-Medium | Medium |
4143
| B81 | [Foreach element extraction widens `false` to `bool`](todo/bugs.md#b81-foreach-element-extraction-widens-false-to-bool) | Low-Medium | Medium |
4244
| B76 | [Blade variables typed from a component class are immune to condition narrowing](todo/bugs.md#b76-blade-variables-typed-from-a-component-class-are-immune-to-condition-narrowing) | Medium | Medium-High |
4345
| B77 | [A foreach over a proven non-empty array still merges the zero-iteration path](todo/bugs.md#b77-a-foreach-over-a-proven-non-empty-array-still-merges-the-zero-iteration-path) | Medium | Medium-High |
4446
| B75 | [A dim-write to the foreach value variable leaks through the loop back-edge](todo/bugs.md#b75-a-dim-write-to-the-foreach-value-variable-leaks-through-the-loop-back-edge) | Medium | High |
47+
| T32 | [Audit `is_type_compatible`'s MAYBE escape hatches for core-engine gaps](todo/type-inference.md#t32-audit-is_type_compatibles-maybe-escape-hatches-for-core-engine-gaps) | Medium | High |
4548
| | **Release 0.10.0** | | |
4649

4750
## Sprint 7 — 1.0 release & IDE extensions
@@ -95,7 +98,6 @@ unlikely to move the needle for most users.
9598
| T3 | [Property hooks (PHP 8.4)](todo/type-inference.md#t3-property-hooks-php-84) | Medium | Medium-High |
9699
| T34 | [`static::CONST` over-narrows to the declaring class's value](todo/type-inference.md#t34-staticconst-over-narrows-to-the-declaring-classs-value) | Medium | Medium-High |
97100
| T29 | [Definite vs possible variable existence tracking](todo/type-inference.md#t29-definite-vs-possible-variable-existence-tracking) | Medium | High |
98-
| T32 | [Audit `is_type_compatible`'s MAYBE escape hatches for core-engine gaps](todo/type-inference.md#t32-audit-is_type_compatibles-maybe-escape-hatches-for-core-engine-gaps) | Medium | High |
99101
| T30 | [Literal type collapse limit](todo/type-inference.md#t30-literal-type-collapse-limit) | Low-Medium | Medium |
100102
| T40 | [`pathinfo()` returns a shape or a string depending on the flags argument](todo/type-inference.md#t40-pathinfo-returns-a-shape-or-a-string-depending-on-the-flags-argument) | Low-Medium | Medium |
101103
| T26 | [Globbed constant unions (`Foo::BAR_*`)](todo/type-inference.md#t26-globbed-constant-unions-foobar_) | Low-Medium | Medium |

docs/todo/bugs.md

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,56 @@ was patched in the sample sources. Every entry was isolated in a
2020
scratch repro and bisected to the minimal trigger shown in its code
2121
block; several sit at the same source sites as fixed bugs from the
2222
previous sweep (B50, B54, B59, B62), where the coarse defect was fixed
23-
and a finer one behind it became visible.
23+
and a finer one behind it became visible. **B83** was filed the same
24+
day from the follow-up re-run at `c618c8aa`, whose compatibility
25+
tightening surfaced one previously-swallowed site, and **B84** from a
26+
probe at `66a524bc` showing the return-position side of that
27+
tightening is still missing.
2428

2529
## Crashes
2630

2731
No outstanding items.
2832

2933
## Type comparison
3034

31-
No outstanding items.
35+
### B84. Return-position compatibility ignores an array shape's value types
36+
37+
**Impact: Medium · Complexity: Medium**
38+
39+
```php
40+
/** @return array<string, int> */
41+
function bad(): array {
42+
return ['a' => 'x']; // not reported
43+
}
44+
45+
/** @param array<string, int> $m */
46+
function takesIntMap(array $m): void {}
47+
48+
function alsoBad(): void {
49+
takesIntMap(['a' => 'x']); // reported, as expected
50+
}
51+
```
52+
53+
Needs investigation: `type_mismatch_argument` correctly reports an
54+
array shape whose values do not satisfy the declared map or list value
55+
type (`array{a: 'x'}` vs `array<string, int>`, `array{'x', 'y'}` vs
56+
`list<int>`), but `type_mismatch_return` accepts the identical
57+
mismatch silently. The return side is not skipping shapes entirely —
58+
a nullability mismatch in a shape value (`array{a: ?bool}` vs
59+
`array<string, int>`) is reported in return position — so the two
60+
diagnostics are reaching different verdicts for the same shape-vs-map
61+
comparison somewhere below the nullability check. `array<string,
62+
never>` as the declared type is the extreme case: any all-optional-keys
63+
shape (e.g. an `array_filter()` result) passes against it, which is
64+
what kept masking scratch probes during the 2026-08-15 sweeps.
65+
66+
Found by probe at `66a524bc`, after the argument-side tightening
67+
landed; no sample-project site currently hits it.
68+
69+
**Fix:** find where the return-position compatibility path diverges
70+
from the argument-position path for shape-to-map value checks and
71+
unify them; the recently tightened argument behaviour is the correct
72+
one.
3273

3374
## Standard-library return types
3475

@@ -264,6 +305,43 @@ half was B54 in the previous sweep.
264305
type excludes `null`, narrow `$x` to non-null (and mirror for `!==` in
265306
the false branch).
266307

308+
### B83. A `match (true)` arm's condition does not narrow inside the arm's result
309+
310+
**Impact: Medium · Complexity: Medium**
311+
312+
```php
313+
/** @param list<int|string> $args */
314+
function takesList(array $args): void {}
315+
316+
function label(?int $buy, ?int $pay, string $kind): void {
317+
$textArgs = match (true) {
318+
$kind === 'xy' && $buy !== null && $pay !== null => [$buy, $pay],
319+
default => [],
320+
};
321+
takesList($textArgs); // reported: array{?int, ?int} does not satisfy list<int|string>
322+
}
323+
```
324+
325+
The `!== null` conjuncts of a `match (true)` arm's condition prove the
326+
values non-null within that arm's result expression, exactly as the
327+
equivalent `if` statement does — and the `if` form narrows correctly.
328+
Inside a match arm nothing narrows: plain variables and property reads
329+
both keep their `null` arms.
330+
331+
Filed 2026-08-15, after the evening sweep: the argument-compatibility
332+
tightening in `c618c8aa` surfaced it — the resulting shape mismatch
333+
was previously swallowed by the compatibility leniency that T32 tracks
334+
(the argument side has since been tightened further; the return-side
335+
remainder is B84).
336+
337+
Sample site: `luxplus-website app/Contexts/Api/Resources/ProductResource.php:210`
338+
(discount label `$textArgs` built from `?int` properties the arm
339+
condition null-checks).
340+
341+
**Fix:** evaluate each `match (true)` arm's result expression under
342+
the same condition-derived scope state the `if` evaluator would build
343+
from that arm's condition.
344+
267345
## Symbol resolution
268346

269347
No outstanding items.

0 commit comments

Comments
 (0)