Warning on fragile literals in constructor argument patterns#254
Warning on fragile literals in constructor argument patterns#254alainfrisch wants to merge 5 commits intoocaml:trunkfrom
Conversation
…arning when the argument is matched against a constant literal.
…meaningful for those taking a string argument, but harmless for others).
|
Thanks for working on this! I'm not fond of the name "semi-opaque" which suggests a semantic meaning (while the behavior is very heuristic and under-specified). What about a more explicit The fact that, if I understand correctly, it only applies to single-argument constructors so far is a bit hackish. Would it be unreasonably hard to annotate the constructor argument instead? (Also as always it would be nice to have this feature exercised somewhere in the testsuite.) |
|
Small question, would a warning be emitted in the following case? type failure = Case1 | Case2
exception Error of failure
try
<some code>
with
| Error Case1 -> ()
| Error Case2 -> ()(EDIT: assuming the |
|
@gasche : no problem for changing the name (I don't expect it to be widely used by user code anyway). Annotating arguments is more tricky since the type-checker does not keep track of attribute on type expressions (nor on expressions, patterns, etc), but only on "declarations" (including those on constructors as a whole). It wouldn't be too hard to detect the attribute on each argument and keep this information in the constructor description (perhaps by synthesizing an attribute whose payload identifies the argument number, to avoid changing the data structure for such a rare case) for later inspection by the type-checker (when analyzing the constructor pattern). But I don't think this is worth the trouble. What could be useful and easier would be to trigger the warning as soon as a constant appear under the constructor pattern. So a constructor such as Assert_failure could be annotated and this would trigger the warning when any of the argument is matched with a constant. |
|
@Gbury : no currently, the warning is only triggered when the argument pattern is a constant literal (i.e. basic values only: string, int, ...). |
|
On Mon, 5 Oct 2015 08:19:11 -0700, Alain Frisch wrote:
How about “ocaml.informational_strings”? How about reusing the “Fragile pattern matching” warning (expanding its definition)?
That could be useful. |
|
@Chris00 the idea of reusing "fragile pattern matching" is clever, but it is rather problematic to extend the application area of a warning a posteriori (because users like to assume that, if a particular warning is not raised by their code today, then they can safely mark it as an error -- except for "deprecated" blah which extends its semantics a bit too often, and I suspect that even that is a mistake). @alainfrisch Sorry for being unclear: I was assuming that annotating each argument would, indeed, unreasonably increase the complexity of the patch, but I thought that maybe there was a hidden miracle (one never knows). I agree raising if one of the arguments is a constant literal (not a constant variant constructor) is a good idea. |
Do you have evidence of it, i.e. users complaining about the same warning codes producing more warnings with a new release? |
|
If we avoid warning extension, we can provide a strong guarantee that a code that is warning-free today under a given set of warning will remain warning-free tomorrow with the same set of warnings. There is evidence that users are interested (see PR#6873), and this cannot work if we change extend the warning area of existing warnings -- or at least we would need to delimit clearly which warnings are "stable" and which are not. |
|
The issue is that many warnings are not formally defined, and are either based on "implementation details" of the type checker or on heuristics. I don't see this as fundamentally different from warnings produced by C compilers based on their optimization levels (more aggressive optimizations can uncover more defects, and newer versions of the compiler can become better). |
Definitely! Our development versions of Frama-C are compiled with
At the risk of sounding picky, this is a pretty terrible choice of warning to expand. I have the impression that this warning is not often active, because it is too burdensome. Few people would benefit from the new diagnostics. |
I don't have an opinion on the point in case, but I'd just like to mention that since you choose to compile with
Yes. It should simply be enabled by default (which will lead to the same problem for you and your As I already said a lot of times I think that the notion of activable warnings should be killed as it leads to programmers having a different view of the language. I personally never, ever tweak warnings in my project and hope that dev team actually designs me a good default language rather than solve design disputes through the addition of activable warnings. In the rare cases were unwarranted warnings should be disabled this should be done via attributes on particular expressions (iirc this is now possible). Note that this doesn't mean that the non-default warnings are not useful but I think that some of them should simply be provided under the form of a separate (and maybe more powerful) static analysis tool or linter. |
No, you can explicitly disable the "default warning set" (
I think it is important to accept relativism here. You have a very personal development methodology with very reactive changes throughout a small, personally-crafted and gardened codebase. This approach may not be applicable to other development methodologies, and a lot of people have the inverse problem of have a codebase too large for them to easily apply style changes as they get proposed/warned upstream. We can, and I think we should, cater for both scenarios. |
This new thing has nothing to do with deprecation, so you shouldn't reuse this warning (and the name of the annotation shouldn't include the word Likewise, this new check is only very loosely related to the notion of fragile pattern-matching, so you shouldn't reuse that either. I'm surprised that you, of all people, would hesitate to add a new warning :-)
I get hurt by it every time. Consider this a complaint from a user.
You're committing a variant of the Valhalla fallacy. Just because the current situation is not perfect, doesn't mean we should feel free to make it worse. In this case, I think we want to break the old code explicitly, because otherwise it's going to break silently when we change the strings, but in most other cases we should strive for compatibility. |
Well that's a broken methodology then. Warnings are not introduced for the sake of annoying people, they are introduced to show that your code base has a problem or will become problematic in the future. If your methodology ignores that I wouldn't trust you in producing quality software in the long run. Besides I rely on default sets of warnings because I believe in modularity which means that I have dozens of independent projects and I can't just spent my time tweaking them on each project, if you want to scale modularity you need to be able to rely on good defaults from the base infrastructure. Note that this is not a "personal development methodology", if you take the MirageOS project they also have in the hundreth of different independent packages to manage and suffer from the same problems. |
@dbuenzli oh come on, is it really so hard to write in a Of course if you enable |
|
Le jeudi, 8 octobre 2015 à 14:40, Simon Cruanes a écrit :
Yes — and furthermore how do I update the set in my dozens of repos when new OCaml versions are out with interesting new warnings ? Manually ? No thanks. How do I discover which set of warnings is the right one ? Sorry I don't have the time for that. And looking at this line my only reaction is, what a terrible user interface, what I'm suppose to infer is being checked by this cryptic line ? If at least there were designed classes or levels of warnings rather than this insane micro management interface. Good design relies on good defaults. I want to work with a language that is designed, i.e. where designers make well thought out choices to liberate me of having to perform these. Daniel |
|
IMO this should be a new warning, and it should be turned on by default. If we're going to be changing the strings returned by exceptions (which I think is fair), it should definitely be a warning, if not an error, to match on said strings. All projects doing this should be updated. |
|
(I have some design drafts in my garage for a better user-interface where warnings have a name. Later.) |
|
So, can anyone suggest a better name for the attribute? Damien doesn't like the deprecated part of Alternatively, we could get rid of the user-facing attribute altogether, since the most important goal is to get the warning on the built-in exception constructors (this can be dealt with an internal attribute). |
|
|
|
How about |
|
What about Unrelated remark: it is possible to silence the warning by rewriting |
Yes, this works. |
|
I quite like Nevertheless, both options inform on a potential ulterior problem without expanding on the cause of this problem. Maybe the attribute should explain that the value of the parameters should be not be relied upon because there are partially arbitrary and are therefore bound to wiggle around their current values. In this case, a name like |
|
I prefer |
|
How about something along the lines of |
|
|
Then |
|
|
|
|
|
Yes. |
I think this is the winner. I've applied the renaming and also introduced a new warning. |
|
Committed to trunk, rev 16502. |
|
Following #379, I have regrets about the current state of the this PR. Due to implementation limitations, we only support constructors with a single argument, but using the attribute on constructors with several arguments will just silently make it meaningless and not warn the user in any way. This is very bad usability. At the very least it would be nice to have an "implementation limitation" warning where the attribute is misplaced. However, this is a bit problematic in terms of backward-compatibility: if we lift the single-argument limitation in the future, then people will be able to write (correct) that does not warn for their OCaml version, but warns on 4.03, and this is always a bit painful. I think the real fix would be to have an implementation that is not half-baked: pose the attribute on constructor arguments, and warn if the corresponding argument is a constant literal. If we were still in the proposal discussion stage, I would now be of the opinion to not merge it until this more complete implementation is available. I'm not sure what's best at this point, but I'm considering proposing to postpone the feature until after the release. |
|
Let's not spend too much effort on this. The proper solution is to use proper types for arguments instead of generic strings/ints to carry error payload (for instance an abstract type, with a "to_string" function). The problem is that it is too late to change the definition of existing exceptions in the stdlib, hence this proposal. I would be perfectly ok with dropping support for adding the attribute on user-defined constructors, and keeping it only on predefined exceptions, if this can avoid over-engineering this stuff. I guess that most third-party libraries avoid the pitfall (or are easier to fix). |
* Use Format to print Cfg instructions Extends the interface to use Format, but does not change the formatting, which leaves much to be desired and will be improved later. Add functions [dump_basic] and [dump_terminator] to implement "-dcfg" compiler flag. Keep the functions [print_basic] and [print_terminator] for printing in dot format to a file. * Add -dcfg compiler flag to ocamlopt * Update asmgen to dump cfg * Fix asmgen tests * Keep preserve_orig_labels=true * Fix formatting
* Use Format to print Cfg instructions Extends the interface to use Format, but does not change the formatting, which leaves much to be desired and will be improved later. Add functions [dump_basic] and [dump_terminator] to implement "-dcfg" compiler flag. Keep the functions [print_basic] and [print_terminator] for printing in dot format to a file. * Add -dcfg compiler flag to ocamlopt * Update asmgen to dump cfg * Fix asmgen tests * Keep preserve_orig_labels=true * Fix formatting
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
* Make the books publication date and cover non-optional * Sort the books by reversed publication date
Some constructors, and most notably prdefined exceptions Invalid_arg/Failure/Sys_error, have a string argument which is mostly for information purpose, and we want to keep the freedom of changing the concrete strings later. This PR arranges so that when a constructor is marked with the
ocaml.semi_opaque(or justsemi_opaque) attribute, patterns that match directly on the argument with a constant literal (usually a string) will trigger a warning. All predefined exceptions are marked with this attribute implicitly, but it is also available for use by regular code.I've re-used the "deprecated" warning for now.
The notion of matching with a constant literal is interpreted strictly; an or-pattern, an alias-pattern, or constants under tuples/multiple arguments/... will not raise the warning.
(To be clear: the intent is not to encourage the current style of using generic exception constructors instead of more specific ones, nor to encourage the use of exceptions to report non-exceptional conditions. But since the stdlib and other libraries do make use of Invalid_arg/Failure, we want to discourage users to match on string literals.)