Repository navigation
B3-3: class invariants at public member functions and constructors - #78
Merged
Merged
Conversation
… loop that stores to local storage keeps what was initialized.
…reads bound the head.
…dition reads what the loop changes.
…ion across loops, with a checked example.
…s of the objects it writes.
…ere objects are initialized.
…ark it initialized at calls.
…ing each field where it is.
… exit, and dropping extent markers.
…onstructors, with extents of its pointer fields.
…lls of its leaves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
cppverify::type_invariantheld only for records passed and returned by value: a class's member functions and constructors ignored it, so nothing a caller learned about an object survived a call, and a member function could leave the object in any state. Now the invariant of a class holds at its public boundary:On the base (#77)
overflowverifies too: member functions ignored the invariant.Stacked on #77 (B3-3 of plan item B3: class invariants; member operators follow).
Design
The plan's rule: the invariant is a precondition and postcondition of every public member function and a postcondition of every constructor, checked at every call, internal calls included, which is sound without pack/unpack (Spec#'s and Dafny's
Valid()discipline need an explicit state of the object because they let methods be called while it is broken; here no call can be):entryClassInvariants(a public member function's, not a constructor's) andexitClassInvariants(a public member function's and every constructor's) give the class's invariants, converted in the member function's context, so a field is a load throughthis; they are generated pre- and postconditions of kindtype-invariant, checked and assumed at calls as any.cv::valid(data, len)in a class invariant is, on entry, a field extent ofthisas in a precondition:pointerFieldOfParametertakesthis->data, andcollectFieldExtentsscans the entry invariants, sothis->datais a synthetic parameter bound at every call. At exit a marker states nothing (a postcondition cannot carry one), so it istruethere (DropExtentMarkers); the next call checks the extent from what its caller knows, and a member function that changeslenmust say how.Found while testing: a falsified claim after a constructor and a member call on a local object ended
solver.timeout(60 s) instead of failing. A call whose footprint is a record parameter's object, as the defaultmodifies(*this)is, framed its bytes with oneHeapFrame, the unbounded quantifier #75 met in loops.havocFootprintsnow frames it as the cells of the record's leaves (RecordCells, from the callee's layout at the actual's address), so the call is a chain of stores: the same claim fails in 0.3 s, and the test file runs in 3 s instead of 66 s.Tests
suite/class_invariants.cpp(Z3, with a check that no proof is vacuous):Ring: a constructor that initializes the buffer in a loop and establishes the invariant;push,pop,size,front_indexverified with it assumed and restored;skipbreaks it (type-invariant);rotatebreaks it through a private member function and restores it;peek_brokencalls a public member function while it is broken (type-invariantat the call);Window: a constructor that does not establish it;Bounded: one that does;known,ring_usewithcv::check), a field broken by the caller before a call (broken_by_caller, at the call), andpop's unstated value (ring_valuefails, in seconds);View: an extent in the invariant, assumed byfirst; a caller passing its own extent (first_of), and one whosegrowmakes the next call's extent fail (grown,bounds).Mutants, each built on an assertions build and run on
class_invariants.cpp,constructors.cpp,member_functions.cpp,pointer_field_extents.cpp,local_record_arguments.cpp, andtype_invariant_lazy.cpp: 8 of 8 killed.class_invariantsclass_invariants(skip)class_invariants(rotate)class_invariants(Window)class_invariants(View)this->ftakes no extentclass_invariants(View)class_invariants(View)class_invariants(grown,ring_value:solver.timeout)Open
Docs
Meter), the rules for private members, callers, and extents.Checked
docs_examples.testanddocs_coverage.testpass; the site builds with warnings as errors.