Conversation
Audit the egglog crate (./src) for panic!/unwrap/expect/todo!/unreachable! sites reachable from user input and convert them into recoverable errors so malformed or edge-case programs report an error instead of aborting. Highlights: - Malformed sort-constructor declarations (e.g. `(sort S (Vec))`, `(sort S (UnstableFn))`) now return `TypeError::BadPresortArguments`; a real span is threaded through `Presort::make_sort`. - Several primitives became partial (return no result instead of panicking): out-of-bounds/negative `vec-set`/`vec-remove`, empty `multiset-pick`, count overflow in multiset ops, and `bigint <<`/`>>`, `bigrat`, `log`, `cbrt`, `log2`. - Command execution now returns `Error` for negative `extract` counts, duplicate rule names, bad `input`/`print-function` files and types, and extraction of deleted/subsumed values. - `unstable-fn` resolution is threaded through `prim`/`query` so unknown, non-literal, or mis-typed targets error instead of panicking. - Parsing/desugar errors for `(fail (include ...))`, `(fail (datatype*))`, subsuming a non-call rewrite, and multi-command `fail`. - Proof subsystem gains `ProveExistsError` variants and graceful errors for running `prove`/`prove-exists` without proofs and for re-parsing edge cases. - CLI write/path failures log and exit cleanly; scheduler no-free-var and unknown-ruleset edge cases no longer panic; serializing a sort whose name contains `-` no longer panics and round-trips correctly. Breaking: `EGraph::print_function` now takes `Option<(File, PathBuf)>` plus a `Span` so write failures return `Error::IoError`. Adds regression tests; `make test` (958) and `make nits` pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #920 +/- ##
==========================================
+ Coverage 87.07% 87.17% +0.10%
==========================================
Files 88 88
Lines 26282 26473 +191
==========================================
+ Hits 22885 23079 +194
+ Misses 3397 3394 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will not alter performance
Comparing Footnotes
|
Port the ad-hoc verification harness into tests/no_panic.rs: a table-driven suite covering ~32 previously-panicking reproductions (malformed sort declarations, partial primitives, command-execution failures, unstable-fn resolution, desugar/parse errors, multiset index with a primitive function, prove-exists without proofs, and term-encoding of escaped strings). Each case returns a recoverable error or runs to completion; a real panic would abort the test binary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This issue (and the comment on it) has a few places where we panic instead or raising errors. I had them included in other PRs and took them out to make reviewing them simpler. If you feel like handling those cases here feel free! #898 |
| if !row.subsumed { | ||
| let target = &row.vals[output_idx]; | ||
| if *target == canonical_value { | ||
| let cost = self.compute_cost_hyperedge(egraph, &row, func).unwrap(); |
There was a problem hiding this comment.
Can this ever fail?
There was a problem hiding this comment.
Yes, reachably. compute_cost_hyperedge returns None when a variant has a child e-class with no finite extraction (e.g. a purely cyclic child). The old .unwrap() was a latent panic; such a variant can never be part of a minimal extraction, so skipping it is correct and the target e-class still extracts via its costed variants. Kept the change and added a comment explaining why.
- Treat internally generated term-encoding programs as invariants again
(revert the Result threading in proof_encoding{,_helpers}.rs to panics),
and fix the actual reason they could fail to parse: `Display for Literal`
now escapes `"` and `\` so strings round-trip through the lexer.
- Revert bigrat `log`/`cbrt` of a non-trivial value back to `todo!`: `None`
would wrongly claim e.g. `cbrt 8/1` has no rational root.
- extract: keep skipping variants with no finite cost (reachable for cyclic
children); document why.
- scheduler: variable-free rules now collect a unit marker per match so they
correctly apply their actions when scheduled, instead of silently doing
nothing (or dividing by zero).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oflatt
left a comment
There was a problem hiding this comment.
just reviewed proof related stuff
Following the same principle as the proof-encoding parse helpers, stop dressing up internal proof-machinery invariants as recoverable errors in `prove_exists`. Revert `ConstructorNotDeclared`, `NoProofRecorded`, `ProofTermExtractionFailed`, `NotProvableByRule`, and `ProofCheck` back to panics (they only fire if the proof code itself is wrong) and drop them from `ProveExistsError`. The one genuinely user-reachable case stays: a missing proof-function annotation means proofs were never enabled for the sort, surfaced as `ProveExistsError::ProofsNotEnabled`. That lookup is the correct gate (it is also satisfied when re-running already-lowered programs, where the `proofs_enabled` flag is false but the proof tables exist), so the check lives here rather than as an up-front `proofs_enabled` guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Audits
./srcforpanic!/unwrap/expect/todo!/unreachable!sites that are reachable from user input and converts them into proper recoverable errors, so malformed or edge-case programs report an error instead of aborting the process. This addresses a recurring class of bug reports where egglog panics instead of returning an error.Of 324 panic-family sites in
./src, an audit classified 52 as reachable (MUST_FIX), 258 as type-checker-guaranteed invariants, and the rest as tests/needs-review. All reachable sites are fixed (plus ~12 more found during implementation); invariants are intentionally left.What now returns an error instead of panicking
TypeError::BadPresortArguments:(sort S (Vec)),(sort S (UnstableFn)),(sort S (Pair i64)), etc. A realspanis threaded throughPresort::make_sort.vec-set/vec-remove, emptymultiset-pick, count overflow inmultisetops, and the numeric primitivesbigint <</>>,bigrat,log,cbrt,log2.Error: negativeextractvariant counts, duplicate rule names, badinput/print-functionfiles & column types,output/extractof deleted/subsumed values.unstable-fn: threadedResultthroughprim/query/resolve_function_container_target_with_contextso unknown, non-literal, or mis-typed targets error instead of panic.(fail (include ...)),(fail (datatype*)), subsuming a non-call rewrite, and multi-commandfail.ProveExistsErrorvariants; runningprove/prove-existswithout proofs enabled; graceful re-parse errors in the term-encoding chain.-(e.g.Math-Expr) no longer panics and round-trips correctly.Intentionally left (genuine invariants)
Type-checker-guaranteed
get_val/base_values().unwrapin primitives, post-typecheck resolution incore.rs, documented Rust-API preconditions (Match::get_value, empty-varsquery), a wasm-only thread-count panic, and compile-time proc-macro guards.Breaking change
EGraph::print_functionnow takes its output sink asOption<(File, PathBuf)>plus aSpan, so write failures returnError::IoErrorinstead of panicking.Testing
extract, missinginputfile, scheduler edge cases, dashed-sort serialization).make testpasses (958 tests);make nitspasses (clippy-D warnings, fmt, doc).🤖 Generated with Claude Code