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

Ghost Constraint Stabilization #1271

Merged
merged 66 commits into from
Jan 13, 2023
Merged

Conversation

juliand665
Copy link
Contributor

@juliand665 juliand665 commented Jan 2, 2023

As part of my thesis under @Aurel300, I've been writing specs for the standard library (#1249), which will ship with Prusti for users to benefit from.

Ghost Constraints → Type-Conditional Spec Refinements

These specs often rely on ghost constraints, e.g. to provide a way to opt out of specs applied by default to certain trait implementations, or to specify known return values for e.g. core::mem::size_of. Since these specs will be active by default, they would require enabling ghost constraints to use Prusti at all (barring the option of conditionalizing specs on enabled Prusti features somehow). Hence we propose to stabilize ghost constraints, addressing some issues with them to get them to a place where this makes sense.

Ghost constraints are now called type-conditional spec refinements (TypeCondSpec for short) and spelled as follows:

#[refine_spec(where Self: Copy, T: Copy, [
    pure,
    ensures(result === T::default())
])

The where draws a clear analogy to existing Rust syntax to help users understand this attribute, and it even results in correct syntax highlighting for the constraints. Note also that pure is now supported here (correctly—my previous implementation was broken), and that you can apply multiple bounds.

Extern Spec Improvements

I also polished up & resolved some limitations with extern specs:

  • An error is now thrown when extern specs are applied to methods with bodies, rather than stubs.
  • They can now be applied to trait impls with generic arguments, which was previously cautiously rejected. There is a unit test ensuring that this works correctly.
  • Extern specs on traits now support bounds in a where clause.
  • They can now be applied to free functions, taking an argument to specify that function's module path, e.g. #[extern_spec(core::mem)] fn swap(...). This argument also works on traits & modules (impls already allow qualifying involved type paths).

Miscellaneous

As part of this PR, I enabled auto-formatting for the prusti-specs crate, and reformatted it in the process. This crate is one of the last few holdovers that weren't yet auto-formatted, the other big one being prusti-viper. It looked like this crate is not currently being majorly worked on by anyone else!

Finally, I fixed a bug where lifetimes would make trait method resolution fail, by instead erasing all regions involved in the resolution process. This made some tests fail before I reworked the piece of code that encodes lifetimes for function calls. This bug made it impossible to specify e.g. impl Add<&i32> for i32, because Prusti could not resolve the general Add::add to this specific implementation (due to the lifetimes necessarily differing between call site & declaration site).

turns out the previous implementation was flawed in that it made the function pure whether or not the constraint held
you can still introduce unsound behavior with them, but they're far from the only way to do so, and absolutely necessary for prusti std
removed a check preventing use of this feature previously; this effectively formalizes it as working
expressions are not implicitly in parentheses, creating very strange errors when desugaring e.g. a + b to &a + b
not thoroughly tested, but let's not let perfect be the enemy of good enough
not thoroughly tested, but let's not let perfect be the enemy of good enough
testing-specific workaround is now only present when cfg(test) is active
@@ -1488,11 +1488,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> {
// The called method might be a trait method.
// We try to resolve it to the concrete implementation
// and type substitutions.
let query = self.encoder.env().query;
Copy link
Member

Choose a reason for hiding this comment

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

Why this alias? It doesn't seem to be used later on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without it, the long line spilled over into a silly-looking chained function call spanning like 5 lines, lol. I considered updating the later usage, but that felt worse. Should I just accept the other formatting rather than polluting this long function's namespace?

got lost in the refactor; i blame copilot lol
better no information than inaccurate information!
while preserving the safety of parenthesizing incoming LHS/RHS
the latter didn't work anyway, and now that we have type-cond-specs, we know we won't need it, so we can drop the annotations entirely.
we were getting spurious failures for the core proof tests from old data
@Aurel300 Aurel300 merged commit 87351cc into viperproject:master Jan 13, 2023
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.

3 participants