Clean up handling of builtin operators#181
Conversation
11d85ba to
14e32e3
Compare
|
@Aurel300 though I did use claude for the second half of this PR (time-wise, I got the AI to logically clean up the commits), I tried to go through all the changes and approve them myself. So should be final and relatively clean for review. |
c0f8dbe to
c0ece36
Compare
Aurel300
left a comment
There was a problem hiding this comment.
Various comments, a lot are cosmetic, the overall code restructuring looks mostly fine.
though I did use claude for the second half of this PR
I strongly wish you would not. Reviewing LLM-assisted code in general expects more of the reviewer than code that was written by yourself (or the review is not done properly). I don't want to trade student project slop for AI slop.
Doctests fixed (9)
|
I don't agree with this. Yes, it's easy to produce AI slop, but it's also easy to produce human slop. As long as I "review" the code myself before asking for a PR review from you (which I already did with hand-written PRs), I do not think that the quality of the code will be any worse than if I had authored it fully myself (and correspondingly the review effort from you). I would point to this PR as proof of that. Yes I forgot to go over the comments and so there were many issues there (I'll make sure not to miss that in the future), but if you look at reviewing the code changes themselves then I would not think that it was different to any other PR for you. Ultimately I see this as a personal decision: do I want to trade faster code iteration at the cost of spending more time going over the LLM generated code myself. Currently, I'm trying it out to see if that's worth it for me, but I don't see this as affecting others. Yes ofc if I got lazy and skipped the self-review step then AI slop PRs would be a problem, but equally I could be lazy and hand-write quick/poor-quality code by hand, which also pushes a lot of the work onto a reviewer. |
|
Regarding the Doctests regressions, it seems that those are due to fixing the unsoundness: I only clicked onto a few, but for all of those I don't see how we would've been able to prove their assertions beforehand. |
Split the monolithic `mir_builtin.rs` into a `builtin/` module (`bin_op`, `un_op`, `cast`, `metadata`, `use_cast`, `use_metadata`) and rework how references, raw pointers, and DSTs are encoded: - References and raw pointers now carry pointer metadata alongside their address/value in the snapshot; the metadata type is derived from the referent as `<T as Pointee>::Metadata` (new `ty/kinds/raw.rs`, plus metadata handling in `immref`/`mutref`). - Thread this metadata through the pure/impure/const encoders and the place walkers in `mir_impure.rs`/`mir_pure.rs`/`mir_shared.rs`. - Support unsizing coercions to `dyn Trait` and through mutable references. - Remove the `Len` builtins and report unsupported rvalues as proper verification errors instead of panicking. - Fix modulo handling, ZST/inhabitedness queries, and reference-address handling in pure code.
`as` casts between integers never panic, so encode `mir::CastKind::IntToInt` without a precondition, wrapping the value into the target type's range instead. The casts that previously (incorrectly) tripped overflow checking now verify, so drop the corresponding `verify_overflow/fail/casts` cases and add a `wrapping` test exercising the new behaviour.
Move the `is_zst` flag off the args-agnostic `RustTy` (where it caused duplicate Viper definitions for the same base type used in different generic contexts) onto the args-aware decomposition, and emit the canonical `s_T_zst` value on demand from a new `TyZstEnc` (`ty/zst.rs`) instead of for every type.
- Propagate pointer metadata to the last (DST-tail) field so re-borrowing an unsized field succeeds, and model `maybe_inhabited` precisely. - Remove the now-unused `transmute` encoder and other dead code. - Move the `sign_mix`/`signed`/`unsigned` cast tests that now verify from `verify_overflow/fail/` to `pass/`. - Simplify the builtin cast/bin-op encoders and apply fmt/clippy cleanup.
`&raw const (fake) (*p)` carries a pointee's pointer metadata (e.g. a slice's length) so that a following `PtrMetadata` can read it back out. The encoder only handled this rvalue in impure code, so reading `slice.len()` or indexing a slice inside a specification failed with an "unsupported rvalue ... in pure code" error. Mirror the impure handling in the pure rvalue encoder, using the pure `null`-address convention. Fixes the issue-721-2, issue-812-1, and issue-812-3 regressions.
This issue is still not fixed for recursive calls (such as in `from_nth.rs`) where the call is not encoded first somehow.
|
@JonasAlaif There are a few unresolved conversations still, otherwise as far as I understand, these are follow-up tasks (for separate PRs):
|
Summary (Claude generated)
Reworks how the encoder models pointer metadata for references, raw pointers,
and dynamically-sized types, reorganizes the builtin encoders into their own
module, and fixes integer
ascasts and zero-sized types. Net result: severalpreviously-failing slice/DST tests now verify, integer-cast overflow behaviour is
modelled correctly, and unsupported constructs report clean errors instead of
panicking. No test regressions vs
rewrite-2023.What's changed
1. Reorganize builtin encoders & model pointer metadata for references
mir_builtin.rsinto abuiltin/module (bin_op,un_op,cast,metadata,use_cast,use_metadata).dynvtable) alongside the address and value in their snapshot. The metadatatype is derived from the referent as
<T as Pointee>::Metadatarather thanbeing a separate generic. New
ty/kinds/raw.rsmodels raw pointers (reference-shapedbut pointee-opaque).
walkers, including propagation to a DST's unsized tail field.
dyn Traitand through mutable references.Lenbuiltins and reports unsupported rvalues as properverification errors instead of panicking or emitting unsound placeholders.
2. Model integer
IntToIntcasts without a preconditionascasts between integers never panic, so they are encoded without aprecondition; out-of-range values wrap into the target type's range via a new
vir::VirCtxt::get_wrapped_valhelper (shared by casts and wrapping arithmetic).adds a
casts/wrapping.rstest.3. Encode zero-sized types via a dedicated
TyZstEncis_zstflag off the args-agnostic base type (where it causedduplicate Viper definitions for the same type used in different generic
contexts) onto the args-aware decomposition, emitting the canonical
s_T_zstvalue on demand from a new
ty/zst.rsencoder.4. Encode
FakeForPtrMetadataraw pointers in pure code&raw const (fake) (*p)(which carries a pointee's pointer metadata so a laterPtrMetadatacan read it back) is now handled in the pure rvalue encoder,so reading
slice.len()or indexing a slice inside a specification works.Test impact
pure-array-unsize-to-slice,issue-721-*,issue-812-*,Dijkstras_algorithm-simpl) and adds new coverage(
casts/{wrapping,sign_mix,signed,unsigned},simple-specs/modulo_div_symbolic).rewrite-2023../x.py fmt-all && ./x.py clippy.Known limitations / follow-ups
verifying:
Rvalue::Lenon a slice place, and certain slice indexing in impurecontexts (
slices/slice-len,slice-snap,type_param_slice).IntToInt/Unsize(e.g. ptr-to-ptr, int-to-float) arestill
todo!().&mut-returning functions with magic wands (e.g.magic-wands/from_nth)do not verify: the recursive call is approximated rather than encoded via its own
contract. The
call_labelscrash on that path is fixed, but full support needsproper recursive method-call encoding (a Viper error without a source position
currently aborts back-translation in
prusti-server).