Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Prusti-ish frontend to Creusot #932

Open
wants to merge 102 commits into
base: master
Choose a base branch
from

Conversation

dewert99
Copy link
Collaborator

This PR is an implementation of the Prusti to Creusot translation described in my thesis.

The Prusti style specifications are contained in use creusot_contracts::prusti_prelude, which has its own version of requires, ensures, logic and ghost.

# Conflicts:
#	creusot-contracts-proc/src/lib.rs
TODO:
Handle associated types
Use the stronger signature from a trait implementation instead of the general trait if possible
… functions logical functions that take "made up" references
# Conflicts:
#	creusot-contracts-dummy/src/lib.rs
#	creusot-contracts-proc/src/lib.rs
#	creusot-contracts/src/ghost.rs
#	creusot-contracts/src/lib.rs
#	creusot-contracts/src/logic/mapping.rs
#	creusot-contracts/src/logic/seq.rs
#	creusot-contracts/src/model.rs
#	creusot/src/translation/traits.rs
#	creusot/src/util.rs
#	creusot/tests/ui.rs
…o assume everything has a home in the current time-slice
@xldenis
Copy link
Collaborator

xldenis commented Jan 27, 2024

This is very impressive work, and I think there's value in preserving (and expanding) on your thesis work, but I'm not sure we want to merge things in this current state. My issue is that it's a significant chunk of work that we don't have the expertise to maintain without you, especially as I'm looking to take a step back and allow others to take the lead on Creusot's development.

However, I do have a proposal which may help maintain both objectives: would it be possible to make creusot-prusti a separate frontend for Creusot? If we can split this into two parts of work: API changes to creusot the library and a new frontend I'm hoping we can keep the complexity split cleaner and lower the entanglement.

I hope I don't sound too ungrateful, and I hope that we can find a way to maintain your work without overwhelming the other Creusot developers.

@dewert99
Copy link
Collaborator Author

I've tried to keep the entanglement relatively low, the creusot::prusti module only depends on the rest of creusot for its error type so it would be fairly easy to extract it into its own crate if that would help. creusot::translation::pearlite::prusti depends on the creusot::translation::pearlite::Term type since it is easier to traverse than raw THIR. The changes I made to existing files in creusot mostly exist to improve the ty field of creusot::translation::pearlite::Term function calls to remember lifetimes that were specified by the use (eg. at::<'a>) and whether they used to be references or boxes (and then undoing these changes when lowering the creusot::translation::pearlite::Term). I could try move these changes to a separate PR but I'm not sure how that would help with maintainability.

I could also try to rewrite creusot::translation::pearlite::prusti to operate over THIR instead of creusot::translation::pearlite::Term which would allow me to also move creusot::translation::pearlite::prusti into a separate crate and revert most of the changes I made in creusot::translation::pearlite and creusot::backend:term but this would be more work.

Whatever I end up doing there still will be the maintenance burden caused by updating the rustc version since its API is unstable and my frontend would need to use the same rustc version as the rest of Creusot.

@xldenis
Copy link
Collaborator

xldenis commented Jan 29, 2024

I've tried to keep the entanglement relatively low, the creusot::prusti module only depends on the rest of creusot for its error type so it would be fairly easy to extract it into its own crate if that would help.

I noticed that and I think that would be a good idea. What I'm imagining is that since we will be moving Creusot to an org, we could make creusot-prusti an alternative binary with its own repo in that organization, using creusot-lib as a dependency. This would allow the two to evolve at different rates: they wouldn't be required to move in strict lockstep.

The changes I made to existing files in creusot mostly exist to improve the ty field of creusot::translation::pearlite::Term function calls to remember lifetimes that were specified by the use (eg. at::<'a>)

I think these can be merged uncontroversially.

Whatever I end up doing there still will be the maintenance burden caused by updating the rustc version since its API is unstable and my frontend would need to use the same rustc version as the rest of Creusot.

That's not really the maintainance burden I was worried about; that's an unchangeable cost, I'm referring more that any changes to Pearlite or Creusot's translation could break the prusti specifications in an unknown manner which no one might have.

Another possible outcome is to ask ourselves: is there a way we could fold some of this work directly into mainline Pearlite? Could we use at as a generalization of ^? Could we use parts of this to build an after_expiry mechanism that works with standard Pearlite? Are there changes to mainline Pearlite we could make which would make building and maintaining a Prusti-style frontend language simpler?

# Conflicts:
#	creusot/src/backend/term.rs
#	creusot/tests/should_succeed/iterators/02_iter_mut.mlcfg
#	creusot/tests/should_succeed/iterators/03_std_iterators.mlcfg
#	creusot/tests/should_succeed/iterators/06_map_precond.mlcfg
@dewert99
Copy link
Collaborator Author

dewert99 commented Feb 1, 2024

Another possible outcome is to ask ourselves: is there a way we could fold some of this work directly into mainline Pearlite? Could we use at as a generalization of ^? Could we use parts of this to build an after_expiry mechanism that works with standard Pearlite? Are there changes to mainline Pearlite we could make which would make building and maintaining a Prusti-style frontend language simpler?

The syntax for the Prusti frontend is essentially Pearlite with some builtin functions (like at) and traits that have special semantics. The majority of the work done by the Prusti front-end is deciding which mutable de-references should be replaced with final operators, as well as rejecting programs that do not have well-defined Prusti semantics. Doing this depends on the rust type-checker to eg. find which lifetimes are blocked, check if types are Copy, normalize associated types and type aliases.

That's not really the maintainance burden I was worried about; that's an unchangeable cost, I'm referring more that any changes to Pearlite or Creusot's translation could break the prusti specifications in an unknown manner which no one might have.

I think I understand, unfortunately maintaining a separate front-end has its own difficulties. At least cargo-creusot, creusot-rustc, creusot_contracts and creusot_contracts_proc would all need Prusti front-end versions, and we would need to extract a library out of creusot_contracts_proc since most of the Prusti version would be the same. The Prusti front-end would probably need to use git dependencies to depend on various creusot crates and would need to be updated semi-frequently just to chage the commit of the dependecies (or just pin to main and then risk breaking if Creusot switches its rustc version). Prusti logic functions also have more information in the signatures that Creusot logic functions, so it would be useful to add Prusti specific attributes to some of the logic functions in creusot-contracts although they might seem out of place if the creusot repo otherwise knew nothing about the Prusti frontend.

# Conflicts:
#	creusot-rustc/src/main.rs
#	creusot/src/backend/term.rs
#	creusot/src/translation/pearlite.rs
#	creusot/src/translation/traits.rs
#	creusot/tests/should_succeed/100doors.mlcfg
#	creusot/tests/should_succeed/bug/874.mlcfg
#	creusot/tests/should_succeed/filter_positive.mlcfg
#	creusot/tests/should_succeed/hashmap.mlcfg
#	creusot/tests/should_succeed/index_range.mlcfg
#	creusot/tests/should_succeed/iterators/08_collect_extend.mlcfg
#	creusot/tests/should_succeed/iterators/16_take.mlcfg
#	creusot/tests/should_succeed/knapsack.mlcfg
#	creusot/tests/should_succeed/knapsack_full.mlcfg
#	creusot/tests/should_succeed/selection_sort_generic.mlcfg
#	creusot/tests/should_succeed/sparse_array.mlcfg
#	creusot/tests/should_succeed/syntax/13_vec_macro.mlcfg
#	creusot/tests/should_succeed/vector/03_knuth_shuffle.mlcfg
#	creusot/tests/should_succeed/vector/06_knights_tour.mlcfg
#	creusot/tests/should_succeed/vector/08_haystack.mlcfg
# Conflicts:
#	Cargo.lock
#	creusot-contracts/src/ghost.rs
#	creusot-contracts/src/ghost_ptr.rs
#	creusot-contracts/src/lib.rs
#	creusot-rustc/src/main.rs
#	creusot/tests/should_fail/bug/869.mlcfg
#	creusot/tests/should_fail/cycle.stderr
#	creusot/tests/should_succeed/100doors.mlcfg
#	creusot/tests/should_succeed/bug/874.mlcfg
#	creusot/tests/should_succeed/filter_positive.mlcfg
#	creusot/tests/should_succeed/hashmap.mlcfg
#	creusot/tests/should_succeed/hillel.mlcfg
#	creusot/tests/should_succeed/iterators/01_range.mlcfg
#	creusot/tests/should_succeed/iterators/03_std_iterators.mlcfg
#	creusot/tests/should_succeed/iterators/04_skip.mlcfg
#	creusot/tests/should_succeed/iterators/07_fuse.mlcfg
#	creusot/tests/should_succeed/iterators/08_collect_extend.mlcfg
#	creusot/tests/should_succeed/iterators/09_empty.mlcfg
#	creusot/tests/should_succeed/iterators/10_once.mlcfg
#	creusot/tests/should_succeed/iterators/14_copied.mlcfg
#	creusot/tests/should_succeed/iterators/15_enumerate.mlcfg
#	creusot/tests/should_succeed/knapsack.mlcfg
#	creusot/tests/should_succeed/knapsack_full.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max_repeat.mlcfg
#	creusot/tests/should_succeed/selection_sort_generic.mlcfg
#	creusot/tests/should_succeed/sparse_array.mlcfg
#	creusot/tests/should_succeed/sum.mlcfg
#	creusot/tests/should_succeed/sum_of_odds.mlcfg
#	creusot/tests/should_succeed/syntax/13_vec_macro.mlcfg
#	creusot/tests/should_succeed/vector/01.mlcfg
#	creusot/tests/should_succeed/vector/03_knuth_shuffle.mlcfg
#	creusot/tests/should_succeed/vector/06_knights_tour.mlcfg
#	creusot/tests/should_succeed/vector/08_haystack.mlcfg
# Conflicts:
#	creusot-contracts/src/lib.rs
#	creusot/src/translation/traits.rs
#	creusot/tests/should_fail/bug/222.mlcfg
#	creusot/tests/should_fail/bug/492.mlcfg
#	creusot/tests/should_fail/bug/692.mlcfg
#	creusot/tests/should_fail/bug/695.mlcfg
#	creusot/tests/should_fail/bug/869.mlcfg
#	creusot/tests/should_fail/final_borrows.mlcfg
#	creusot/tests/should_succeed/100doors.mlcfg
#	creusot/tests/should_succeed/all_zero.mlcfg
#	creusot/tests/should_succeed/bdd.mlcfg
#	creusot/tests/should_succeed/bug/552.mlcfg
#	creusot/tests/should_succeed/bug/594.mlcfg
#	creusot/tests/should_succeed/bug/682.mlcfg
#	creusot/tests/should_succeed/bug/766.mlcfg
#	creusot/tests/should_succeed/bug/874.mlcfg
#	creusot/tests/should_succeed/bug/922.mlcfg
#	creusot/tests/should_succeed/bug/949.mlcfg
#	creusot/tests/should_succeed/bug/box_borrow_resolve.mlcfg
#	creusot/tests/should_succeed/bug/final_borrows.mlcfg
#	creusot/tests/should_succeed/bug/two_phase.mlcfg
#	creusot/tests/should_succeed/checked_ops.mlcfg
#	creusot/tests/should_succeed/closures/01_basic.mlcfg
#	creusot/tests/should_succeed/closures/03_generic_bound.mlcfg
#	creusot/tests/should_succeed/closures/04_generic_closure.mlcfg
#	creusot/tests/should_succeed/closures/05_map.mlcfg
#	creusot/tests/should_succeed/closures/06_fn_specs.mlcfg
#	creusot/tests/should_succeed/closures/07_mutable_capture.mlcfg
#	creusot/tests/should_succeed/constrained_types.mlcfg
#	creusot/tests/should_succeed/drop_pair.mlcfg
#	creusot/tests/should_succeed/filter_positive.mlcfg
#	creusot/tests/should_succeed/ghost_ptr_token.mlcfg
#	creusot/tests/should_succeed/hashmap.mlcfg
#	creusot/tests/should_succeed/heapsort_generic.mlcfg
#	creusot/tests/should_succeed/hillel.mlcfg
#	creusot/tests/should_succeed/immut.mlcfg
#	creusot/tests/should_succeed/index_range.mlcfg
#	creusot/tests/should_succeed/inplace_list_reversal.mlcfg
#	creusot/tests/should_succeed/invariant_moves.mlcfg
#	creusot/tests/should_succeed/ite_normalize.mlcfg
#	creusot/tests/should_succeed/iterators/01_range.mlcfg
#	creusot/tests/should_succeed/iterators/02_iter_mut.mlcfg
#	creusot/tests/should_succeed/iterators/03_std_iterators.mlcfg
#	creusot/tests/should_succeed/iterators/04_skip.mlcfg
#	creusot/tests/should_succeed/iterators/05_map.mlcfg
#	creusot/tests/should_succeed/iterators/06_map_precond.mlcfg
#	creusot/tests/should_succeed/iterators/07_fuse.mlcfg
#	creusot/tests/should_succeed/iterators/08_collect_extend.mlcfg
#	creusot/tests/should_succeed/iterators/09_empty.mlcfg
#	creusot/tests/should_succeed/iterators/10_once.mlcfg
#	creusot/tests/should_succeed/iterators/11_repeat.mlcfg
#	creusot/tests/should_succeed/iterators/12_zip.mlcfg
#	creusot/tests/should_succeed/iterators/13_cloned.mlcfg
#	creusot/tests/should_succeed/iterators/14_copied.mlcfg
#	creusot/tests/should_succeed/iterators/15_enumerate.mlcfg
#	creusot/tests/should_succeed/iterators/16_take.mlcfg
#	creusot/tests/should_succeed/knapsack.mlcfg
#	creusot/tests/should_succeed/knapsack_full.mlcfg
#	creusot/tests/should_succeed/lang/branch_borrow_2.mlcfg
#	creusot/tests/should_succeed/lang/move_path.mlcfg
#	creusot/tests/should_succeed/lang/promoted_constants.mlcfg
#	creusot/tests/should_succeed/lang/while_let.mlcfg
#	creusot/tests/should_succeed/list_index_mut.mlcfg
#	creusot/tests/should_succeed/list_reversal_lasso.mlcfg
#	creusot/tests/should_succeed/loop.mlcfg
#	creusot/tests/should_succeed/mapping_test.mlcfg
#	creusot/tests/should_succeed/mutex.mlcfg
#	creusot/tests/should_succeed/one_side_update.mlcfg
#	creusot/tests/should_succeed/option.mlcfg
#	creusot/tests/should_succeed/projection_toggle.mlcfg
#	creusot/tests/should_succeed/projections.mlcfg
#	creusot/tests/should_succeed/prophecy.mlcfg
#	creusot/tests/should_succeed/red_black_tree.mlcfg
#	creusot/tests/should_succeed/resolve_uninit.mlcfg
#	creusot/tests/should_succeed/result/own.mlcfg
#	creusot/tests/should_succeed/result/result.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max_3.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max_many.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max_repeat.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_some_2_list.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_some_2_tree.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_some_list.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_some_tree.mlcfg
#	creusot/tests/should_succeed/selection_sort_generic.mlcfg
#	creusot/tests/should_succeed/slices/01.mlcfg
#	creusot/tests/should_succeed/sparse_array.mlcfg
#	creusot/tests/should_succeed/split_borrow.mlcfg
#	creusot/tests/should_succeed/sum.mlcfg
#	creusot/tests/should_succeed/sum_of_odds.mlcfg
#	creusot/tests/should_succeed/swap_borrows.mlcfg
#	creusot/tests/should_succeed/switch.mlcfg
#	creusot/tests/should_succeed/syntax/04_assoc_prec.mlcfg
#	creusot/tests/should_succeed/syntax/09_maintains.mlcfg
#	creusot/tests/should_succeed/syntax/12_ghost_code.mlcfg
#	creusot/tests/should_succeed/syntax/13_vec_macro.mlcfg
#	creusot/tests/should_succeed/syntax/derive_macros.mlcfg
#	creusot/tests/should_succeed/take_first_mut.mlcfg
#	creusot/tests/should_succeed/trait_impl.mlcfg
#	creusot/tests/should_succeed/traits/16_impl_cloning.mlcfg
#	creusot/tests/should_succeed/type_invariants/borrows.mlcfg
#	creusot/tests/should_succeed/type_invariants/vec_inv.mlcfg
#	creusot/tests/should_succeed/unnest.mlcfg
#	creusot/tests/should_succeed/vector/01.mlcfg
#	creusot/tests/should_succeed/vector/02_gnome.mlcfg
#	creusot/tests/should_succeed/vector/03_knuth_shuffle.mlcfg
#	creusot/tests/should_succeed/vector/06_knights_tour.mlcfg
#	creusot/tests/should_succeed/vector/07_read_write.mlcfg
#	creusot/tests/should_succeed/vector/08_haystack.mlcfg
#	creusot/tests/should_succeed/vector/09_capacity.mlcfg
let rec cfg test_never [#"../match.rs" 54 0 54 53] [@cfg:stackify] [@cfg:subregion_analysis] (x : Core_Result_Result_Type.t_result (borrowed uint32, borrowed uint32) ()) : ()
requires {[#"../match.rs" 52 0 52 72] let x = match x with
| Core_Result_Result_Type.C_Ok x -> x
| Core_Result_Result_Type.C_Err x -> absurd
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@xldenis this is the issue I was referring to in #972

}
}

#[requires({let x = match x {Ok(x) => x, Err(x) => x}; *(x.0) == 0u32})]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is generated from here

dewert99 and others added 13 commits March 20, 2024 14:23
# Conflicts:
#	creusot/Cargo.toml
#	creusot/tests/should_succeed/100doors.mlcfg
#	creusot/tests/should_succeed/bug/874.mlcfg
#	creusot/tests/should_succeed/hillel.mlcfg
#	creusot/tests/should_succeed/iterators/03_std_iterators.mlcfg
#	creusot/tests/should_succeed/iterators/08_collect_extend.mlcfg
#	creusot/tests/should_succeed/knapsack_full.mlcfg
#	creusot/tests/should_succeed/rusthorn/inc_max_repeat.mlcfg
#	creusot/tests/should_succeed/selection_sort_generic.mlcfg
#	creusot/tests/should_succeed/sum.mlcfg
#	creusot/tests/should_succeed/sum_of_odds.mlcfg
#	creusot/tests/should_succeed/syntax/13_vec_macro.mlcfg
#	creusot/tests/should_succeed/vector/01.mlcfg
#	creusot/tests/should_succeed/vector/03_knuth_shuffle.mlcfg
#	creusot/tests/should_succeed/vector/06_knights_tour.mlcfg
#	creusot/tests/should_succeed/vector/08_haystack.mlcfg
(cherry picked from commit 98cc00f)
(cherry picked from commit 8f66a1f)
(cherry picked from commit 379db64)
chore: Release creusot-args version 0.2.0

# Conflicts:
#	Cargo.lock
#	cargo-creusot/Cargo.toml
#	creusot-args/Cargo.toml
#	creusot-contracts-dummy/Cargo.toml
#	creusot-contracts-proc/Cargo.toml
#	creusot-contracts/Cargo.toml
#	creusot-deps.opam
#	creusot-dev-config/Cargo.toml
#	creusot-metadata/Cargo.toml
#	creusot-rustc/Cargo.toml
#	creusot-setup/Cargo.toml
#	creusot-setup/src/tools_versions_urls.rs
#	creusot/Cargo.toml
#	creusot/tests/should_succeed/100doors.mlcfg
#	creusot/tests/should_succeed/bug/874.mlcfg
#	creusot/tests/should_succeed/hillel.mlcfg
#	creusot/tests/should_succeed/insertion_sort.mlcfg
#	creusot/tests/should_succeed/iterators/03_std_iterators.mlcfg
#	creusot/tests/should_succeed/iterators/08_collect_extend.mlcfg
#	creusot/tests/should_succeed/knapsack_full.mlcfg
#	creusot/tests/should_succeed/red_black_tree/why3session.xml
#	creusot/tests/should_succeed/red_black_tree/why3shapes.gz
#	creusot/tests/should_succeed/rusthorn/inc_max_repeat.mlcfg
#	creusot/tests/should_succeed/selection_sort_generic.mlcfg
#	creusot/tests/should_succeed/sum.mlcfg
#	creusot/tests/should_succeed/sum_of_odds.mlcfg
#	creusot/tests/should_succeed/syntax/13_vec_macro.mlcfg
#	creusot/tests/should_succeed/vector/01.mlcfg
#	creusot/tests/should_succeed/vector/03_knuth_shuffle.mlcfg
#	creusot/tests/should_succeed/vector/06_knights_tour.mlcfg
#	creusot/tests/should_succeed/vector/08_haystack.mlcfg
#	creusot/tests/ui.rs
#	mlcfg
#	pearlite-syn/Cargo.toml
#	why3/Cargo.toml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants