Repository navigation
Member operators, own-object returns, and class invariants at reference and pointer parameters - #79
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.
…s own object as nothing, and hold class invariants at reference and pointer parameters.
… for class invariants.
…eturns to its own object.
…ns to its own object, as an argument is.
…urns to its own object.
…assed by reference or pointer.
…s at reference and pointer parameters.
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 member operator was not a member call: for
a == bwith a memberoperator==, the converter tookafor the operator's parameter andbas an argument beyond it, an error. An operator that returns its own object by reference (Bits &operator|=, every assignment operator) was an unsupported type. Now a member operator takes its first operand as its object, and one that returns*thisreturns nothing the caller does not hold:On the base (#78):
operator|=keeps the invariant only becauseosatisfies it too, which settles the question #78 left open: an object a parameter refers to or points to now satisfies its class invariant at every function boundary.This branch:
The base (#78) has the verdicts the other way round:
readfails at its call toget(type-invariant@22:3), sincelwas not known to satisfy the invariant, andspoilverifies.Stacked on #78 (B3-4, the last part of plan item B3: classes).
Design
convertExecCallArgstakes the object of aCXXOperatorCallExprwhose callee is an instance member function from its first argument, bound asobj.m()'s object is, and the parameters from the rest. A free operator is a free function and is unchanged.returnsOwnObjectrecognizes a member function returning an lvalue reference to its own class whose every return isreturn *this;, the shape of an assignment or compound-assignment operator. The reference names an object the caller already holds, so theVFunctionreturns void,findUnsupportedTypeaccepts the return type, and eachreturn *this;returns no value. A call that uses the reference, as an argument ((a += b) += c), as the source of a copy (Bits c = (a |= b);), or of an assignment (c = (a |= b);), is an error naming the operator (rejectOwnObjectUse, at the three places a record value is read from an expression), so nothing reads a value that is not there; without it these were rejected with messages about something else. A function that returns*thison some paths and another object on others is not one: it returns a reference, which is still unsupported.ThisSubstituteset to the parameter, whichCXXThisExprconverts to, sovaluein the invariant readsl->value. A pointer's instance is guarded byp == nullptr ||. A callee's precondition is shown at each call, so an object that a caller breaks fails at the call (spoiled_argument), and a callee that breaks it fails at its return (spoil). Two cases are left out, both conservative: a pointer with avalid(p, n)extent addresses several objects, and gets no instance; an extent marker in the invariant isthis's (B3-3: class invariants at public member functions and constructors #78), so in a parameter's instance it istrue(DropExtentMarkers), and a member function that needs the extent cannot be called through such a parameter (first_through,bounds).summedlends local objects tooperator+, which has a local object of its own. The record escape scan (recordStmtSafe, verify: Lend local records and addresses to callees that keep no address of them #71) rejected every allocation in the callee; it now accepts the callee's own automatic object unless it is initialized with an address it must not keep. An allocation fromnewmakes the calleeUsesDynamicStorage, which the scan rejects before it looks at a statement, as before.Tests
suite/member_operators.cpp(Z3, with a check that no proof is vacuous):Bits:operator==andoperator|=on locals (bits_use), each operand's invariant used byoperator|=;Money: a memberoperator+=returning*thisand a freeoperator+returning a value (summed),a += afailingaliasing(self_add), and the returned reference used as an argument (chained), copied (copied), and assigned (assigned), each rejected;Level: a reference and a pointer parameter assuming the invariant (read,peek), a mutable reference keeping it (lower) and breaking it (spoil), a caller passing a broken object (spoiled_argument), and a null pointer (none);Span: an extent in the invariant, not carried by a reference (first_through).suite/record_references.cpp:by_referencenow verifies; it read a field of a record passed by reference and needed its invariant.Mutants, each built on an assertions build and run on
member_operators.cpp,class_invariants.cpp,record_references.cpp,member_functions.cpp,local_record_arguments.cpp, andtype_invariant_lazy.cpp: 10 of 10 killed.member_operators(bits_use)member_operators(Bits::operator|=)member_operators(Bits::operator|=),record_references(by_reference)member_operators(spoil)member_operators(none)member_operators(first_through: the marker ons->datais rejected)member_operators(summed)member_operators(chained: "call arguments have order-dependent heap evaluations")member_operators(copied: "aggregate initialization and copying are unsupported")member_operators(assigned: "unsupported aggregate initializer for an automatic object")Open
valid(p, n)extent of a class with an invariant assumes nothing of its elements; quantifying the invariant over the extent is future work.this: a parameter's instance does not carry it. Carrying it would need a field extent of the parameter's object (verify: State the extents of pointer fields, and separate pairs with an extent over their extents #72's synthetic parameters, for a field of an object that is notthis).Docs
Bits); the class-invariant rule for reference and pointer parameters, replacing the note that they assume nothing.Checked
member_operators.cpp,record_references.cpp,member_functions.cpp, andlocal_record_arguments.cpppass.docs_examples.testanddocs_coverage.testpass; the site builds with warnings as errors.