Skip to content

prove: util::range_alloc - #699

Draft
Marsman1996 wants to merge 3 commits into
asterinas:mainfrom
Marsman1996:prove-range_alloc
Draft

prove: util::range_alloc#699
Marsman1996 wants to merge 3 commits into
asterinas:mainfrom
Marsman1996:prove-range_alloc

Conversation

@Marsman1996

@Marsman1996 Marsman1996 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

The 3 #[verifier::external_body] are caused by the BTreeMap
This PR is parsed into 3 (including this one):

@rikosellic

Copy link
Copy Markdown
Collaborator

I think vstd already supports BTreeMap? https://github.com/verus-lang/verus/pull/2189/files

@Marsman1996

Marsman1996 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

I think vstd already supports BTreeMap? https://github.com/verus-lang/verus/pull/2189/files

But there is no spec foe BTreeMap::get_mut
Well, I shall add them in the vstd_extra

@Marsman1996
Marsman1996 requested a review from rikosellic August 8, 2026 12:35
@rikosellic

Copy link
Copy Markdown
Collaborator

You may also create a PR for Verus.

@rikosellic

Copy link
Copy Markdown
Collaborator

I will try to investigate BTreeMap more closely tomorrow. Then we can propose a PR for Verus. This PR should not be merged until the Verus PR is merged. It is not very urgent.

Comment thread ostd/specs/util/range_alloc_specs.rs Outdated
@Marsman1996

This comment was marked as off-topic.

&&& old_map[*key] == old_value
&&& new_map == old_map.insert(*key, new_value)
},
;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this function and axiom? Does this just mean insert?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This axiom provides a concrete interpretation of borrowed_key_mutated when the borrowed lookup key has the same type as the key stored in the map.

The generic BTreeMap::get_mut specification must support borrowed-key lookups where the types differ, such as BTreeMap<String, V>::get_mut(&str). In that case, the borrowed key cannot generally be converted back into the exact stored key, so the specification uses the abstract relation borrowed_key_mutated.

When the borrowed and stored key types are both Key, the relation can be characterized precisely:

  • The key existed in old_map.
  • Its value was old_value.
  • new_map is old_map with that key mapped to new_value.

Therefore, this is not specifying a call to executable BTreeMap::insert. The spec-level Map::insert is a functional update used to describe the final mathematical state of the map. The axiom also records the successful lookup and the previous value.

Declaring it as a broadcast axiom allows Verus to apply this interpretation automatically when reasoning about get_mut with identical borrowed and stored key types.

@rikosellic rikosellic Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definition is very weird. Because the whole system only works when Q is identical to Key. A more general way may be to add a borrow_spec to the Borrow trait. Then we can have some definition like exists |k: Key|, old_map.contains(key) && key.borrow_spec() == q && new_map == old_map.insert(k, new_value). Next we can add a broadcast lemma that handles the identical case, which removes the exists quantifier. However, this will require a fundamental change to Borrow's spec, which is undefined now. You can find it in std_specs/core.rs. You'd better open a question about how to support the general Borrow trait in Verus Zulip first.

Some(value) => borrowed_key_mutated(old(map)@, final(map)@, key, *value, *final(value)),
None => !contains_borrowed_key(old(map)@, key) && final(map)@ == old(map)@,
},
;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is incorrect that this function does not have any preconditions. According to the std documentation, the ordering of Q must match the ordering of Key. This precondition is not expressed here.

@rikosellic rikosellic Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I find this one is also related to borrow_spec. Maybe we can merge the version without btree_map::CursorMut and BTreeMap::get_mut first. The borrow_spec needs to be discussed with Verus developers

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can merge the version without btree_map::CursorMut and BTreeMap::get_mut first.

Yes, I agree.

@Marsman1996
Marsman1996 force-pushed the prove-range_alloc branch 2 times, most recently from b2ff64e to 60daf60 Compare August 11, 2026 11:12
@Marsman1996

Copy link
Copy Markdown
Collaborator Author

Verus's external specification of Borrow currently has no logical model for this compatibility, so have to add a axiom in verified_libs/vstd_extra/src/external/btree.rs

@Marsman1996 Marsman1996 added AI-assist AI-aided proof or generation exec code Proofs about execution code labels Aug 11, 2026
Comment thread verified_libs/vstd_extra/src/external/btree.rs Outdated
{
&&& borrowed_key_removed(old_map, remainder, key)
&&& borrowed_key_removed(new_map, remainder, key)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this exists clause?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existential clause is the frame condition for get_mut. The preceding predicates only constrain the value associated with the borrowed key; without this clause, the specification would also allow arbitrary changes to every other entry. Requiring both maps to produce the same remainder after removing the borrowed key ensures that they differ only at that entry. We use borrowed_key_removed because, for a general Key: Borrow<Q>, Verus currently has no borrow_spec that lets us directly identify the corresponding stored Key.

For the Q = Key case used by range_alloc, the existential clause reduces to:

old_map.remove(*key) == new_map.remove(*key)

Together with the two value-mapping predicates, this states that only the value corresponding to key may change; all other entries remain unchanged. This matches BTreeMap::get_mut, which returns a mutable reference to the value corresponding to the borrowed key.

The borrowed_key_ordering_matches precondition separately captures the documented requirement that the ordering of Q must match the ordering of Key.

For an arbitrary heterogeneous Key: Borrow<Q>, this remains an abstract specification because vstd does not yet formalize the semantics and ordering laws of a general Borrow implementation. However, it is precise for the Q = Key case used here.

@Marsman1996
Marsman1996 marked this pull request as draft August 12, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-assist AI-aided proof or generation exec code Proofs about execution code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants