Commit 781172d
committed
Fix isExhaustive pattern checking
Kudos to ChatGPT, which explains:
The bug is in the “record” branch of `isExhaustive`. Right now,
when the first column contains (only) records (i.e. `numSeen == 0` and
`extractRecordPatterns` succeeds), the code does this:
1. Collect all field names seen anywhere in the matrix (`baseRecord`).
2. For **each field name independently**, check exhaustiveness of that one
field (`specializeRowByRecordField fieldName`), then wrap the resulting
missing patterns back into a single-field record.
That is logically wrong for records: it’s checking **each field in
isolation** (like an OR), but exhaustiveness requires the **cartesian
product of fields** (like an AND across fields). So your first example
slips through because:
- for field `a`, both `True` and `False` appear somewhere
- for field `b`, both `True` and `False` appear somewhere
…but not all **combinations** appear.
This is already handled correctly by `simplify`: `Can.PVar _` becomes
`Anything`. So destructured names like `{ key = lKey }` do *not* introduce
additional constraints. That’s why the `Just { first = { key = lKey,
value = lValue }, rest }` pattern should not force enumeration of all
`key/value/rest` combinations—those are `Anything` patterns. The fix
below preserves that behavior.
---
Instead of iterating fields one-by-one with `specializeRowByRecordField`,
we must treat a record pattern as a product of its fields: build a
consistent “base” field set and specialize the matrix into a vector
of field-patterns (one per field) and recurse on that vector.
You already have the machinery for this in `isUseful`:
- it uses `collectRecordFieldsWithAnyPattern` to get a base map `{fieldName -> Anything}`
- then uses `specializeRowByRecord baseMap` to expand a record into `Map.elems specializedMap`
`isExhaustive` should do the same.
No other code changes are required.
---
Patterns:
- `{ a = False, b = True }`
- `{ a = True, b = False }`
After specialization by the base record `{a = _, b = _}`, the algorithm
checks exhaustiveness on the **2-column** matrix `[aPattern, bPattern]`
and will find missing rows such as:
- `{ a = False, b = False }`
- `{ a = True, b = True }`
So it correctly reports `Incomplete`.
Inside `Just { first = { key = lKey, value = lValue }, rest }` all
those `lKey/lValue/rest` are `PVar`, so `simplify` turns them into
`Anything`. That makes the record subpatterns maximally general, so
`Ctor Just [...]` covers all `Just _` cases. Together with `Nothing`,
the `Maybe` match is exhaustive, and the record-product recursion won’t
introduce spurious missing combinations.1 parent 2e61355 commit 781172d
1 file changed
+16
-38
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
271 | 272 | | |
272 | 273 | | |
273 | 274 | | |
| |||
430 | 431 | | |
431 | 432 | | |
432 | 433 | | |
433 | | - | |
434 | | - | |
435 | | - | |
436 | | - | |
437 | | - | |
438 | | - | |
439 | | - | |
440 | | - | |
441 | | - | |
442 | | - | |
443 | | - | |
444 | | - | |
445 | | - | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
450 | | - | |
451 | | - | |
452 | | - | |
453 | | - | |
454 | | - | |
455 | | - | |
456 | 434 | | |
457 | 435 | | |
458 | 436 | | |
| |||
0 commit comments