Matching compilation: fix a pessimization from #13076#13338
Matching compilation: fix a pessimization from #13076#13338gasche merged 4 commits intoocaml:trunkfrom
Conversation
|
In #13076 (comment) @ncik-roberts proposed two further testcases: type nothing = |
type t = A | B | C of nothing
let f : bool * t -> int = function
| true, A -> 3
| false, A -> 4
| _, B -> 5
| _, C _ -> .and type t =
| A of int
| B of string
| C of string
| D of string
let compare t1 t2 =
match t1, t2 with
| A i, A j -> Int.compare i j
| B l1, B l2 -> String.compare l1 l2
| C l1, C l2 -> String.compare l1 l2
| D l1, D l2 -> String.compare l1 l2
| A _, (B _ | C _ | D _ ) -> -1
| (B _ | C _ | D _ ), A _ -> 1
| B _, (C _ | D _) -> -1
| (C _ | D _), B _ -> 1
| C _, D _ -> -1
| D _, C _ -> 1My checks seem to indicate that both examples indeed are pessimized in the current trunk, and this pessimization is fixed by the present PR. Good news! But I'm not sure why the second example is affected, as it does not rely on type-checker totality information. (For the first example: knowing that empty types and not inhabited requires type-checker information similar to GADTs.) I think that what is going on in the second example is related to the way or-pattern compilation discards some parts of the default environment, as discussed in details in #13076 (comment) . |
a5f3f18 to
92ed7a9
Compare
ncik-roberts
left a comment
There was a problem hiding this comment.
Thanks for looking into this! Indeed, I checked all of the regression cases I found, and this appears to fix them. The code seems correct and clear to me.
|
Actually I notice a few things with polymorphic variants. These get let f x y =
match x, y with
| _, `Y1 -> 0
| `X1, `Y2 -> 1
| (`X2 | `X3), `Y3 -> 2
| `X1, `Y3
| `X2, `Y2
| `X3, _ -> 3
let check_results r1 r2 =
match r1 r2 with
| (Ok _ as r), _ | _, (Ok _ as r) -> r
| (Error `A as r), Error _
| Error _, (Error `A as r) -> r
| (Error `B as r), Error `B -> rI haven't looked into why yet (sorry, I'm timing out for the day soon). |
|
Thanks for the polymorphic-variants examples. I haven't looked at the root cause either, but my guess would be that it is similar to the situation analyzed in details in #13076 (comment) : or-patterns trim the default environment (instead of growing the context with negative information), and there are a few cases where, before #13076, the pattern-matching compiler could locally detect that a sub-matrix is Total because its default environment becomes empty. Thinking it, it may be possible to imitate this behavior now (with the global/current partiality split), by strengthening the current partiality into Total when the global partiality is also Total. |
|
The fix I had in mind appears to work. I pushed your examples in the testsuite, and the fix. This is an addition to the previous fix (there are now two separate fixes for two separate code-quality regressions in #13076), so it would need a re-review. |
| | { cases = []; args = [] } -> comp_exit ctx m.default | ||
| | { cases = []; args = [] } -> | ||
| begin match comp_exit partial ctx m.default with | ||
| | None -> fatal_error "Matching: impossible empty matrix in a Total match" |
There was a problem hiding this comment.
I'm not fond of leaving "fatal errors" in the compiler codebase, and even less fond of adding them. This one is in fact not new, it was present before #13076, it corresponds to the following case:
Line 3310 in 74a76cc
(The new fatal_error appears to be raised less often than the previous one: before comp_exit would fail unconditionally when the default environment is empty, and now we only fail when the match is known to be Total. But note that before #13076 there was an extra catch-all clause added to the default environment for partial matches, so partial matches would never see an empty environment. In other words, the conditions to raise the fatal error are in fact the same as before.)
ncik-roberts
left a comment
There was a problem hiding this comment.
I've reviewed the final two commits and confirmed they fix the issues I flagged.
lambda/matching.ml
Outdated
|
|
||
| When a pattern-matching is globally Total, a jump out of a given | ||
| submatrix may only target a default submatrix correspond to | ||
| a further split. When it is Total, some jumps may fail to match |
There was a problem hiding this comment.
"currently [Total]" in opposition to "globally [Total]"?
There was a problem hiding this comment.
I think that this was a typo / brain bug and I meant Partial. Thanks! Rebased.
4dd3c6a to
38ef9f1
Compare
(no behavior change)
… default This un-does a small pessimization of ocaml#13076, which was analyzed in details in ocaml#13076 (comment), but at the time was believed to not affect any program in the wild. Since then Nick Roberts found instances of this pattern involving or-patterns and polymorphic variants. Thanks to the new presentation of partiality information that distingues the "global" and "current" information, the pessimization is now easy to undo, as done in this commit.
38ef9f1 to
74bbc91
Compare
lpw25
left a comment
There was a problem hiding this comment.
Approving on the basis of @ncik-roberts review
|
Thanks! Merged. |
This PR, which sits on top of #13337, proposes a fix for the pessimization of pattern-matching compilation analyzed in #13076 (comment) . The fix is precisely along the lines proposed in that comment, to track the "global" (or toplevel) partiality of pattern-matching separately from the "current" (or local) partiality of the submatrix.