"All clauses guarded" warning (25) now part of standard "non exhaustive" warning (8)#315
Conversation
|
A bit of history. The reason why the "all clauses guarded, bad style" warning was introduced (not by me) is that pedantic users objected to getting a "non exhaustive" warning for brilliant code such as Personally, I don't care much for pedantry. But do keep in mind that this change will be viewed as a regression by some. |
|
Could we arrange things so that: let f = function
| Some x when x -> foogives warning 8, whilst: let f = function
| Some x when x -> foo
| None when true -> foogives warning 25. That would seem to be in the spirit of the messages, and both the bug reports were complaining that they didn't get warning 8 even though the matches would still not have been exhaustive without the |
|
@lpw25 I don't understand what you suggest. Certainly you're not proposing to special case |
|
The point is as follows: In Xavier's example, the patterns without On the contrary, see the example in the mantis PRs 7059 and 6438: there it is the case that the clauses are clearly non-exhaustive, and users expect to see a non-exhaustivity warning. Given that the non-exhaustivity warning is more objective and more interesting and more precise (it gives a non-matched example) than the "when bad style" warning, I think it would be good to see the non-exhaustivity warning in priority. If showing both warnings is too difficult for some reason, then we could try always showing the non-exhaustivity warning when we can, and only showing the "when bad style" warning when the without-when version is exhaustive. (Luc says in PR#6438 that the exhaustivity information is already computed by first dropping the |
|
I'm starting to doubt my memory. Perhaps the controversial example was the following buggy variant of where no "not exhaustive" warning is displayed, even though it fails if At any rate, I agree that the "not exhaustive" warning should take precedence over the "all clauses guarded" one. |
|
@gasche, currently guarded clauses are just ignored in the exhaustiveness check, and this is the only sound approach. |
|
Indeed, I misread Luc's comment; exhaustiveness is not computed with when guards ignored, but after having removed all clauses that have a guard. This is very different. However I still think that you could improve on the current behaviour without computing exhaustivity twice (which would be a good way to compute both over- and under-approximations of guards coverage). The current algorithm is (purely guessing):
The present PR proposes the following instead:
With your constraint to call exhaustiveness only once, I would suggest the following:
Note that warning 8 already seems to be fairly clever in presence of some guarded clauses: it can tell whether one of the erased clause could have matched the proposed counter-example. I don't know how this is implemented (without checking exhaustiveness twice), but this aspect of the behaviour is good and need not change. |
|
As I mentioned in my answer, this is not sufficient. The fact the guarded clauses are exhaustive after taking their guard does not mean the pattern-matching is exhaustive, and if all cases do not appear at least twice, it is probably not exhaustive. |
|
I originally understood your remark as saying that my (or really Leo's) proposal was worse than what we currently have. If I understand correctly, you are saying that it is better, but that it could be improved further; with my last proposal, warning 25 is replaced by warning 8 in some cases, and you suggest to go even further by (reasonably) assuming that all guards may be either true or false for some values. Your criterion is, however, not completely correct. Consider: This pattern-matching is exhaustive while no pattern is repeated twice: each value is matched by two distinct patterns. So a more complete refinement would be "every guarded pattern is included in the union of the other patterns" (otherwise whenever the guard is false the corresponding values raise match failure), and I don't suggest implementing it. I don't think we need to enter the infinite bikeshedding hell of how to make this warning distinction perfect. The point is that today some users are surprised when they see the warning text of 25 instead of 8, and surprise is bad. If we can remove all cases where they are surprised, the problem is fixed. Finally, you also remark that, if people set 8 as an error, they probably want 25 as an error as well. I think that is a reasonable idea. Let's hear some counter-arguments first. One may argue that people that are careful about this would have set 25 as an error as well; but it's fair to assume they haven't realized this is a problem (for the same reason our users are surprised). One may argue that raising an error (instead of a warning) in more cases is going to break code; but we are already proposing to turn some 25 into 8, which is going to break code as well. So let me revise my proposition to take your remark into account -- bringing it closer to Alain's current change proposal.
|
|
Actually, your example precisely falls into my criterion. I've never said that the clause itself should be duplicated, but that all the values matched should be matched by at least two clauses, to have a chance of having something exhaustive. |
|
Sorry, this was "unprovable" of course. |
|
The counter-example says "Here is an example of a value that is not matched: ...", I don't think it would be misleading. But it is true that, in the cases we are discussing, adding all missing clauses still results in a warning (2.ii instead of 1.ii). |
|
Sorry, the discussion is getting too long to follow... Do we all agree that as soon as warning 8 is enabled, one should never get a runtime failure because of a incomplete pattern matching? Since we are not going to analyze guard expressions, we must check exhaustivity after excluding clauses with guards (as of today) to be on the safe side. The "all clause guarded" is perhaps a bad style worth reporting by tweaking the warning text, but it should certainly trigger the "non exhaustive" warning. Can participants to this discussion provide examples where the behavior implemented in the PR is not satisfactory? |
This is not satisfactory. I would like to see instead or even: This change of behaviour is precisely what the two Mantis tickets you linked to are asking for. (You and Jacques also interpret it, from a tooling perspective, thinking of situations where 8 is enabled but not 25, or where 8 is an error. This is also a valid concern and your change does improve things in this regard. But the users, at least for PR#6438, are actually asking for non-coverage counter-example(s).) Edit: below is a my proposal for a warning logic that adresses this issue:
|
|
Thanks, this is clearer. So you are not arguing against the merger of warning 8 and 25, only about improving the text to show a certain counter-example. I propose to accept the current PR and postpone the discussion. FWIW, I don't like the "binary" logic you propose: if some clauses are non-guarded, it would still be useful to report certain counter-examples (i.e. values which are not matched by any clause even without taking their guard into account); I guess the argument against this approach is that running the check twice can be costly. |
I'm not against this, but I suspect it corresponds to burying the issue.
The current implementation is already rather good when there is a mix of guarded and non-guarded clauses and I am not suggesting to change that. Indeed, calling coverage checking twice would be better in this case (to distinguish pattern In other words, the current compiler implements (2.i) and (2.ii) correctly. Your PR implements (1.i) (this arguably closes MPR#7059); I think we should implement (1.ii) as well (to close MPR#6438). |
|
Ok, understood. But is anyone willing to spend time on providing a more helpful text for a discouraged style anyway? Perhaps this is burying the issue, but I prefer to fix now what I consider to be a bug (not getting warning 8 but having a runtime failure) and to leave the improvement I don't care about for later and/or someone else. |
|
Btw, I was wondering if anyone would be opposed to turning warning 8 into an actual type-checking error? The idea that language features can implicitly raise exceptions is fine for a dynamically typed language, but it defeats the idea that statically well-typed program cannot go wrong. (I know there are other cases, such as recursive modules or circular lazy values, but they are more advanced stuff than pattern matching.) |
I am. Not that I would ever ignore it or turn it off, but because I think that type errors should be for genuine errors of soundness. I also don't think errors should be issued for things which cannot decidably be checked fully. |
|
There are also various cases where you know by not-match-local reasoning that a case or family of cases cannot be reached (because you caught them in a matching on the same value that dominates this match's control flow); some people use Note that you can use a final |
For me, that's the difference between static and dynamic type systems: both "prevent segfaults", but dynamic type systems do it by allowing themselves to raise (or return dummy results) on apparently harmless operations, while static type system require users to be explicit about "dynamic checks". |
A warning still requires users to be explicit. |
You mean, by explicitly ignoring the warning? But if the warning is not turned into an error, it is in practice invisible to many users, so I don't see how this can be qualified as an explicit action. |
My sympathy for users who ignore warnings is not particularly high. |
|
If people accept warnings in their code base (i.e. they don't turn them into errors), they will necessarily have their console filled of them quickly and will thus soon enough start ignoring them. I guess that what I don't understand is the notion of warning not turned into errors; except for polluting the console, I don't see the point. |
I agree, but warnings are still errors that you can turn on and off, as well as indicating (by the very fact you can turn them off) that they are not related to proper soundness. Warnings are also generally ignored in released packages, which is good for things which check conditions that are undecidable in general or difficult to predict. I would be disappointed if my working program (inevitably featuring GADTs) was broken by a change to the exhaustivity check in the compiler, but such things can be inevitable when trying to check such awkward conditions. |
They must be. There's nothing more annoying than packages failing to compile with a new release of the compiler because new warnings were introduced or tweaked since the last release of the package. |
… now part of the standard non-exhaustive warning (warning 8).
2df8657 to
8602717
Compare
"All clauses guarded" warning (25) now part of standard "non exhaustive" warning (8)
|
Reading again both #6438 and #7059, it seems to me that both reporters complain that warning 8 is not raised because of the guard, and that this is bad. So I've merged this PR. One could indeed refine the warning message when all clauses are guarded, but since this is considered bad style, I'm not sure it's worth the trouble. |
…stream PR 9876) (ocaml#315) On target architectures with 32 or more registers, a register was used to cache the value of the young_limit field of the domain state. This reduced the size and execution time of the code for inlined allocations. However, this usage is problematic with respect to polling for signals and to inter-domain communication in Multicore OCaml, because it is often not possible to change the value of the register when we change young_limit. So, the change to young_limit doesn't take effect immediately, only when the register is reloaded from young_limit. - Removes the caching of young_limit in a register from the ARM64, PowerPC and RISC-V ports. - Recycle the former "young limit" register, giving one more allocatable register Now that we have a unused callee-save register on ARM64, PowerPC, and RISC-V, make it available for register allocation. - Assorted cleanups in runtime/*.S and in asmcomp/*/proc.ml - ARM64: wrong register pressure limits for Iextcall There are only 7 callee-save integer registers (x19 to x25), not 10. Co-authored-by: Xavier Leroy <[email protected]>
…caml#315) On target architectures with 32 or more registers, a register was used to cache the value of the young_limit field of the domain state. This reduced the size and execution time of the code for inlined allocations. However, this usage is problematic with respect to polling for signals and to inter-domain communication in Multicore OCaml, because it is often not possible to change the value of the register when we change young_limit. So, the change to young_limit doesn't take effect immediately, only when the register is reloaded from young_limit. - Removes the caching of young_limit in a register from the ARM64, PowerPC and RISC-V ports. - Recycle the former "young limit" register, giving one more allocatable register Now that we have a unused callee-save register on ARM64, PowerPC, and RISC-V, make it available for register allocation. - Assorted cleanups in runtime/*.S and in asmcomp/*/proc.ml - ARM64: wrong register pressure limits for Iextcall There are only 7 callee-save integer registers (x19 to x25), not 10. Co-authored-by: Xavier Leroy <[email protected]>
23a7f73 flambda-backend: Fix some Debuginfo.t scopes in the frontend (ocaml#248) 33a04a6 flambda-backend: Attempt to shrink the heap before calling the assembler (ocaml#429) 8a36a16 flambda-backend: Fix to allow stage 2 builds in Flambda 2 -Oclassic mode (ocaml#442) d828db6 flambda-backend: Rename -no-extensions flag to -disable-all-extensions (ocaml#425) 68c39d5 flambda-backend: Fix mistake with extension records (ocaml#423) 423f312 flambda-backend: Refactor -extension and -standard flags (ocaml#398) 585e023 flambda-backend: Improved simplification of array operations (ocaml#384) faec6b1 flambda-backend: Typos (ocaml#407) 8914940 flambda-backend: Ensure allocations are initialised, even dead ones (ocaml#405) 6b58001 flambda-backend: Move compiler flag -dcfg out of ocaml/ subdirectory (ocaml#400) 4fd57cf flambda-backend: Use ghost loc for extension to avoid expressions with overlapping locations (ocaml#399) 8d993c5 flambda-backend: Let's fix instead of reverting flambda_backend_args (ocaml#396) d29b133 flambda-backend: Revert "Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382)" (ocaml#395) d0cda93 flambda-backend: Revert ocaml#373 (ocaml#393) 1c6eee1 flambda-backend: Fix "make check_all_arches" in ocaml/ subdirectory (ocaml#388) a7960dd flambda-backend: Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382) bf7b1a8 flambda-backend: List and Array Comprehensions (ocaml#147) f2547de flambda-backend: Compile more stdlib files with -O3 (ocaml#380) 3620c58 flambda-backend: Four small inliner fixes (ocaml#379) 2d165d2 flambda-backend: Regenerate ocaml/configure 3838b56 flambda-backend: Bump Menhir to version 20210419 (ocaml#362) 43c14d6 flambda-backend: Re-enable -flambda2-join-points (ocaml#374) 5cd2520 flambda-backend: Disable inlining of recursive functions by default (ocaml#372) e98b277 flambda-backend: Import ocaml#10736 (stack limit increases) (ocaml#373) 82c8086 flambda-backend: Use hooks for type tree and parse tree (ocaml#363) 33bbc93 flambda-backend: Fix parsecmm.mly in ocaml subdirectory (ocaml#357) 9650034 flambda-backend: Right-to-left evaluation of arguments of String.get and friends (ocaml#354) f7d3775 flambda-backend: Revert "Magic numbers" (ocaml#360) 0bd2fa6 flambda-backend: Add [@inline ready] attribute and remove [@inline hint] (not [@inlined hint]) (ocaml#351) cee74af flambda-backend: Ensure that functions are evaluated after their arguments (ocaml#353) 954be59 flambda-backend: Bootstrap dd5c299 flambda-backend: Change prefix of all magic numbers to avoid clashes with upstream. c2b1355 flambda-backend: Fix wrong shift generation in Cmm_helpers (ocaml#347) 739243b flambda-backend: Add flambda_oclassic attribute (ocaml#348) dc9b7fd flambda-backend: Only speculate during inlining if argument types have useful information (ocaml#343) aa190ec flambda-backend: Backport fix from PR#10719 (ocaml#342) c53a574 flambda-backend: Reduce max inlining depths at -O2 and -O3 (ocaml#334) a2493dc flambda-backend: Tweak error messages in Compenv. 1c7b580 flambda-backend: Change Name_abstraction to use a parameterized type (ocaml#326) 07e0918 flambda-backend: Save cfg to file (ocaml#257) 9427a8d flambda-backend: Make inlining parameters more aggressive (ocaml#332) fe0610f flambda-backend: Do not cache young_limit in a processor register (upstream PR 9876) (ocaml#315) 56f28b8 flambda-backend: Fix an overflow bug in major GC work computation (ocaml#310) 8e43a49 flambda-backend: Cmm invariants (port upstream PR 1400) (ocaml#258) e901f16 flambda-backend: Add attributes effects and coeffects (#18) aaa1cdb flambda-backend: Expose Flambda 2 flags via OCAMLPARAM (ocaml#304) 62db54f flambda-backend: Fix freshening substitutions 57231d2 flambda-backend: Evaluate signature substitutions lazily (upstream PR 10599) (ocaml#280) a1a07de flambda-backend: Keep Sys.opaque_identity in Cmm and Mach (port upstream PR 9412) (ocaml#238) faaf149 flambda-backend: Rename Un_cps -> To_cmm (ocaml#261) ecb0201 flambda-backend: Add "-dcfg" flag to ocamlopt (ocaml#254) 32ec58a flambda-backend: Bypass Simplify (ocaml#162) bd4ce4a flambda-backend: Revert "Semaphore without probes: dummy notes (ocaml#142)" (ocaml#242) c98530f flambda-backend: Semaphore without probes: dummy notes (ocaml#142) c9b6a04 flambda-backend: Remove hack for .depend from runtime/dune (ocaml#170) 6e5d4cf flambda-backend: Build and install Semaphore (ocaml#183) 924eb60 flambda-backend: Special constructor for %sys_argv primitive (ocaml#166) 2ac6334 flambda-backend: Build ocamldoc (ocaml#157) c6f7267 flambda-backend: Add -mbranches-within-32B to major_gc.c compilation (where supported) a99fdee flambda-backend: Merge pull request ocaml#10195 from stedolan/mark-prefetching bd72dcb flambda-backend: Prefetching optimisations for sweeping (ocaml#9934) 27fed7e flambda-backend: Add missing index param for Obj.field (ocaml#145) cd48b2f flambda-backend: Fix camlinternalOO at -O3 with Flambda 2 (ocaml#132) 9d85430 flambda-backend: Fix testsuite execution (ocaml#125) ac964ca flambda-backend: Comment out `[@inlined]` annotation. (ocaml#136) ad4afce flambda-backend: Fix magic numbers (test suite) (ocaml#135) 9b033c7 flambda-backend: Disable the comparison of bytecode programs (`ocamltest`) (ocaml#128) e650abd flambda-backend: Import flambda2 changes (`Asmpackager`) (ocaml#127) 14dcc38 flambda-backend: Fix error with Record_unboxed (bug in block kind patch) (ocaml#119) 2d35761 flambda-backend: Resurrect [@inline never] annotations in camlinternalMod (ocaml#121) f5985ad flambda-backend: Magic numbers for cmx and cmxa files (ocaml#118) 0e8b9f0 flambda-backend: Extend conditions to include flambda2 (ocaml#115) 99870c8 flambda-backend: Fix Translobj assertions for Flambda 2 (ocaml#112) 5106317 flambda-backend: Minor fix for "lazy" compilation in Matching with Flambda 2 (ocaml#110) dba922b flambda-backend: Oclassic/O2/O3 etc (ocaml#104) f88af3e flambda-backend: Wire in the remaining Flambda 2 flags (ocaml#103) 678d647 flambda-backend: Wire in the Flambda 2 inlining flags (ocaml#100) 1a8febb flambda-backend: Formatting of help text for some Flambda 2 options (ocaml#101) 9ae1c7a flambda-backend: First set of command-line flags for Flambda 2 (ocaml#98) bc0bc5e flambda-backend: Add config variables flambda_backend, flambda2 and probes (ocaml#99) efb8304 flambda-backend: Build our own ocamlobjinfo from tools/objinfo/ at the root (ocaml#95) d2cfaca flambda-backend: Add mutability annotations to Pfield etc. (ocaml#88) 5532555 flambda-backend: Lambda block kinds (ocaml#86) 0c597ba flambda-backend: Revert VERSION, etc. back to 4.12.0 (mostly reverts 822d0a0 from upstream 4.12) (ocaml#93) 037c3d0 flambda-backend: Float blocks 7a9d190 flambda-backend: Allow --enable-middle-end=flambda2 etc (ocaml#89) 9057474 flambda-backend: Root scanning fixes for Flambda 2 (ocaml#87) 08e02a3 flambda-backend: Ensure that Lifthenelse has a boolean-valued condition (ocaml#63) 77214b7 flambda-backend: Obj changes for Flambda 2 (ocaml#71) ecfdd72 flambda-backend: Cherry-pick 9432cfdadb043a191b414a2caece3e4f9bbc68b7 (ocaml#84) d1a4396 flambda-backend: Add a `returns` field to `Cmm.Cextcall` (ocaml#74) 575dff5 flambda-backend: CMM traps (ocaml#72) 8a87272 flambda-backend: Remove Obj.set_tag and Obj.truncate (ocaml#73) d9017ae flambda-backend: Merge pull request ocaml#80 from mshinwell/fb-backport-pr10205 3a4824e flambda-backend: Backport PR#10205 from upstream: Avoid overwriting closures while initialising recursive modules f31890e flambda-backend: Install missing headers of ocaml/runtime/caml (ocaml#77) 83516f8 flambda-backend: Apply node created for probe should not be annotated as tailcall (ocaml#76) bc430cb flambda-backend: Add Clflags.is_flambda2 (ocaml#62) ed87247 flambda-backend: Preallocation of blocks in Translmod for value let rec w/ flambda2 (ocaml#59) a4b04d5 flambda-backend: inline never on Gc.create_alarm (ocaml#56) cef0bb6 flambda-backend: Config.flambda2 (ocaml#58) ff0e4f7 flambda-backend: Pun labelled arguments with type constraint in function applications (ocaml#53) d72c5fb flambda-backend: Remove Cmm.memory_chunk.Double_u (ocaml#42) 9d34d99 flambda-backend: Install missing artifacts 10146f2 flambda-backend: Add ocamlcfg (ocaml#34) 819d38a flambda-backend: Use OC_CFLAGS, OC_CPPFLAGS, and SHAREDLIB_CFLAGS for foreign libs (#30) f98b564 flambda-backend: Pass -function-sections iff supported. (#29) e0eef5e flambda-backend: Bootstrap (#11 part 2) 17374b4 flambda-backend: Add [@@Builtin] attribute to Primitives (#11 part 1) 85127ad flambda-backend: Add builtin, effects and coeffects fields to Cextcall (#12) b670bcf flambda-backend: Replace tuple with record in Cextcall (#10) db451b5 flambda-backend: Speedups in Asmlink (#8) 2fe489d flambda-backend: Cherry-pick upstream PR#10184 from upstream, dynlink invariant removal (rev 3dc3cd7 upstream) d364bfa flambda-backend: Local patch against upstream: enable function sections in the Dune build 886b800 flambda-backend: Local patch against upstream: remove Raw_spacetime_lib (does not build with -m32) 1a7db7c flambda-backend: Local patch against upstream: make dune ignore ocamldoc/ directory e411dd3 flambda-backend: Local patch against upstream: remove ocaml/testsuite/tests/tool-caml-tex/ 1016d03 flambda-backend: Local patch against upstream: remove ocaml/dune-project and ocaml/ocaml-variants.opam 93785e3 flambda-backend: To upstream: export-dynamic for otherlibs/dynlink/ via the natdynlinkops files (still needs .gitignore + way of generating these files) 63db8c1 flambda-backend: To upstream: stop using -O3 in otherlibs/Makefile.otherlibs.common eb2f1ed flambda-backend: To upstream: stop using -O3 for dynlink/ 6682f8d flambda-backend: To upstream: use flambda_o3 attribute instead of -O3 in the Makefile for systhreads/ de197df flambda-backend: To upstream: renamed ocamltest_unix.xxx files for dune bf3773d flambda-backend: To upstream: dune build fixes (depends on previous to-upstream patches) 6fbc80e flambda-backend: To upstream: refactor otherlibs/dynlink/, removing byte/ and native/ 71a03ef flambda-backend: To upstream: fix to Ocaml_modifiers in ocamltest 686d6e3 flambda-backend: To upstream: fix dependency problem with Instruct c311155 flambda-backend: To upstream: remove threadUnix 52e6e78 flambda-backend: To upstream: stabilise filenames used in backtraces: stdlib/, otherlibs/systhreads/, toplevel/toploop.ml 7d08e0e flambda-backend: To upstream: use flambda_o3 attribute in stdlib 403b82e flambda-backend: To upstream: flambda_o3 attribute support (includes bootstrap) 65032b1 flambda-backend: To upstream: use nolabels attribute instead of -nolabels for otherlibs/unix/ f533fad flambda-backend: To upstream: remove Compflags, add attributes, etc. 49fc1b5 flambda-backend: To upstream: Add attributes and bootstrap compiler a4b9e0d flambda-backend: Already upstreamed: stdlib capitalisation patch 4c1c259 flambda-backend: ocaml#9748 from xclerc/share-ev_defname (cherry-pick 3e937fc) 00027c4 flambda-backend: permanent/default-to-best-fit (cherry-pick 64240fd) 2561dd9 flambda-backend: permanent/reraise-by-default (cherry-pick 50e9490) c0aa4f4 flambda-backend: permanent/gc-tuning (cherry-pick e9d6d2f) git-subtree-dir: ocaml git-subtree-split: 23a7f73
* Format page * Replace Lorem Ipsum in Learn page * Replace subtitle for Featured packages
See http://caml.inria.fr/mantis/view.php?id=7059, http://caml.inria.fr/mantis/view.php?id=6438 .
Previously, one could compile with warning 8 enabled and still get a runtime error because of a non-exhaustive warning (all clauses guarded raised warning 25 but not warning 8).
A non-consensual aspect of this PR might be that I keep using two different constructors in Warnings, both mapped to number 8. I don't see anything wrong with it, though.