diff --git a/docs/dev-guide/src/config/flags.md b/docs/dev-guide/src/config/flags.md index 4087f0d856a..52202754628 100644 --- a/docs/dev-guide/src/config/flags.md +++ b/docs/dev-guide/src/config/flags.md @@ -23,7 +23,6 @@ | [`DUMP_REBORROWING_DAG_IN_DEBUG_INFO`](#dump_reborrowing_dag_in_debug_info) | `bool` | `false` | A | | [`DUMP_VIPER_PROGRAM`](#dump_viper_program) | `bool` | `false` | A | | [`ENABLE_CACHE`](#enable_cache) | `bool` | `true` | A | -| [`ENABLE_GHOST_CONSTRAINTS`](#enable_ghost_constraints) | `bool` | `false` | A | | [`ENABLE_PURIFICATION_OPTIMIZATION`](#enable_purification_optimization) | `bool` | `false` | A | | [`ENABLE_TYPE_INVARIANTS`](#enable_type_invariants) | `bool` | `false` | A | | [`ENABLE_VERIFY_ONLY_BASIC_BLOCK_PATH`](#enable_verify_only_basic_block_path) | `bool` | `false` | A | @@ -180,15 +179,6 @@ You can find them either in `log/viper_program` or `target/verify/log/viper_prog When enabled, verification requests (to verify individual `fn`s) are cached to improve future verification. By default the cache is only saved in memory (of the `prusti-server` if enabled). For long-running verification projects use [`CACHE_PATH`](#cache_path) to save to disk. -## `ENABLE_GHOST_CONSTRAINTS` - -When enabled, ghost constraints can be used in Prusti specifications. - -Ghost constraints allow for specifications which are only active if a certain "constraint" (i.e. a trait bound -on a generic type parameter) is satisfied. - -**This is an experimental feature**, because it is currently possible to introduce unsound verification behavior. - ## `ENABLE_PURIFICATION_OPTIMIZATION` When enabled, impure methods are optimized using the purification optimization, which tries to convert heap operations to pure (snapshot-based) operations. diff --git a/docs/user-guide/src/SUMMARY.md b/docs/user-guide/src/SUMMARY.md index 7f6270fee66..baf9cde630d 100644 --- a/docs/user-guide/src/SUMMARY.md +++ b/docs/user-guide/src/SUMMARY.md @@ -26,7 +26,7 @@ - [External specifications](verify/external.md) - [Loop body invariants](verify/loop.md) - [Pledges](verify/pledge.md) - - [Trait contract refinement](verify/traits.md) + - [Type-conditional spec refinements](verify/type_cond_spec.md) - [Closures](verify/closure.md) - [Specification entailments](verify/spec_ent.md) - [Type models](verify/type-models.md) diff --git a/docs/user-guide/src/verify/summary.md b/docs/user-guide/src/verify/summary.md index c4d9cb1e905..7bf7be4a336 100644 --- a/docs/user-guide/src/verify/summary.md +++ b/docs/user-guide/src/verify/summary.md @@ -17,7 +17,7 @@ The following features are either currently supported or planned to be supported - [External specifications](external.md) - [Loop body invariants](loop.md) - [Pledges](pledge.md) -- [Trait contract refinement](traits.md) +- [Type-conditional spec refinements](type_cond_spec.md) - [Closures](closure.md) - [Specification entailments](spec_ent.md) - [Type models](type-models.md) diff --git a/docs/user-guide/src/verify/traits.md b/docs/user-guide/src/verify/traits.md deleted file mode 100644 index 3fac5a91f98..00000000000 --- a/docs/user-guide/src/verify/traits.md +++ /dev/null @@ -1,19 +0,0 @@ -# Trait contract refinement - -> **NOT YET SUPPORTED:** This feature is not yet supported in the new version of Prusti. - -In some cases, marker traits simply modify the behavior of methods in their super-traits. For instance consider the `PartialEq` and `Eq` traits. In order to consider this additional behavior for verification, traits support contract refinement on trait level: - -```rust -pub trait PartialEq { - #[ensures(/* partial equivalence formulas */)] - fn eq(&self, other: &Rhs) -> bool; -} - -#[refine_ensures(PartialEq::eq = "self.eq(&self)")] -pub trait Eq: PartialEq {} -``` - -Thus, any client implementing `Eq` on a custom type can take advantage of the additional semantics of the total equivalence. Similarly `#[refine_requires]` can be used to refine the precondition of a super-trait. - -> Such trait refinement is not scoped. Therefore, considering the previous example, implementing `Eq` on a type implies that the total equivalence contract is always considered on the type, irrespective of whether `Eq` is in scope or not. diff --git a/docs/user-guide/src/verify/type_cond_spec.md b/docs/user-guide/src/verify/type_cond_spec.md new file mode 100644 index 00000000000..ff8a9c0e198 --- /dev/null +++ b/docs/user-guide/src/verify/type_cond_spec.md @@ -0,0 +1,35 @@ +# Type-Conditional Spec Refinement + +When specifying trait methods or generic functions, there is often a special case that allows for more complete specification. In these cases, you can attach a type-conditional spec refinement attribute to the function in question, spelled e.g. `#[refine_spec(where T: A + B, U: C, [requires(true), pure])]` + +For example, one could use this to specify a function like `core::mem::size_of` by defining a trait for types whose size we'd like to specify: + +```rust +#[pure] +#[refine_spec(where T: KnownSize, [ + ensures(result == T::size()), +])] +fn size_of() -> usize; + +pub trait KnownSize { + #[pure] + fn size() -> usize; +} +``` + +> Note that the involved functions are marked as `pure`, allowing them to be used within specifications. This is another common use case, because functions can only be `pure` if their parameters and result are `Copy`, so it is often useful to specify something like `#[refine_spec(where T: Copy, [pure])]`. + +There are some marker traits which simply modify the behavior of methods in their super-traits. For instance, consider the `PartialEq` and `Eq` traits. In order to consider this additional behavior for verification, we can refine the contract of `PartialEq::eq` when the type is known to be marked `Eq`: + +```rust +pub trait PartialEq { + #[refine_spec(where Self: Eq, [ + ensures(self == self), // reflexive + // we could write more specs here + ])] + #[ensures(/* partial equivalence formulas */)] + fn eq(&self, other: &Rhs) -> bool; +} +``` + +Thus, any client implementing `Eq` on a custom type can take advantage of the additional semantics of the total equivalence. diff --git a/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs b/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs index 2edd74c80cc..2c56de6d0a2 100644 --- a/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs +++ b/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs @@ -90,7 +90,7 @@ pub fn model(_attr: TokenStream, _tokens: TokenStream) -> TokenStream { #[cfg(not(feature = "prusti"))] #[proc_macro_attribute] -pub fn ghost_constraint(_attr: TokenStream, tokens: TokenStream) -> TokenStream { +pub fn refine_spec(_attr: TokenStream, tokens: TokenStream) -> TokenStream { tokens } @@ -221,13 +221,8 @@ pub fn model(_attr: TokenStream, tokens: TokenStream) -> TokenStream { #[cfg(feature = "prusti")] #[proc_macro_attribute] -pub fn ghost_constraint(attr: TokenStream, tokens: TokenStream) -> TokenStream { - rewrite_prusti_attributes( - SpecAttributeKind::GhostConstraint, - attr.into(), - tokens.into(), - ) - .into() +pub fn refine_spec(attr: TokenStream, tokens: TokenStream) -> TokenStream { + rewrite_prusti_attributes(SpecAttributeKind::RefineSpec, attr.into(), tokens.into()).into() } #[cfg(feature = "prusti")] diff --git a/prusti-contracts/prusti-contracts/src/lib.rs b/prusti-contracts/prusti-contracts/src/lib.rs index 6fe66caae8f..9c299eeb7ef 100644 --- a/prusti-contracts/prusti-contracts/src/lib.rs +++ b/prusti-contracts/prusti-contracts/src/lib.rs @@ -45,7 +45,7 @@ pub use prusti_contracts_proc_macros::model; /// A macro to add trait bounds on a generic type parameter and specifications /// which are active only when these bounds are satisfied for a call. -pub use prusti_contracts_proc_macros::ghost_constraint; +pub use prusti_contracts_proc_macros::refine_spec; /// A macro for defining ghost blocks which will be left in for verification /// but omitted during compilation. diff --git a/prusti-contracts/prusti-specs/src/common.rs b/prusti-contracts/prusti-specs/src/common.rs index 3a4e1513715..25cae0947f9 100644 --- a/prusti-contracts/prusti-specs/src/common.rs +++ b/prusti-contracts/prusti-specs/src/common.rs @@ -1,20 +1,20 @@ //! Common code for spec-rewriting -use std::borrow::BorrowMut; -use std::collections::HashMap; use proc_macro2::Ident; -use syn::{GenericParam, parse_quote, TypeParam}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use uuid::Uuid; -pub(crate) use syn_extensions::*; -pub(crate) use self_type_rewriter::*; pub(crate) use receiver_rewriter::*; +pub(crate) use self_type_rewriter::*; +use std::{borrow::BorrowMut, collections::HashMap}; +use syn::{parse_quote, punctuated::Punctuated, spanned::Spanned, GenericParam, TypeParam}; +pub(crate) use syn_extensions::*; +use uuid::Uuid; /// Module which provides various extension traits for syn types. /// These allow for writing of generic code over these types. mod syn_extensions { - use syn::{Attribute, Generics, ImplItemMacro, ImplItemMethod, ItemFn, ItemImpl, ItemStruct, ItemTrait, Macro, Signature, TraitItemMacro, TraitItemMethod}; + use syn::{ + Attribute, Generics, ImplItemMacro, ImplItemMethod, ItemFn, ItemImpl, ItemStruct, + ItemTrait, Macro, Signature, TraitItemMacro, TraitItemMethod, + }; /// Trait which signals that the corresponding syn item contains generics pub(crate) trait HasGenerics { @@ -26,28 +26,36 @@ mod syn_extensions { fn generics(&self) -> &Generics { self } - fn generics_mut(&mut self) -> &mut Generics { self } + fn generics_mut(&mut self) -> &mut Generics { + self + } } impl HasGenerics for ItemTrait { fn generics(&self) -> &Generics { &self.generics } - fn generics_mut(&mut self) -> &mut Generics { &mut self.generics } + fn generics_mut(&mut self) -> &mut Generics { + &mut self.generics + } } impl HasGenerics for ItemStruct { fn generics(&self) -> &Generics { &self.generics } - fn generics_mut(&mut self) -> &mut Generics { &mut self.generics } + fn generics_mut(&mut self) -> &mut Generics { + &mut self.generics + } } impl HasGenerics for ItemImpl { fn generics(&self) -> &syn::Generics { &self.generics } - fn generics_mut(&mut self) -> &mut Generics { &mut self.generics } + fn generics_mut(&mut self) -> &mut Generics { + &mut self.generics + } } /// Abstraction over everything that has a [syn::Signature] @@ -129,7 +137,7 @@ mod syn_extensions { &self.attrs } } - + impl HasAttributes for ItemFn { fn attrs(&self) -> &Vec { &self.attrs @@ -141,7 +149,7 @@ mod syn_extensions { mod self_type_rewriter { use syn::{ parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, ImplItemMethod, ItemFn, Type, - TypePath, + TypePath, WhereClause, }; /// Given a replacement for the `Self` type and the trait it should fulfill, @@ -185,6 +193,16 @@ mod self_type_rewriter { } } + impl SelfTypeRewriter for WhereClause { + fn rewrite_self_type(&mut self, self_type: &Type, self_type_trait: Option<&TypePath>) { + let mut rewriter = Rewriter { + self_type, + self_type_trait, + }; + rewriter.rewrite_where_clause(self); + } + } + struct Rewriter<'a> { self_type: &'a Type, self_type_trait: Option<&'a TypePath>, @@ -198,6 +216,10 @@ mod self_type_rewriter { pub fn rewrite_item_fn(&mut self, item: &mut syn::ItemFn) { syn::visit_mut::visit_item_fn_mut(self, item); } + + pub fn rewrite_where_clause(&mut self, where_clause: &mut WhereClause) { + syn::visit_mut::visit_where_clause_mut(self, where_clause); + } } impl<'a> VisitMut for Rewriter<'a> { @@ -241,9 +263,10 @@ mod self_type_rewriter { mod receiver_rewriter { use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{quote, quote_spanned}; - use syn::{FnArg, ImplItemMethod, ItemFn, Macro, parse_quote_spanned, Type}; - use syn::spanned::Spanned; - use syn::visit_mut::VisitMut; + use syn::{ + parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, FnArg, ImplItemMethod, ItemFn, + Macro, Type, + }; /// Rewrites the receiver of a method-like item. /// This can be used to convert impl methods to free-standing functions. @@ -267,14 +290,14 @@ mod receiver_rewriter { impl RewritableReceiver for ImplItemMethod { fn rewrite_receiver(&mut self, new_ty: &Type) { - let mut rewriter = Rewriter {new_ty}; + let mut rewriter = Rewriter { new_ty }; rewriter.rewrite_impl_item_method(self); } } impl RewritableReceiver for ItemFn { fn rewrite_receiver(&mut self, new_ty: &Type) { - let mut rewriter = Rewriter {new_ty}; + let mut rewriter = Rewriter { new_ty }; rewriter.rewrite_item_fn(self); } } @@ -296,13 +319,15 @@ mod receiver_rewriter { let tokens_span = tokens.span(); let rewritten = TokenStream::from_iter(tokens.into_iter().map(|token| match token { TokenTree::Group(group) => { - let new_group = - proc_macro2::Group::new(group.delimiter(), Self::rewrite_tokens(group.stream())); + let new_group = proc_macro2::Group::new( + group.delimiter(), + Self::rewrite_tokens(group.stream()), + ); TokenTree::Group(new_group) } TokenTree::Ident(ident) if ident == "self" => { TokenTree::Ident(proc_macro2::Ident::new("_self", ident.span())) - }, + } other => other, })); parse_quote_spanned! {tokens_span=> @@ -316,17 +341,15 @@ mod receiver_rewriter { if let FnArg::Receiver(receiver) = fn_arg { let span = receiver.span(); let and = match &receiver.reference { - Some((_, Some(lifetime))) => - quote_spanned!{span => &#lifetime}, - Some((_, None)) => - quote_spanned!{span => &}, - None => quote! {} + Some((_, Some(lifetime))) => quote_spanned! {span => &#lifetime}, + Some((_, None)) => quote_spanned! {span => &}, + None => quote! {}, }; let mutability = &receiver.mutability; let new_ty = self.new_ty; let new_fn_arg: FnArg = parse_quote_spanned! {span=> - _self : #and #mutability #new_ty - }; + _self : #and #mutability #new_ty + }; *fn_arg = new_fn_arg; } else { syn::visit_mut::visit_fn_arg_mut(self, fn_arg); @@ -380,9 +403,12 @@ pub(crate) fn merge_generics(target: &mut T, source: &T) { if let GenericParam::Type(type_param_source) = param_source { // We can remove the target type param here, because the source will not have the // same type param with the same identifiers - let maybe_type_param_source = existing_target_type_params.remove(&type_param_source.ident); + let maybe_type_param_source = + existing_target_type_params.remove(&type_param_source.ident); if let Some(type_param_target) = maybe_type_param_source { - type_param_target.bounds.extend(type_param_source.bounds.clone()); + type_param_target + .bounds + .extend(type_param_source.bounds.clone()); } else { new_generic_params.push(GenericParam::Type(type_param_source.clone())); } @@ -398,8 +424,13 @@ pub(crate) fn merge_generics(target: &mut T, source: &T) { } // Merge the where clause - match (generics_target.where_clause.as_mut(), generics_source.where_clause.as_ref()) { - (Some(target_where), Some(source_where)) => target_where.predicates.extend(source_where.predicates.clone()), + match ( + generics_target.where_clause.as_mut(), + generics_source.where_clause.as_ref(), + ) { + (Some(target_where), Some(source_where)) => target_where + .predicates + .extend(source_where.predicates.clone()), (None, Some(source_where)) => generics_target.where_clause = Some(source_where.clone()), _ => (), } @@ -487,8 +518,8 @@ mod tests { use super::*; mod merge_generics { - use syn::parse_quote; use crate::merge_generics; + use syn::parse_quote; macro_rules! test_merge { ([$($source:tt)+] into [$($target:tt)+] gives [$($expected:tt)+]) => { diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/common.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/common.rs index 697e67f49c0..69e00ba7ed5 100644 --- a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/common.rs +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/common.rs @@ -1,28 +1,33 @@ -use crate::{ExternSpecKind, extract_prusti_attributes, generate_spec_and_assertions, RewritableReceiver, SelfTypeRewriter}; -use quote::{quote, ToTokens}; -use syn::{Expr, FnArg, GenericParam, GenericArgument, parse_quote_spanned, Pat, PatType, Token}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use crate::common::{HasAttributes, HasSignature}; -use crate::span_overrider::SpanOverrider; -use crate::untyped::AnyFnItem; -use syn::visit::Visit; -use syn::visit_mut::VisitMut; +use crate::{ + common::{HasAttributes, HasSignature}, + extract_prusti_attributes, generate_spec_and_assertions, + span_overrider::SpanOverrider, + untyped::AnyFnItem, + ExternSpecKind, RewritableReceiver, SelfTypeRewriter, +}; +use itertools::Itertools; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::{ + parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, visit::Visit, + visit_mut::VisitMut, Expr, FnArg, GenericArgument, GenericParam, Pat, PatType, Token, +}; /// Counts the number of elided lifetimes in receivers and types. /// For details see the function `with_explicit_lifetimes`. struct ElidedLifetimeCounter { - num_elided_lifetimes: u32 + num_elided_lifetimes: u32, } - impl ElidedLifetimeCounter { fn new() -> ElidedLifetimeCounter { - ElidedLifetimeCounter { num_elided_lifetimes: 0 } + ElidedLifetimeCounter { + num_elided_lifetimes: 0, + } } } -impl <'ast> syn::visit::Visit<'ast> for ElidedLifetimeCounter { +impl<'ast> syn::visit::Visit<'ast> for ElidedLifetimeCounter { fn visit_receiver(&mut self, receiver: &syn::Receiver) { if let Some((_, None)) = receiver.reference { self.num_elided_lifetimes += 1; @@ -36,7 +41,6 @@ impl <'ast> syn::visit::Visit<'ast> for ElidedLifetimeCounter { } } - fn has_multiple_elided_lifetimes(inputs: &Punctuated) -> bool { let mut visitor = ElidedLifetimeCounter::new(); for input in inputs { @@ -60,31 +64,34 @@ fn returns_reference_with_elided_lifetime(return_type: &syn::ReturnType) -> bool /// annotations. The explicit lifetime annotations correspond to what Rust would assign /// for the elided lifetimes in the original signature. fn with_explicit_lifetimes(sig: &syn::Signature) -> Option { - // This struct is responsible for inserting an explicit lifetime for elided // lifetimes in the receiver and output type. struct SelfLifetimeInserter {} impl syn::visit_mut::VisitMut for SelfLifetimeInserter { fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) { - reference.lifetime = parse_quote_spanned!{reference.span() => 'prusti_self_lifetime }; + reference.lifetime = parse_quote_spanned! {reference.span() => 'prusti_self_lifetime }; } fn visit_receiver_mut(&mut self, receiver: &mut syn::Receiver) { - receiver.reference.as_mut().unwrap().1 = parse_quote_spanned!{receiver.span() => 'prusti_self_lifetime }; + receiver.reference.as_mut().unwrap().1 = + parse_quote_spanned! {receiver.span() => 'prusti_self_lifetime }; } } - if !returns_reference_with_elided_lifetime(&sig.output) || - !has_multiple_elided_lifetimes(&sig.inputs) + if !returns_reference_with_elided_lifetime(&sig.output) + || !has_multiple_elided_lifetimes(&sig.inputs) { return None; } let mut new_sig = sig.clone(); - let mut inserter = SelfLifetimeInserter{}; + let mut inserter = SelfLifetimeInserter {}; // Insert explicit lifetime parameter to method signature - new_sig.generics.params.insert(0, parse_quote_spanned!{new_sig.generics.params.span() => 'prusti_self_lifetime }); + new_sig.generics.params.insert( + 0, + parse_quote_spanned! {new_sig.generics.params.span() => 'prusti_self_lifetime }, + ); // Assign the explicit lifetime to the reference to self if let Some(syn::FnArg::Receiver(r)) = new_sig.inputs.first_mut() { @@ -133,13 +140,14 @@ pub(crate) fn generate_extern_spec_method_stub parse_quote_spanned! {method_sig_span=> <#self_type> :: #method_ident - } + }, }; // Build the method stub - let stub_method: syn::ImplItemMethod = - generate_extern_spec_function_stub(method, &method_path, extern_spec_kind); - + let stub_method = + generate_extern_spec_function_stub(method, &method_path, extern_spec_kind, false); + let stub_method: syn::ImplItemMethod = syn::parse2(stub_method)?; + // Eagerly extract and process specifications let mut stub_method = AnyFnItem::ImplMethod(stub_method); let prusti_attributes = extract_prusti_attributes(&mut stub_method); @@ -155,7 +163,10 @@ pub(crate) fn generate_extern_spec_method_stub( +pub(crate) fn generate_extern_spec_function_stub( function: &Input, fn_path: &syn::ExprPath, extern_spec_kind: ExternSpecKind, -) -> Output { + mangle_name: bool, +) -> TokenStream { let signature = function.sig(); + let mut signature = with_explicit_lifetimes(signature).unwrap_or_else(|| signature.clone()); + if mangle_name { + signature.ident = format_ident!("prusti_extern_spec_{}", signature.ident); + } // Make elided lifetimes explicit, if necessary. - let signature = with_explicit_lifetimes(signature).unwrap_or_else(|| signature.clone()); let attrs = function.attrs().clone(); let generic_params = &signature.generic_params_as_call_args(); let args = &signature.params_as_call_args(); let extern_spec_kind_string: String = extern_spec_kind.into(); - - parse_quote_spanned! {function.span()=> + + quote_spanned! {function.span()=> #[trusted] #[prusti::extern_spec = #extern_spec_kind_string] #(#attrs)* @@ -216,20 +231,17 @@ impl MethodParamsAsCallArguments for H { impl MethodParamsAsCallArguments for Punctuated { fn params_as_call_args(&self) -> Punctuated { - Punctuated::from_iter( - self.iter() - .map(|param| -> Expr { - let span = param.span(); - match param { - FnArg::Typed(PatType { pat: box Pat::Ident(ident), .. }) => - parse_quote_spanned! {span=>#ident }, - FnArg::Receiver(_) => - parse_quote_spanned! {span=>self}, - _ => - unimplemented!(), - } - }) - ) + Punctuated::from_iter(self.iter().map(|param| -> Expr { + let span = param.span(); + match param { + FnArg::Typed(PatType { + pat: box Pat::Ident(ident), + .. + }) => parse_quote_spanned! {span=>#ident }, + FnArg::Receiver(_) => parse_quote_spanned! {span=>self}, + _ => unimplemented!(), + } + })) } } @@ -246,20 +258,18 @@ impl GenericParamsAsCallArguments for H { impl GenericParamsAsCallArguments for Punctuated { fn generic_params_as_call_args(&self) -> Punctuated { use syn::*; - Punctuated::from_iter( - self.iter() - .flat_map(|param| -> Option { - let span = param.span(); - match param { - GenericParam::Type(TypeParam { ident, .. }) => - Some(parse_quote_spanned! {span=>#ident }), - GenericParam::Lifetime(_) => - None, - GenericParam::Const(ConstParam { ident, .. }) => - Some(parse_quote_spanned! {span=>#ident }), - } - }) - ) + Punctuated::from_iter(self.iter().flat_map(|param| -> Option { + let span = param.span(); + match param { + GenericParam::Type(TypeParam { ident, .. }) => { + Some(parse_quote_spanned! {span=>#ident }) + } + GenericParam::Lifetime(_) => None, + GenericParam::Const(ConstParam { ident, .. }) => { + Some(parse_quote_spanned! {span=>#ident }) + } + } + })) } } @@ -279,7 +289,10 @@ impl GenericParamsAsCallArguments for Punctuated { /// } /// ``` pub fn add_phantom_data_for_generic_params(item_struct: &mut syn::ItemStruct) { - let fields = item_struct.generics.params.iter() + let fields = item_struct + .generics + .params + .iter() .flat_map(|param| match param { syn::GenericParam::Type(tp) => { let ident = tp.ident.clone(); @@ -306,7 +319,9 @@ pub fn rewrite_generics(gens: &syn::Generics) -> syn::AngleBracketedGenericArgum .map(|gp| { let ts = match gp { syn::GenericParam::Type(syn::TypeParam { ident, .. }) - | syn::GenericParam::Const(syn::ConstParam { ident, .. }) => ident.into_token_stream(), + | syn::GenericParam::Const(syn::ConstParam { ident, .. }) => { + ident.into_token_stream() + } syn::GenericParam::Lifetime(ld) => ld.lifetime.into_token_stream(), }; syn::parse2::(ts).unwrap() @@ -314,3 +329,28 @@ pub fn rewrite_generics(gens: &syn::Generics) -> syn::AngleBracketedGenericArgum .collect(); syn::parse_quote! { < #(#args),* > } } + +/// Checks that the given block is a stub (`;`), returning an error otherwise. +pub(super) fn check_is_stub(block: &syn::Block) -> Result<(), syn::Error> { + if is_stub(block) { + Ok(()) + } else { + Err(syn::Error::new( + block.span(), + "Unexpected method body. (Extern specs only define specifications.)", + )) + } +} + +/// Recognizes method stubs, e.g. `fn foo();`. +/// +/// The absence of a body is represented in a roundabout way: +/// They have a body comprising a single verbatim item containing a single semicolon token. +fn is_stub(block: &syn::Block) -> bool { + if let Ok(Some(syn::Stmt::Item(syn::Item::Verbatim(tokens)))) = block.stmts.iter().at_most_one() + { + tokens.to_string() == ";" + } else { + false + } +} diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/functions.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/functions.rs new file mode 100644 index 00000000000..0df7ef84aa3 --- /dev/null +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/functions.rs @@ -0,0 +1,51 @@ +//! Process external specifications on free functions. +//! In practice, these will be combined with a module argument to extern_spec +//! e.g. `#[extern_spec(core::mem)] fn swap` + +use super::common::generate_extern_spec_function_stub; +use crate::ExternSpecKind; +use proc_macro2::{Group, TokenStream, TokenTree}; +use quote::{quote, ToTokens}; +use syn::{parse_quote_spanned, spanned::Spanned}; + +pub fn rewrite_stub(stub_tokens: &TokenStream, path: &syn::Path) -> syn::Result { + // Transforms function stubs (functions with a `;` after the + // signature instead of the body) into functions, then + // processes them. + let mut new_tokens = TokenStream::new(); + for mut token in stub_tokens.clone().into_iter() { + if let TokenTree::Punct(punct) = &mut token { + if punct.as_char() == ';' { + new_tokens.extend( + Group::new(proc_macro2::Delimiter::Brace, TokenStream::new()).to_token_stream(), + ); + continue; + } + } + new_tokens.extend(token.to_token_stream()); + } + let res: Result = syn::parse2(new_tokens); + if res.is_err() { + return Err(syn::Error::new( + stub_tokens.span(), + "invalid function signature", + )); + } + + let mut item = res.unwrap(); + if let syn::Item::Fn(item_fn) = &mut item { + Ok(rewrite_fn(item_fn, path)) + } else { + Ok(quote!(#item)) + } +} + +/// Rewrite a specification function to a call to the specified function. +/// The result of this rewriting is then parsed in `ExternSpecResolver`. +pub fn rewrite_fn(item_fn: &syn::ItemFn, path: &syn::Path) -> TokenStream { + let ident = &item_fn.sig.ident; + let path_span = item_fn.sig.ident.span(); + let path = parse_quote_spanned!(path_span=> #path :: #ident); + + generate_extern_spec_function_stub(item_fn, &path, ExternSpecKind::Method, true) +} diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/impls.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/impls.rs index b15f96bffee..9515be76f18 100644 --- a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/impls.rs +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/impls.rs @@ -1,10 +1,10 @@ -use crate::SPECS_VERSION; -use crate::{ExternSpecKind, is_predicate_macro, specifications::common::generate_struct_name}; +use super::common::*; +use crate::{ + is_predicate_macro, specifications::common::generate_struct_name, ExternSpecKind, SPECS_VERSION, +}; use proc_macro2::TokenStream; use quote::quote_spanned; -use syn::parse_quote_spanned; -use syn::spanned::Spanned; -use super::common::*; +use syn::{parse_quote_spanned, spanned::Spanned}; pub fn rewrite_extern_spec(item_impl: &syn::ItemImpl) -> syn::Result { let rewritten = rewrite_extern_spec_internal(item_impl)?; @@ -32,14 +32,7 @@ fn rewrite_extern_spec_internal(item_impl: &syn::ItemImpl) -> syn::Result) -> )); } syn::ImplItem::Method(method) => { + check_is_stub(&method.block)?; + let (rewritten_method, spec_items) = generate_extern_spec_method_stub( method, item_ty, @@ -101,11 +96,11 @@ fn rewrite_plain_impl(impl_item: &mut syn::ItemImpl, new_ty: Box) -> rewritten_items.extend(spec_items.into_iter().map(syn::ImplItem::Method)); rewritten_items.push(syn::ImplItem::Method(rewritten_method)); - }, + } syn::ImplItem::Macro(makro) if is_predicate_macro(makro) => { return Err(syn::Error::new( makro.span(), - "Can not declare abstract predicate in external spec" + "Can not declare abstract predicate in external spec", )); } _ => { @@ -137,7 +132,8 @@ fn rewrite_trait_impl( new_impl.items.clear(); let item_trait_path = impl_item.trait_.as_ref().unwrap().1.clone(); - let item_trait_typath: syn::TypePath = parse_quote_spanned! {item_trait_path.span()=> #item_trait_path }; + let item_trait_typath: syn::TypePath = + parse_quote_spanned! {item_trait_path.span()=> #item_trait_path }; // TODO: reduce duplication with rewrite_plain_impl for item in impl_item.items.into_iter() { @@ -149,6 +145,8 @@ fn rewrite_trait_impl( )); } syn::ImplItem::Method(method) => { + check_is_stub(&method.block)?; + let (rewritten_method, spec_items) = generate_extern_spec_method_stub( &method, &item_ty, @@ -156,7 +154,9 @@ fn rewrite_trait_impl( ExternSpecKind::TraitImpl, )?; - new_impl.items.extend(spec_items.into_iter().map(syn::ImplItem::Method)); + new_impl + .items + .extend(spec_items.into_iter().map(syn::ImplItem::Method)); new_impl.items.push(syn::ImplItem::Method(rewritten_method)); } _ => { @@ -173,17 +173,6 @@ fn rewrite_trait_impl( Ok(new_impl) } -fn has_generic_arguments(path: &syn::Path) -> bool { - for seg in path.segments.iter() { - if let syn::PathArguments::AngleBracketed(args) = &seg.arguments { - if !args.args.is_empty() { - return true; - } - } - } - false -} - #[cfg(test)] mod tests { use super::rewrite_extern_spec_internal; @@ -340,16 +329,53 @@ mod tests { } #[test] - fn generics_not_supported() { + fn generic_trait() { let mut inp_impl: syn::ItemImpl = parse_quote!( - impl MyTrait for MyStruct { + impl MyTrait for MyStruct { + fn foo(&mut self, arg1: Foo); + } + ); + + let rewritten = rewrite_extern_spec_internal(&mut inp_impl).unwrap(); + + let newtype_ident = &rewritten.generated_struct.ident; + let expected_impl: syn::ItemImpl = parse_quote! { + impl #newtype_ident <> { + #[prusti::extern_spec = "trait_impl"] + #[allow(unused, dead_code)] + #[prusti::trusted] + fn foo(_self: &mut MyStruct, arg1: Foo) { + > :: foo :: <>(_self, arg1) + } + } + }; + + assert_eq_tokenizable(rewritten.generated_impl.clone(), expected_impl); + } + + #[test] + fn generic_blanket_impl() { + let mut inp_impl: syn::ItemImpl = parse_quote!( + impl MyTrait for MyStruct { fn foo(&mut self, arg1: I); } ); - let rewritten = rewrite_extern_spec_internal(&mut inp_impl); + let rewritten = rewrite_extern_spec_internal(&mut inp_impl).unwrap(); - assert!(rewritten.is_err()); + let newtype_ident = &rewritten.generated_struct.ident; + let expected_impl: syn::ItemImpl = parse_quote! { + impl #newtype_ident { + #[prusti::extern_spec = "trait_impl"] + #[allow(unused, dead_code)] + #[prusti::trusted] + fn foo(_self: &mut MyStruct, arg1: I) { + > :: foo :: <>(_self, arg1) + } + } + }; + + assert_eq_tokenizable(rewritten.generated_impl.clone(), expected_impl); } } } diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mod.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mod.rs index d8e8d432d56..119ce3f9dfc 100644 --- a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mod.rs +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mod.rs @@ -3,6 +3,7 @@ pub mod impls; pub mod mods; pub mod traits; +pub mod functions; mod common; #[derive(Debug, Clone, Copy)] diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mods.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mods.rs index dca2acaf78a..b9c9ee3029c 100644 --- a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mods.rs +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/mods.rs @@ -5,82 +5,35 @@ //! Modules are rewritten so that their name does not clash with the module //! they are specifying. -use super::common::generate_extern_spec_function_stub; -use crate::{specifications::common::generate_mod_name, ExternSpecKind}; -use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::{quote, ToTokens}; -use syn::{parse_quote_spanned, spanned::Spanned}; - -pub fn rewrite_extern_spec(item_mod: &mut syn::ItemMod) -> syn::Result { - let path = syn::Path { - leading_colon: None, - segments: syn::punctuated::Punctuated::new(), - }; - rewrite_mod(item_mod, &path)?; - Ok(quote!(#item_mod)) -} - -fn rewrite_mod(item_mod: &mut syn::ItemMod, path: &syn::Path) -> syn::Result<()> { - if item_mod.content.is_none() { - return Ok(()); - } - - let mut path = path.clone(); +use super::{ + common::check_is_stub, + functions::{rewrite_fn, rewrite_stub}, +}; +use proc_macro2::TokenStream; +use syn::spanned::Spanned; + +pub fn rewrite_mod(item_mod: &syn::ItemMod, mut path: syn::Path) -> syn::Result { path.segments.push(syn::PathSegment { ident: item_mod.ident.clone(), arguments: syn::PathArguments::None, }); - item_mod.ident = syn::Ident::new(&generate_mod_name(&item_mod.ident), item_mod.span()); - for item in item_mod.content.as_mut().unwrap().1.iter_mut() { + let mut rewritten_fns = TokenStream::new(); + for item in item_mod.content.as_ref().iter().flat_map(|c| &c.1) { match item { - syn::Item::Fn(item_fn) => { - rewrite_fn(item_fn, &path); + syn::Item::Fn(ref item_fn) => { + check_is_stub(&item_fn.block)?; + rewritten_fns.extend(rewrite_fn(item_fn, &path)); } - syn::Item::Mod(inner_mod) => { - rewrite_mod(inner_mod, &path)?; - } - syn::Item::Verbatim(tokens) => { - // Transforms function stubs (functions with a `;` after the - // signature instead of the body) into functions, then - // processes them. - let mut new_tokens = TokenStream::new(); - for mut token in tokens.clone().into_iter() { - if let TokenTree::Punct(punct) = &mut token { - if punct.as_char() == ';' { - new_tokens.extend( - Group::new(proc_macro2::Delimiter::Brace, TokenStream::new()) - .to_token_stream(), - ); - continue; - } - } - new_tokens.extend(token.to_token_stream()); - } - let res: Result = syn::parse2(new_tokens); - if res.is_err() { - return Err(syn::Error::new(item.span(), "invalid function signature")); - } - - let mut item = res.unwrap(); - if let syn::Item::Fn(item_fn) = &mut item { - rewrite_fn(item_fn, &path); - } - *tokens = quote!(#item) - } - syn::Item::Use(_) => {} + syn::Item::Mod(ref inner_mod) => rewritten_fns.extend(rewrite_mod(inner_mod, path.clone())?), + syn::Item::Verbatim(ref tokens) => rewritten_fns.extend(rewrite_stub(tokens, &path)?), + syn::Item::Use(_) => rewritten_fns.extend(syn::Error::new( + item.span(), + "`use` statements have no effect in #[extern_spec] modules; module contents share the outer scope.", + ).to_compile_error()), _ => return Err(syn::Error::new(item.span(), "unexpected item")), } } - Ok(()) -} - -/// Rewrite a specification function to a call to the specified function. -/// The result of this rewriting is then parsed in `ExternSpecResolver`. -fn rewrite_fn(item_fn: &mut syn::ItemFn, path: &syn::Path) { - let ident = &item_fn.sig.ident; - let path_span = item_fn.sig.ident.span(); - let path = parse_quote_spanned!(path_span=> #path :: #ident); - *item_fn = generate_extern_spec_function_stub(item_fn, &path, ExternSpecKind::Method); + Ok(rewritten_fns) } diff --git a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/traits.rs b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/traits.rs index 15dc31c6b3f..da50ef0b751 100644 --- a/prusti-contracts/prusti-specs/src/extern_spec_rewriter/traits.rs +++ b/prusti-contracts/prusti-specs/src/extern_spec_rewriter/traits.rs @@ -1,11 +1,12 @@ //! Encoding of external specs for traits -use crate::{ExternSpecKind, is_predicate_macro, parse_quote_spanned}; -use crate::specifications::common::generate_struct_name_for_trait; +use super::common::*; +use crate::{ + common::SelfTypeRewriter, is_predicate_macro, parse_quote_spanned, + specifications::common::generate_struct_name_for_trait, ExternSpecKind, +}; use proc_macro2::TokenStream; use quote::{quote_spanned, ToTokens}; -use syn::parse_quote; -use syn::spanned::Spanned; -use super::common::*; +use syn::{parse_quote, spanned::Spanned, TypeParam}; /// Generates a struct for a `syn::ItemTrait` which is used for checking /// compilation of external specs on traits. @@ -25,8 +26,16 @@ use super::common::*; /// ``` /// and a corresponding impl block with methods of `SomeTrait`. /// -pub fn rewrite_extern_spec(item_trait: &syn::ItemTrait) -> syn::Result { - let generated_struct = generate_new_struct(item_trait)?; +pub fn rewrite_extern_spec( + item_trait: &syn::ItemTrait, + mod_path: syn::Path, +) -> syn::Result { + let mut trait_path = mod_path; + trait_path.segments.push(syn::PathSegment { + ident: item_trait.ident.clone(), + arguments: syn::PathArguments::None, + }); + let generated_struct = generate_new_struct(item_trait, trait_path)?; let trait_impl = generated_struct.generate_impl()?; let new_struct = generated_struct.generated_struct; @@ -37,9 +46,10 @@ pub fn rewrite_extern_spec(item_trait: &syn::ItemTrait) -> syn::Result syn::Result { - let trait_ident = &item_trait.ident; - +fn generate_new_struct( + item_trait: &syn::ItemTrait, + trait_path: syn::Path, +) -> syn::Result { let struct_name = generate_struct_name_for_trait(item_trait); let struct_ident = syn::Ident::new(&struct_name, item_trait.span()); @@ -47,30 +57,38 @@ fn generate_new_struct(item_trait: &syn::ItemTrait) -> syn::Result - #trait_ident :: <#(#parsed_generics),*> + #trait_path :: <#(#parsed_generics),*> }; + // Generic type parameters are added as generics to the struct + new_generics.extend(parsed_generics.into_iter().map(syn::GenericParam::Type)); + // Add a where clause which restricts this self type parameter to the trait - if let Some(where_clause) = &item_trait.generics.where_clause { - return Err(syn::Error::new(where_clause.span(), "Where clauses for extern traits specs are not supported")); - } - let self_where_clause: syn::WhereClause = parse_quote! { - where #self_type_ident: #self_type_trait + let self_where_clause: syn::WhereClause = if let Some(where_clause) = + &item_trait.generics.where_clause + { + let mut where_clause = where_clause.clone(); + where_clause.rewrite_self_type(&parse_quote! { #self_type_ident }, Some(&self_type_trait)); + // remove trailing comma + let p = where_clause.predicates.pop().unwrap(); + where_clause.predicates.push(p.into_value()); + // merge into existing where clause + parse_quote! { + #where_clause, #self_type_ident: #self_type_trait + } + } else { + parse_quote! { + where #self_type_ident: #self_type_trait + } }; new_struct.generics.where_clause = Some(self_where_clause); @@ -84,20 +102,13 @@ fn generate_new_struct(item_trait: &syn::ItemTrait) -> syn::Result syn::Result> { - let mut result = vec![]; - for generic_param in item_trait.generics.params.iter() { - if let syn::GenericParam::Type(type_param) = generic_param { - result.push( - ProvidedTypeParam::try_parse(type_param) - .ok_or_else(|| syn::Error::new( - type_param.span(), - "Type parameters in external trait specs must be annotated with exactly one of #[generic] or #[concrete]" - ))?, - ); - } - } - Ok(result) +fn parse_trait_type_params(item_trait: &syn::ItemTrait) -> syn::Result> { + item_trait + .generics + .type_params() + .cloned() + .map(check_for_legacy_attributes) + .collect() } struct GeneratedStruct<'a> { @@ -140,20 +151,19 @@ impl<'a> GeneratedStruct<'a> { } syn::TraitItem::Method(trait_method) => { if let Some(default) = &trait_method.default { - return Err(syn::Error::new( - default.span(), - "Default methods in external trait specs are invalid", - )); + return Err(check_is_stub(default).expect_err("this cannot be a stub")); } let (method, spec_fns) = self.generate_method_stub(trait_method)?; struct_impl.items.push(syn::ImplItem::Method(method)); - struct_impl.items.extend(spec_fns.into_iter().map(syn::ImplItem::Method)); - }, + struct_impl + .items + .extend(spec_fns.into_iter().map(syn::ImplItem::Method)); + } syn::TraitItem::Macro(makro) if is_predicate_macro(makro) => { return Err(syn::Error::new( makro.span(), - "Can not declare abstract predicate in external spec" + "Can not declare abstract predicate in external spec", )); } _ => unimplemented!("Unimplemented trait item for extern spec"), @@ -163,55 +173,32 @@ impl<'a> GeneratedStruct<'a> { Ok(struct_impl) } - fn generate_method_stub(&self, trait_method: &syn::TraitItemMethod) -> syn::Result<(syn::ImplItemMethod, Vec)> { + fn generate_method_stub( + &self, + trait_method: &syn::TraitItemMethod, + ) -> syn::Result<(syn::ImplItemMethod, Vec)> { let self_type_ident = &self.self_type_ident; let self_type_path: syn::TypePath = parse_quote_spanned! {self_type_ident.span()=> #self_type_ident }; let self_type = syn::Type::Path(self_type_path); - generate_extern_spec_method_stub(trait_method, &self_type, Some(&self.self_type_trait), ExternSpecKind::Trait) - } -} - -#[derive(Debug)] -enum ProvidedTypeParam { - /// Something non-concrete, i.e. `T` - ConcreteType(syn::TypeParam), - /// Something concrete, i.e. `i32` - GenericType(syn::TypeParam), -} - -impl ProvidedTypeParam { - fn try_parse(from: &syn::TypeParam) -> Option { - if from.attrs.len() != 1 { - return None; - } - - let path = &from.attrs[0].path; - if path.segments.len() != 1 { - return None; - } - - // Closure for cloning and removing the attrs - let clone_without_attrs = || { - let mut cloned = from.clone(); - cloned.attrs.clear(); - cloned - }; - - match path.segments[0].ident.to_string().as_str() { - "generic" => Some(ProvidedTypeParam::GenericType(clone_without_attrs())), - "concrete" => Some(ProvidedTypeParam::ConcreteType(clone_without_attrs())), - _ => None - } + generate_extern_spec_method_stub( + trait_method, + &self_type, + Some(&self.self_type_trait), + ExternSpecKind::Trait, + ) } } -impl ToTokens for ProvidedTypeParam { - fn to_tokens(&self, tokens: &mut TokenStream) { - match &self { - ProvidedTypeParam::ConcreteType(ty_param) | ProvidedTypeParam::GenericType(ty_param) => ty_param.to_tokens(tokens), - } +fn check_for_legacy_attributes(param: TypeParam) -> syn::Result { + if let Some(attr) = param.attrs.first() { + Err(syn::Error::new( + attr.span(), + "The `#[concrete]` and `#[generic]` attributes are deprecated. To refine specs for specific concrete types, use type-conditional spec refinements instead.", + )) + } else { + Ok(param) } } diff --git a/prusti-contracts/prusti-specs/src/lib.rs b/prusti-contracts/prusti-specs/src/lib.rs index e2387b80582..91d53564f11 100644 --- a/prusti-contracts/prusti-specs/src/lib.rs +++ b/prusti-contracts/prusti-specs/src/lib.rs @@ -11,7 +11,7 @@ #[macro_use] mod common; mod extern_spec_rewriter; -mod ghost_constraints; +mod type_cond_specs; mod parse_closure_macro; mod parse_quote_spanned; mod predicate; @@ -23,7 +23,6 @@ mod type_model; mod user_provided_type_params; mod print_counterexample; - use proc_macro2::{Span, TokenStream, TokenTree}; use quote::{quote, quote_spanned, ToTokens}; use rewriter::AstRewriter; @@ -33,7 +32,7 @@ use syn::{spanned::Spanned, visit::Visit}; use crate::{ common::{merge_generics, RewritableReceiver, SelfTypeRewriter}, predicate::{is_predicate_macro, ParsedPredicate}, - specifications::preparser::{parse_ghost_constraint, parse_prusti, NestedSpec}, + specifications::preparser::{parse_prusti, parse_type_cond_spec, NestedSpec}, }; pub use extern_spec_rewriter::ExternSpecKind; use parse_closure_macro::ClosureWithSpec; @@ -51,21 +50,30 @@ macro_rules! handle_result { }; } +macro_rules! result_to_tokens { + ($body:block) => {{ + let body = || $body; + handle_result!(body()) + }}; +} + fn extract_prusti_attributes( item: &mut untyped::AnyFnItem, ) -> Vec<(SpecAttributeKind, TokenStream)> { let mut prusti_attributes = Vec::new(); let mut regular_attributes = Vec::new(); for attr in item.attrs_mut().drain(0..) { - if attr.path.segments.len() == 1 || (attr.path.segments.len() == 2 && attr.path.segments[0].ident == "prusti_contracts") { - let idx = attr.path.segments.len()-1; + if attr.path.segments.len() == 1 + || (attr.path.segments.len() == 2 && attr.path.segments[0].ident == "prusti_contracts") + { + let idx = attr.path.segments.len() - 1; if let Ok(attr_kind) = attr.path.segments[idx].ident.to_string().try_into() { let tokens = match attr_kind { SpecAttributeKind::Requires | SpecAttributeKind::Ensures | SpecAttributeKind::AfterExpiry | SpecAttributeKind::AssertOnExpiry - | SpecAttributeKind::GhostConstraint => { + | SpecAttributeKind::RefineSpec => { // We need to drop the surrounding parenthesis to make the // tokens identical to the ones passed by the native procedural // macro call. @@ -84,7 +92,9 @@ fn extract_prusti_attributes( } SpecAttributeKind::Invariant => unreachable!("type invariant on function"), SpecAttributeKind::Model => unreachable!("model on function"), - SpecAttributeKind::PrintCounterexample => unreachable!("print_counterexample on function"), + SpecAttributeKind::PrintCounterexample => { + unreachable!("print_counterexample on function") + } }; prusti_attributes.push((attr_kind, tokens)); } else { @@ -162,7 +172,7 @@ fn generate_spec_and_assertions( // `check_incompatible_attrs`; so we'll never reach here. SpecAttributeKind::Predicate => unreachable!(), SpecAttributeKind::Invariant => unreachable!(), - SpecAttributeKind::GhostConstraint => ghost_constraints::generate(attr_tokens, item), + SpecAttributeKind::RefineSpec => type_cond_specs::generate(attr_tokens, item), SpecAttributeKind::Model => unreachable!(), SpecAttributeKind::PrintCounterexample => unreachable!(), }; @@ -277,14 +287,6 @@ fn generate_for_pure(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedR )); } - do_generate_for_pure(item) -} - -/// Generate spec items and attributes to typecheck and later retrieve "pure" annotations. -/// -/// This the actual generating logic called by `generate_for_pure` after checking that the body is empty. -/// It's exposed separately for use in ghost constraints. -fn do_generate_for_pure(item: &untyped::AnyFnItem) -> GeneratedResult { Ok(( vec![], vec![parse_quote_spanned! {item.span()=> @@ -293,6 +295,21 @@ fn do_generate_for_pure(item: &untyped::AnyFnItem) -> GeneratedResult { )) } +/// Generate spec items and attributes to typecheck and later retrieve "pure" annotations, but encoded as a referenced separate function that type-conditional spec refinements can apply trait bounds to. +fn generate_for_pure_refinements(item: &untyped::AnyFnItem) -> GeneratedResult { + let mut rewriter = rewriter::AstRewriter::new(); + let spec_id = rewriter.generate_spec_id(); + let spec_id_str = spec_id.to_string(); + let spec_item = rewriter.process_pure_refinement(spec_id, item)?; + + Ok(( + vec![spec_item], + vec![parse_quote_spanned! {item.span()=> + #[prusti::pure_spec_id_ref = #spec_id_str] + }], + )) +} + /// Generate spec items and attributes to typecheck and later retrieve "trusted" annotations. fn generate_for_trusted(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult { if !attr.is_empty() { @@ -343,41 +360,29 @@ fn generate_for_trusted_for_types(attr: TokenStream, item: &syn::DeriveInput) -> .params .iter() .map(|generic_param| match generic_param { - syn::GenericParam::Type(param) => { - syn::GenericParam::Type( - syn::TypeParam { - attrs: Vec::new(), - bounds: syn::punctuated::Punctuated::new(), - colon_token: None, - default: None, - eq_token: None, - ident: param.ident.clone(), - } - ) - }, - syn::GenericParam::Lifetime(param) => { - syn::GenericParam::Lifetime( - syn::LifetimeDef { - attrs: Vec::new(), - bounds: syn::punctuated::Punctuated::new(), - colon_token: None, - lifetime: param.lifetime.clone(), - } - ) - }, - syn::GenericParam::Const(param) => { - syn::GenericParam::Const( - syn::ConstParam { - attrs: Vec::new(), - colon_token: param.colon_token, - const_token: param.const_token, - default: None, - eq_token: None, - ident: param.ident.clone(), - ty: param.ty.clone(), - } - ) - } + syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { + attrs: Vec::new(), + bounds: syn::punctuated::Punctuated::new(), + colon_token: None, + default: None, + eq_token: None, + ident: param.ident.clone(), + }), + syn::GenericParam::Lifetime(param) => syn::GenericParam::Lifetime(syn::LifetimeDef { + attrs: Vec::new(), + bounds: syn::punctuated::Punctuated::new(), + colon_token: None, + lifetime: param.lifetime.clone(), + }), + syn::GenericParam::Const(param) => syn::GenericParam::Const(syn::ConstParam { + attrs: Vec::new(), + colon_token: param.colon_token, + const_token: param.const_token, + default: None, + eq_token: None, + ident: param.ident.clone(), + ty: param.ty.clone(), + }), }) .collect::>(); // TODO: similarly to extern_specs, don't generate an actual impl @@ -387,10 +392,7 @@ fn generate_for_trusted_for_types(attr: TokenStream, item: &syn::DeriveInput) -> } }; - Ok(( - vec![syn::Item::Impl(item_impl)], - vec![], - )) + Ok((vec![syn::Item::Impl(item_impl)], vec![])) } pub fn body_variant(tokens: TokenStream) -> TokenStream { @@ -539,13 +541,13 @@ pub fn refine_trait_spec(_attr: TokenStream, tokens: TokenStream) -> TokenStream let illegal_attribute_span = prusti_attributes .iter() - .filter(|(kind, _)| kind == &SpecAttributeKind::GhostConstraint) + .filter(|(kind, _)| kind == &SpecAttributeKind::RefineSpec) .map(|(_, tokens)| tokens.span()) .next(); if let Some(span) = illegal_attribute_span { let err = Err(syn::Error::new( span, - "Ghost constraints in trait spec refinements not supported", + "Type-conditional spec refinements in trait spec refinements not supported", )); handle_result!(err); } @@ -638,41 +640,31 @@ pub fn trusted(attr: TokenStream, tokens: TokenStream) -> TokenStream { .params .iter() .map(|generic_param| match generic_param { - syn::GenericParam::Type(param) => { - syn::GenericParam::Type( - syn::TypeParam { - attrs: Vec::new(), - bounds: syn::punctuated::Punctuated::new(), - colon_token: None, - default: None, - eq_token: None, - ident: param.ident.clone(), - } - ) - }, + syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { + attrs: Vec::new(), + bounds: syn::punctuated::Punctuated::new(), + colon_token: None, + default: None, + eq_token: None, + ident: param.ident.clone(), + }), syn::GenericParam::Lifetime(param) => { - syn::GenericParam::Lifetime( - syn::LifetimeDef { - attrs: Vec::new(), - bounds: syn::punctuated::Punctuated::new(), - colon_token: None, - lifetime: param.lifetime.clone(), - } - ) - }, - syn::GenericParam::Const(param) => { - syn::GenericParam::Const( - syn::ConstParam { - attrs: Vec::new(), - colon_token: param.colon_token, - const_token: param.const_token, - default: None, - eq_token: None, - ident: param.ident.clone(), - ty: param.ty.clone(), - } - ) + syn::GenericParam::Lifetime(syn::LifetimeDef { + attrs: Vec::new(), + bounds: syn::punctuated::Punctuated::new(), + colon_token: None, + lifetime: param.lifetime.clone(), + }) } + syn::GenericParam::Const(param) => syn::GenericParam::Const(syn::ConstParam { + attrs: Vec::new(), + colon_token: param.colon_token, + const_token: param.const_token, + default: None, + eq_token: None, + ident: param.ident.clone(), + ty: param.ty.clone(), + }), }) .collect::>(); // TODO: similarly to extern_specs, don't generate an actual impl @@ -741,24 +733,42 @@ pub fn invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream { } pub fn extern_spec(attr: TokenStream, tokens: TokenStream) -> TokenStream { - let item: syn::Item = handle_result!(syn::parse2(tokens)); - match item { - syn::Item::Impl(item_impl) => { - handle_result!(extern_spec_rewriter::impls::rewrite_extern_spec(&item_impl)) - } - syn::Item::Trait(item_trait) => { - handle_result!(extern_spec_rewriter::traits::rewrite_extern_spec( - &item_trait - )) - } - syn::Item::Mod(mut item_mod) => { - handle_result!(extern_spec_rewriter::mods::rewrite_extern_spec( - &mut item_mod - )) + result_to_tokens!({ + let item: syn::Item = syn::parse2(tokens)?; + let mod_path: syn::Path = Some(attr) + .filter(|attr| !attr.is_empty()) + .map(syn::parse2) + .transpose()? + .unwrap_or_else(|| syn::Path { + leading_colon: None, + segments: syn::punctuated::Punctuated::new(), + }); + match item { + syn::Item::Impl(item_impl) => { + if !mod_path.segments.is_empty() { + return Err(syn::Error::new( + mod_path.span(), + "extern_spec does not take a path argument for impls--you can qualify the involved types directly", + )); + } + extern_spec_rewriter::impls::rewrite_extern_spec(&item_impl) + } + syn::Item::Trait(item_trait) => { + extern_spec_rewriter::traits::rewrite_extern_spec(&item_trait, mod_path) + } + syn::Item::Mod(item_mod) => { + extern_spec_rewriter::mods::rewrite_mod(&item_mod, mod_path) + } + // we're expecting function stubs, so they aren't represented as Item::Fn + syn::Item::Verbatim(stub_tokens) => { + extern_spec_rewriter::functions::rewrite_stub(&stub_tokens, &mod_path) + } + _ => Err(syn::Error::new( + Span::call_site(), // this covers the entire macro invocation, unlike attr.span() which changes to only cover arguments if possible + "Extern specs cannot be attached to this item", + )), } - _ => syn::Error::new(attr.span(), "Extern specs cannot be attached to this item") - .to_compile_error(), - } + }) } pub fn predicate(tokens: TokenStream) -> TokenStream { @@ -779,9 +789,10 @@ pub fn rewrite_prusti_attributes_for_types( // Collect the remaining Prusti attributes, removing them from `item`. prusti_attributes.extend(extract_prusti_attributes_for_types(&mut item)); - if prusti_attributes.len() > 1 && prusti_attributes - .iter() - .any(|(ak, _)| ak == &SpecAttributeKind::Trusted) + if prusti_attributes.len() > 1 + && prusti_attributes + .iter() + .any(|(ak, _)| ak == &SpecAttributeKind::Trusted) { return syn::Error::new( item.span(), @@ -793,8 +804,9 @@ pub fn rewrite_prusti_attributes_for_types( // we order the attributes to ensure a model attribute is processed first prusti_attributes.sort_by(|(ak1, _), (ak2, _)| ak1.cmp(ak2)); - let (generated_spec_items, generated_attributes) = - handle_result!(generate_spec_and_assertions_for_types(prusti_attributes, &mut item)); + let (generated_spec_items, generated_attributes) = handle_result!( + generate_spec_and_assertions_for_types(prusti_attributes, &mut item) + ); quote_spanned! {item.span()=> #(#generated_attributes)* @@ -803,7 +815,6 @@ pub fn rewrite_prusti_attributes_for_types( } } - fn extract_prusti_attributes_for_types( item: &mut syn::DeriveInput, ) -> Vec<(SpecAttributeKind, TokenStream)> { @@ -817,13 +828,12 @@ fn extract_prusti_attributes_for_types( SpecAttributeKind::Ensures => unreachable!("ensures on type"), SpecAttributeKind::AfterExpiry => unreachable!("after_expiry on type"), SpecAttributeKind::AssertOnExpiry => unreachable!("assert_on_expiry on type"), - SpecAttributeKind::GhostConstraint => unreachable!("ghost_constraint on type"), + SpecAttributeKind::RefineSpec => unreachable!("refine_spec on type"), SpecAttributeKind::Pure => unreachable!("pure on type"), SpecAttributeKind::Invariant => unreachable!("invariant on type"), SpecAttributeKind::Predicate => unreachable!("predicate on type"), SpecAttributeKind::Terminates => unreachable!("terminates on type"), - SpecAttributeKind::Trusted | - SpecAttributeKind::Model => { + SpecAttributeKind::Trusted | SpecAttributeKind::Model => { assert!(attr.tokens.is_empty(), "Unexpected shape of an attribute."); attr.tokens } @@ -865,11 +875,13 @@ fn generate_spec_and_assertions_for_types( SpecAttributeKind::Pure => unreachable!(), SpecAttributeKind::Predicate => unreachable!(), SpecAttributeKind::Invariant => unreachable!(), - SpecAttributeKind::GhostConstraint => unreachable!(), + SpecAttributeKind::RefineSpec => unreachable!(), SpecAttributeKind::Terminates => unreachable!(), SpecAttributeKind::Trusted => generate_for_trusted_for_types(attr_tokens, item), SpecAttributeKind::Model => generate_for_model(attr_tokens, item), - SpecAttributeKind::PrintCounterexample => generate_for_print_counterexample(attr_tokens, item), + SpecAttributeKind::PrintCounterexample => { + generate_for_print_counterexample(attr_tokens, item) + } }; let (new_items, new_attributes) = rewriting_result?; generated_items.extend(new_items); @@ -883,7 +895,7 @@ fn generate_spec_and_assertions_for_types( fn generate_for_model(attr: TokenStream, item: &mut syn::DeriveInput) -> GeneratedResult { match syn::Item::from(item.clone()) { syn::Item::Struct(item_struct) => { - match type_model::rewrite(item_struct){ + match type_model::rewrite(item_struct) { Ok(result) => { match result.first() { Some(syn::Item::Struct(new_item)) => { @@ -892,7 +904,7 @@ fn generate_for_model(attr: TokenStream, item: &mut syn::DeriveInput) -> Generat } _ => unreachable!(), } - }, + } Err(err) => Err(err), } } @@ -903,30 +915,31 @@ fn generate_for_model(attr: TokenStream, item: &mut syn::DeriveInput) -> Generat } } - /// Generate spec items and attributes to typecheck and later retrieve "print_counterexample" annotations. -fn generate_for_print_counterexample(attr: TokenStream, item: &mut syn::DeriveInput) -> GeneratedResult { +fn generate_for_print_counterexample( + attr: TokenStream, + item: &mut syn::DeriveInput, +) -> GeneratedResult { match syn::Item::from(item.clone()) { syn::Item::Struct(item_struct) => { - match print_counterexample::rewrite_struct(attr, item_struct){ + match print_counterexample::rewrite_struct(attr, item_struct) { Ok(result) => Ok((result, vec![])), Err(err) => Err(err), } } syn::Item::Enum(item_enum) => { - match print_counterexample::rewrite_enum(attr, item_enum){ + match print_counterexample::rewrite_enum(attr, item_enum) { Ok(result) => { match result.first() { Some(syn::Item::Enum(new_item)) => { *item = syn::DeriveInput::from(new_item.clone()); //print_counterexample removes all attributes inside the enum Ok((vec![result[1].clone()], vec![])) - }, + } _ => unreachable!(), } - }, + } Err(err) => Err(err), } - } _ => Err(syn::Error::new( attr.span(), diff --git a/prusti-contracts/prusti-specs/src/parse_closure_macro.rs b/prusti-contracts/prusti-specs/src/parse_closure_macro.rs index 365a4fdfdcc..34d0a12d1b0 100644 --- a/prusti-contracts/prusti-specs/src/parse_closure_macro.rs +++ b/prusti-contracts/prusti-specs/src/parse_closure_macro.rs @@ -3,7 +3,7 @@ use syn::parse::{Parse, ParseStream}; pub(crate) struct ClosureWithSpec { pub pres: Vec, pub posts: Vec, - pub cl: syn::ExprClosure + pub cl: syn::ExprClosure, } impl Parse for ClosureWithSpec { @@ -21,7 +21,7 @@ impl Parse for ClosureWithSpec { match id.to_string().as_ref() { "requires" => pres.push(syn::parse2(attr.tokens.clone())), "ensures" => posts.push(syn::parse2(attr.tokens.clone())), - _ => return false + _ => return false, } true } else { diff --git a/prusti-contracts/prusti-specs/src/predicate.rs b/prusti-contracts/prusti-specs/src/predicate.rs index 3fe390e2d5a..8b2f152b05c 100644 --- a/prusti-contracts/prusti-specs/src/predicate.rs +++ b/prusti-contracts/prusti-specs/src/predicate.rs @@ -1,10 +1,12 @@ //! Predicate parsing -use crate::{rewriter, SpecificationId, SPECS_VERSION}; +use crate::{ + common::{HasMacro, HasSignature}, + rewriter, SpecificationId, SPECS_VERSION, +}; use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::{parse::Parse, parse_quote_spanned, spanned::Spanned}; -use crate::common::{HasMacro, HasSignature}; #[derive(Debug)] pub struct PredicateWithBody { diff --git a/prusti-contracts/prusti-specs/src/rewriter.rs b/prusti-contracts/prusti-specs/src/rewriter.rs index 7cd66bd7bc1..41b31b381b7 100644 --- a/prusti-contracts/prusti-specs/src/rewriter.rs +++ b/prusti-contracts/prusti-specs/src/rewriter.rs @@ -113,9 +113,15 @@ impl AstRewriter { // terminator in MIR has a span set to the one character just after // the identifier let (return_type, return_modifier) = if spec_type == SpecItemType::Termination { - (quote_spanned! {item_span => Int}, quote_spanned! {item_span => Int::new(0) + }) + ( + quote_spanned! {item_span => Int}, + quote_spanned! {item_span => Int::new(0) + }, + ) } else { - (quote_spanned! {item_span => bool}, quote_spanned! {item_span => !!}) + ( + quote_spanned! {item_span => bool}, + quote_spanned! {item_span => !!}, + ) }; let mut spec_item: syn::ItemFn = parse_quote_spanned! {item_span=> #[allow(unused_must_use, unused_parens, unused_variables, dead_code, non_snake_case)] @@ -164,6 +170,30 @@ impl AstRewriter { ) } + pub fn process_pure_refinement( + &mut self, + spec_id: SpecificationId, + item: &untyped::AnyFnItem, + ) -> syn::Result { + let item_span = item.span(); + let item_name = syn::Ident::new( + &format!("prusti_pure_ghost_item_{}", item.sig().ident), + item_span, + ); + + let spec_id_str = spec_id.to_string(); + let mut spec_item: syn::ItemFn = parse_quote_spanned! {item_span=> + #[allow(unused_must_use, unused_parens, unused_variables, dead_code)] + #[prusti::spec_only] + #[prusti::spec_id = #spec_id_str] + fn #item_name() {} // we only need this for attaching constraints to (to evaluate when the function is pure) + }; + + spec_item.sig.generics = item.sig().generics.clone(); + spec_item.sig.inputs = item.sig().inputs.clone(); + Ok(syn::Item::Fn(spec_item)) + } + /// Parse a pledge with lhs into a Rust expression pub fn process_assert_pledge( &mut self, diff --git a/prusti-contracts/prusti-specs/src/span_overrider.rs b/prusti-contracts/prusti-specs/src/span_overrider.rs index a517a64ad45..12039ca523d 100644 --- a/prusti-contracts/prusti-specs/src/span_overrider.rs +++ b/prusti-contracts/prusti-specs/src/span_overrider.rs @@ -2,18 +2,16 @@ use proc_macro2::Span; /// Override all span information pub struct SpanOverrider { - span: Span + span: Span, } impl SpanOverrider { pub fn new(span: Span) -> Self { - SpanOverrider { - span - } + SpanOverrider { span } } } -impl syn::visit_mut::VisitMut for SpanOverrider { +impl syn::visit_mut::VisitMut for SpanOverrider { fn visit_span_mut(&mut self, span: &mut Span) { *span = self.span; } diff --git a/prusti-contracts/prusti-specs/src/spec_attribute_kind.rs b/prusti-contracts/prusti-specs/src/spec_attribute_kind.rs index c77bb53ee42..8a64d3c0495 100644 --- a/prusti-contracts/prusti-specs/src/spec_attribute_kind.rs +++ b/prusti-contracts/prusti-specs/src/spec_attribute_kind.rs @@ -3,8 +3,8 @@ use std::convert::TryFrom; /// This type identifies one of the procedural macro attributes of Prusti #[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord)] pub enum SpecAttributeKind { - /// All type specifications that alter its type must be processed before - /// PrintCounterexample. Currently this only applies to Model. + /// All type specifications that alter its type must be processed before + /// PrintCounterexample. Currently this only applies to Model. Model = 0, Requires = 1, Ensures = 2, @@ -14,7 +14,7 @@ pub enum SpecAttributeKind { Trusted = 6, Predicate = 7, Invariant = 8, - GhostConstraint = 9, + RefineSpec = 9, Terminates = 10, PrintCounterexample = 11, } @@ -32,7 +32,7 @@ impl TryFrom for SpecAttributeKind { "trusted" => Ok(SpecAttributeKind::Trusted), "predicate" => Ok(SpecAttributeKind::Predicate), "invariant" => Ok(SpecAttributeKind::Invariant), - "ghost_constraint" => Ok(SpecAttributeKind::GhostConstraint), + "refine_spec" => Ok(SpecAttributeKind::RefineSpec), "model" => Ok(SpecAttributeKind::Model), "print_counterexample" => Ok(SpecAttributeKind::PrintCounterexample), _ => Err(name), diff --git a/prusti-contracts/prusti-specs/src/specifications/common.rs b/prusti-contracts/prusti-specs/src/specifications/common.rs index 31b94930f79..bf949348049 100644 --- a/prusti-contracts/prusti-specs/src/specifications/common.rs +++ b/prusti-contracts/prusti-specs/src/specifications/common.rs @@ -3,8 +3,10 @@ //! Please see the `parser.rs` file for more information about //! specifications. -use std::convert::TryFrom; -use std::fmt::{Display, Debug}; +use std::{ + convert::TryFrom, + fmt::{Debug, Display}, +}; use uuid::Uuid; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -45,7 +47,17 @@ impl<'a> TryFrom<&'a str> for SpecType { } #[derive( - Debug, Default, PartialEq, Eq, Hash, Clone, Copy, serde::Serialize, serde::Deserialize, PartialOrd, Ord, + Debug, + Default, + PartialEq, + Eq, + Hash, + Clone, + Copy, + serde::Serialize, + serde::Deserialize, + PartialOrd, + Ord, )] /// A unique ID of the specification element such as entire precondition /// or postcondition. @@ -56,6 +68,7 @@ pub struct SpecificationId(Uuid); pub enum SpecIdRef { Precondition(SpecificationId), Postcondition(SpecificationId), + Purity(SpecificationId), Pledge { lhs: Option, rhs: SpecificationId, @@ -109,23 +122,19 @@ pub(crate) fn generate_struct_name_for_trait(item: &syn::ItemTrait) -> String { format!("PrustiTrait{}_{}", item.ident, uuid) } -pub(crate) fn generate_mod_name(ident: &syn::Ident) -> String { - let uuid = Uuid::new_v4().simple(); - format!("{}_{}", ident, uuid) -} - fn generate_name_for_type(ty: &syn::Type) -> Option { match ty { - syn::Type::Path(ty_path) => { - Some(String::from_iter(ty_path.path.segments.iter() - .map(|seg| seg.ident.to_string()))) - }, + syn::Type::Path(ty_path) => Some(String::from_iter( + ty_path + .path + .segments + .iter() + .map(|seg| seg.ident.to_string()), + )), syn::Type::Slice(ty_slice) => { let ty = &*ty_slice.elem; Some(format!("Slice{}", generate_name_for_type(ty)?.as_str())) - }, - _ => { - None } + _ => None, } } diff --git a/prusti-contracts/prusti-specs/src/specifications/mod.rs b/prusti-contracts/prusti-specs/src/specifications/mod.rs index a669cb22200..62b27210064 100644 --- a/prusti-contracts/prusti-specs/src/specifications/mod.rs +++ b/prusti-contracts/prusti-specs/src/specifications/mod.rs @@ -1,19 +1,3 @@ -/// The following grammar defines Prusti expressions (this is an LL(finite) grammar): -/// assertion ::= prusti_expr ; -/// pledge ::= pledge_lhs, ",", prusti_expr ; -/// pledge_lhs ::= [ ? actual rust expression ?, "=>" ], prusti_expr ; -/// -/// prusti_expr ::= conjunction, [ "==>", prusti_expr ] ; -/// conjunction ::= entailment, { "&&", entailment } ; -/// entailment ::= primary | ? actual rust expression ?, [ "|=", [ "|", ? args as parsed by syn2 ?, "|" ], "[", [ ( requires | ensures ), { ",", ( requires | ensures ) } ], "]" ] ; -/// primary ::= "(", prusti_expr, ")" -/// | "forall", "(", "|", ? one or more args as parsed by syn2 ?, "|", prusti_expr, [ ",", "triggers", "=", ? array as parsed by syn2 ? ] ")" -/// ; -/// requires ::= "requires", "(", prusti_expr, ")" ; -/// ensures ::= "ensures", "(", prusti_expr, ")" ; -/// -/// This grammar doesn't yet handle the unintuitive precedence difference between `&&` and `||` operators. - pub mod common; pub mod preparser; pub mod untyped; diff --git a/prusti-contracts/prusti-specs/src/specifications/preparser.rs b/prusti-contracts/prusti-specs/src/specifications/preparser.rs index df45fbad30a..9b0b875042f 100644 --- a/prusti-contracts/prusti-specs/src/specifications/preparser.rs +++ b/prusti-contracts/prusti-specs/src/specifications/preparser.rs @@ -1,17 +1,18 @@ /// The preparser processes Prusti syntax into Rust syntax. - -use proc_macro2::{Span, TokenStream, TokenTree, Delimiter}; +use proc_macro2::{Delimiter, Span, TokenStream, TokenTree}; +use proc_macro2::{Punct, Spacing::*}; +use quote::{quote, quote_spanned, ToTokens}; use std::collections::VecDeque; -use quote::{ToTokens, quote, quote_spanned}; -use proc_macro2::Punct; -use proc_macro2::Spacing::*; -use syn::{parse::{Parse, ParseStream}, spanned::Spanned}; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, +}; /// The representation of an argument to a quantifier (for example `a: i32`) #[derive(Debug, Clone)] pub struct Arg { pub name: syn::Ident, - pub typ: syn::Type + pub typ: syn::Type, } pub fn parse_prusti(tokens: TokenStream) -> syn::Result { @@ -28,7 +29,10 @@ pub fn parse_prusti_pledge(tokens: TokenStream) -> syn::Result { let (reference, rhs) = PrustiTokenStream::new(tokens).parse_pledge()?; if let Some(reference) = reference { if reference.to_string() != "result" { - return err(reference.span(), "reference of after_expiry must be \"result\""); + return err( + reference.span(), + "reference of after_expiry must be \"result\"", + ); } } syn::parse2::(rhs.clone())?; @@ -42,7 +46,10 @@ pub fn parse_prusti_assert_pledge(tokens: TokenStream) -> syn::Result<(TokenStre let (reference, lhs, rhs) = PrustiTokenStream::new(tokens).parse_assert_pledge()?; if let Some(reference) = reference { if reference.to_string() != "result" { - return err(reference.span(), "reference of assert_on_expiry must be \"result\""); + return err( + reference.span(), + "reference of assert_on_expiry must be \"result\"", + ); } } syn::parse2::(lhs.clone())?; @@ -50,7 +57,7 @@ pub fn parse_prusti_assert_pledge(tokens: TokenStream) -> syn::Result<(TokenStre Ok((lhs, rhs)) } -pub fn parse_ghost_constraint(tokens: TokenStream) -> syn::Result { +pub fn parse_type_cond_spec(tokens: TokenStream) -> syn::Result { syn::parse2(tokens) } @@ -159,7 +166,10 @@ impl PrustiTokenStream { (token, _, _) => PrustiToken::Token(token.clone()), }); } - Self { tokens, source_span } + Self { + tokens, + source_span, + } } fn is_empty(&self) -> bool { @@ -189,24 +199,23 @@ impl PrustiTokenStream { /// Processes a Prusti token stream back into Rust syntax. /// Prusti-specific syntax is not allowed and will raise an error. fn parse_rust_only(self) -> syn::Result { - Ok(TokenStream::from_iter(self.tokens - .into_iter() - .map(|token| match token { - PrustiToken::Group(_, _, box stream) => stream.parse_rust_only(), - PrustiToken::Token(tree) => Ok(tree.to_token_stream()), - PrustiToken::BinOp(span, PrustiBinaryOp::Rust(op)) => Ok(op.to_tokens(span)), - _ => err(token.span(), "unexpected Prusti syntax"), - }) - .collect::, _>>()? - .into_iter())) + Ok(TokenStream::from_iter( + self.tokens + .into_iter() + .map(|token| match token { + PrustiToken::Group(_, _, box stream) => stream.parse_rust_only(), + PrustiToken::Token(tree) => Ok(tree.to_token_stream()), + PrustiToken::BinOp(span, PrustiBinaryOp::Rust(op)) => Ok(op.to_tokens(span)), + _ => err(token.span(), "unexpected Prusti syntax"), + }) + .collect::, _>>()? + .into_iter(), + )) } /// Processes a Prusti token stream for a pledge, in the form `a => b` or /// just `b`. - fn parse_pledge(self) -> syn::Result<( - Option, - TokenStream - )> { + fn parse_pledge(self) -> syn::Result<(Option, TokenStream)> { let mut pledge_ops = self.split(PrustiBinaryOp::Rust(RustOp::Arrow), false); if pledge_ops.len() == 1 { Ok((None, pledge_ops[0].expr_bp(0)?)) @@ -219,11 +228,7 @@ impl PrustiTokenStream { /// Processes a Prusti token stream for an assert pledge, in the form `a => /// b, c` or `b, c`. - fn parse_assert_pledge(self) -> syn::Result<( - Option, - TokenStream, - TokenStream - )> { + fn parse_assert_pledge(self) -> syn::Result<(Option, TokenStream, TokenStream)> { let mut pledge_ops = self.split(PrustiBinaryOp::Rust(RustOp::Arrow), false); let (reference, body) = match (pledge_ops.pop(), pledge_ops.pop(), pledge_ops.pop()) { (Some(body), None, _) => (None, body), @@ -232,7 +237,11 @@ impl PrustiTokenStream { }; let mut body_parts = body.split(PrustiBinaryOp::Rust(RustOp::Comma), false); if body_parts.len() == 2 { - Ok((reference, body_parts[0].expr_bp(0)?, body_parts[1].expr_bp(0)?)) + Ok(( + reference, + body_parts[0].expr_bp(0)?, + body_parts[1].expr_bp(0)?, + )) } else { err(Span::call_site(), "missing assertion") } @@ -245,22 +254,22 @@ impl PrustiTokenStream { fn expr_bp(&mut self, min_bp: u8) -> syn::Result { let mut lhs = match self.tokens.pop_front() { Some(PrustiToken::Group(span, delimiter, box stream)) => { - let mut group = proc_macro2::Group::new( - delimiter, - stream.parse()?, - ); + let mut group = proc_macro2::Group::new(delimiter, stream.parse()?); group.set_span(span); TokenTree::Group(group).to_token_stream() } Some(PrustiToken::Outer(span)) => { - let _stream = self.pop_group(Delimiter::Parenthesis) + let _stream = self + .pop_group(Delimiter::Parenthesis) .ok_or_else(|| error(span, "expected parenthesized expression after outer"))?; todo!() } Some(PrustiToken::Quantifier(span, kind)) => { - let mut stream = self.pop_group(Delimiter::Parenthesis) - .ok_or_else(|| error(span, "expected parenthesized expression after quantifier"))?; - let args = stream.pop_closure_args() + let mut stream = self.pop_group(Delimiter::Parenthesis).ok_or_else(|| { + error(span, "expected parenthesized expression after quantifier") + })?; + let args = stream + .pop_closure_args() .ok_or_else(|| error(span, "expected quantifier body"))?; { @@ -273,7 +282,12 @@ impl PrustiTokenStream { for pat in parsed_cl.inputs { match pat { syn::Pat::Type(_) => {} - _ => return err(pat.span(), "quantifier arguments must have explicit types"), + _ => { + return err( + pat.span(), + "quantifier arguments must have explicit types", + ) + } } } }; @@ -287,16 +301,14 @@ impl PrustiTokenStream { kind.translate(span, triggers, args, body) } - Some(PrustiToken::SpecEnt(span, _)) - | Some(PrustiToken::CallDesc(span, _)) => - return err(span, "unexpected operator"), + Some(PrustiToken::SpecEnt(span, _)) | Some(PrustiToken::CallDesc(span, _)) => { + return err(span, "unexpected operator") + } // some Rust binary operators can appear on their own, e.g. `(..)` - Some(PrustiToken::BinOp(span, PrustiBinaryOp::Rust(op))) => - op.to_tokens(span), + Some(PrustiToken::BinOp(span, PrustiBinaryOp::Rust(op))) => op.to_tokens(span), - Some(PrustiToken::BinOp(span, _)) => - return err(span, "unexpected binary operator"), + Some(PrustiToken::BinOp(span, _)) => return err(span, "unexpected binary operator"), Some(PrustiToken::Token(token)) => token.to_token_stream(), None => return Ok(TokenStream::new()), }; @@ -307,10 +319,7 @@ impl PrustiTokenStream { // precedence operators (e.g. plus) are connected into atoms // as far as our parser is concerned. Some(PrustiToken::Group(span, delimiter, box stream)) => { - let mut group = proc_macro2::Group::new( - *delimiter, - stream.clone().parse()?, - ); + let mut group = proc_macro2::Group::new(*delimiter, stream.clone().parse()?); group.set_span(*span); lhs.extend(TokenTree::Group(group).to_token_stream()); self.tokens.pop_front(); @@ -326,15 +335,15 @@ impl PrustiTokenStream { let span = *span; let once = *once; self.tokens.pop_front(); - let args = self.pop_closure_args() + let args = self + .pop_closure_args() .ok_or_else(|| error(span, "expected closure arguments"))?; let nested_closure_specs = self.pop_group_of_nested_specs(span)?; lhs = translate_spec_ent( span, once, lhs, - args - .split(PrustiBinaryOp::Rust(RustOp::Comma), true) + args.split(PrustiBinaryOp::Rust(RustOp::Comma), true) .into_iter() .map(|stream| stream.parse()) .collect::, _>>()?, @@ -346,10 +355,10 @@ impl PrustiTokenStream { Some(PrustiToken::CallDesc(..)) => todo!("call desc"), Some(PrustiToken::BinOp(span, op)) => (*span, *op), - Some(PrustiToken::Outer(span)) => - return err(*span, "unexpected outer"), - Some(PrustiToken::Quantifier(span, _)) => - return err(*span, "unexpected quantifier"), + Some(PrustiToken::Outer(span)) => return err(*span, "unexpected outer"), + Some(PrustiToken::Quantifier(span, _)) => { + return err(*span, "unexpected quantifier") + } None => break, }; @@ -370,8 +379,7 @@ impl PrustiTokenStream { fn pop_group(&mut self, delimiter: Delimiter) -> Option { match self.tokens.pop_front() { - Some(PrustiToken::Group(_, del, box stream)) if del == delimiter - => Some(stream), + Some(PrustiToken::Group(_, del, box stream)) if del == delimiter => Some(stream), _ => None, } } @@ -380,8 +388,14 @@ impl PrustiTokenStream { let mut tokens = VecDeque::new(); // special case: empty closure might be parsed as a logical or - if matches!(self.tokens.front(), Some(PrustiToken::BinOp(_, PrustiBinaryOp::Or))) { - return Some(Self { tokens, source_span: self.source_span }); + if matches!( + self.tokens.front(), + Some(PrustiToken::BinOp(_, PrustiBinaryOp::Or)) + ) { + return Some(Self { + tokens, + source_span: self.source_span, + }); } if !self.tokens.pop_front()?.is_closure_brace() { @@ -395,9 +409,12 @@ impl PrustiTokenStream { tokens.push_back(token); } - Some(Self { tokens, source_span: self.source_span }) + Some(Self { + tokens, + source_span: self.source_span, + }) } - + fn pop_parenthesized_group(&mut self) -> syn::Result { match self.tokens.pop_front() { Some(PrustiToken::Group(_span, Delimiter::Parenthesis, box group)) => { @@ -406,25 +423,33 @@ impl PrustiTokenStream { _ => Err(error(self.source_span, "expected parenthesized group")), } } - + fn pop_single_nested_spec(&mut self) -> syn::Result> { - let first = self.tokens.pop_front().ok_or_else(|| { - error(self.source_span, "expected nested spec") - })?; + let first = self + .tokens + .pop_front() + .ok_or_else(|| error(self.source_span, "expected nested spec"))?; if let PrustiToken::Token(TokenTree::Ident(spec_type)) = first { match spec_type.to_string().as_ref() { "requires" => Ok(NestedSpec::Requires(self.pop_parenthesized_group()?)), "ensures" => Ok(NestedSpec::Ensures(self.pop_parenthesized_group()?)), "pure" => Ok(NestedSpec::Pure), - other => err(self.source_span, format!("unexpected nested spec type: {}", other).as_ref()), + other => err( + self.source_span, + format!("unexpected nested spec type: {}", other).as_ref(), + ), } } else { err(self.source_span, "expected identifier") } } - fn pop_group_of_nested_specs(&mut self, span: Span) -> syn::Result>> { - let group_of_specs = self.pop_group(Delimiter::Bracket) + fn pop_group_of_nested_specs( + &mut self, + span: Span, + ) -> syn::Result>> { + let group_of_specs = self + .pop_group(Delimiter::Bracket) .ok_or_else(|| error(span, "expected nested specification in brackets"))?; let parsed = group_of_specs .split(PrustiBinaryOp::Rust(RustOp::Comma), true) @@ -435,19 +460,19 @@ impl PrustiTokenStream { Ok(parsed) } - fn split( - self, - split_on: PrustiBinaryOp, - allow_trailing: bool, - ) -> Vec { + fn split(self, split_on: PrustiBinaryOp, allow_trailing: bool) -> Vec { if self.tokens.is_empty() { return vec![]; } - let mut res = self.tokens + let mut res = self + .tokens .into_iter() .collect::>() .split(|token| matches!(token, PrustiToken::BinOp(_, t) if *t == split_on)) - .map(|group| Self { tokens: group.iter().cloned().collect(), source_span: self.source_span }) + .map(|group| Self { + tokens: group.iter().cloned().collect(), + source_span: self.source_span, + }) .collect::>(); if allow_trailing && res.len() > 1 && res[res.len() - 1].tokens.is_empty() { res.pop(); @@ -466,22 +491,24 @@ impl PrustiTokenStream { &self.tokens[len - 2], &self.tokens[len - 1], ] { - [ - PrustiToken::BinOp(_, PrustiBinaryOp::Rust(RustOp::Comma)), - PrustiToken::Token(TokenTree::Ident(ident)), - PrustiToken::BinOp(_, PrustiBinaryOp::Rust(RustOp::Assign)), - PrustiToken::Group(triggers_span, Delimiter::Bracket, box triggers), - ] if ident == "triggers" => { - let triggers = triggers.clone() + [PrustiToken::BinOp(_, PrustiBinaryOp::Rust(RustOp::Comma)), PrustiToken::Token(TokenTree::Ident(ident)), PrustiToken::BinOp(_, PrustiBinaryOp::Rust(RustOp::Assign)), PrustiToken::Group(triggers_span, Delimiter::Bracket, box triggers)] + if ident == "triggers" => + { + let triggers = triggers + .clone() .split(PrustiBinaryOp::Rust(RustOp::Comma), true) .into_iter() - .map(|mut stream| stream - .pop_group(Delimiter::Parenthesis) - .ok_or_else(|| error(*triggers_span, "trigger sets must be tuples of expressions"))? - .split(PrustiBinaryOp::Rust(RustOp::Comma), true) - .into_iter() - .map(|stream| stream.parse()) - .collect::, _>>()) + .map(|mut stream| { + stream + .pop_group(Delimiter::Parenthesis) + .ok_or_else(|| { + error(*triggers_span, "trigger sets must be tuples of expressions") + })? + .split(PrustiBinaryOp::Rust(RustOp::Comma), true) + .into_iter() + .map(|stream| stream.parse()) + .collect::, _>>() + }) .collect::, _>>(); self.tokens.truncate(len - 4); triggers @@ -492,38 +519,64 @@ impl PrustiTokenStream { } #[derive(Debug)] -pub struct GhostConstraint { - pub trait_bounds: syn::PredicateType, - pub comma: syn::token::Comma, +pub struct TypeCondSpecRefinement { + pub trait_bounds: Vec, pub specs: Vec>, } -impl Parse for GhostConstraint { +impl Parse for TypeCondSpecRefinement { fn parse(input: ParseStream) -> syn::Result { - Ok(GhostConstraint { + input + .parse::() + .map_err(with_type_cond_spec_example)?; + Ok(TypeCondSpecRefinement { trait_bounds: parse_trait_bounds(input)?, - comma: input.parse().map_err(with_ghost_constraint_example)?, - specs: PrustiTokenStream::new(input.parse().expect("Failed to parse GhostConstraint")) + specs: PrustiTokenStream::new(input.parse().unwrap()) .parse_rest(|pts| pts.pop_group_of_nested_specs(input.span()))?, }) } } -fn parse_trait_bounds(input: ParseStream) -> syn::Result { +fn parse_trait_bounds(input: ParseStream) -> syn::Result> { + let mut bounds: Vec = Vec::new(); + loop { + let predicate = input + .parse::() + .map_err(with_type_cond_spec_example)?; + bounds.push(validate_predicate(predicate)?); + input + .parse::() + .map_err(with_type_cond_spec_example)?; + if input.peek(syn::token::Bracket) || input.is_empty() { + // now expecting specs in [] + // also breaking when empty, to handle that as missing specs rather than a missing constraint + break; + } + } + Ok(bounds) +} + +fn validate_predicate(predicate: syn::WherePredicate) -> syn::Result { use syn::WherePredicate::*; - let where_predicate: syn::WherePredicate = input.parse().map_err(with_ghost_constraint_example)?; - match where_predicate { + + match predicate { Type(type_bound) => { validate_trait_bounds(&type_bound)?; Ok(type_bound) } Lifetime(lifetime_bound) => disallowed_lifetime_error(lifetime_bound.span()), - Eq(eq_bound) => err(eq_bound.span(), "equality predicates are not supported in trait bounds"), + Eq(eq_bound) => err( + eq_bound.span(), + "equality constraints are not allowed in type-conditional spec refinements", + ), } } fn disallowed_lifetime_error(span: Span) -> syn::Result { - err(span, "lifetimes are not allowed in ghost constraint trait bounds") + err( + span, + "lifetimes are not allowed in type-conditional spec refinement trait bounds", + ) } fn validate_trait_bounds(trait_bounds: &syn::PredicateType) -> syn::Result<()> { @@ -537,7 +590,7 @@ fn validate_trait_bounds(trait_bounds: &syn::PredicateType) -> syn::Result<()> { } syn::TypeParamBound::Trait(trait_bound) => { if let Some(lt) = &trait_bound.lifetimes { - return disallowed_lifetime_error(lt.span()) + return disallowed_lifetime_error(lt.span()); } } } @@ -546,12 +599,12 @@ fn validate_trait_bounds(trait_bounds: &syn::PredicateType) -> syn::Result<()> { Ok(()) } -fn with_ghost_constraint_example(mut err: syn::Error) -> syn::Error { - err.combine(error(err.span(), "expected a trait bound and specifications in brackets, e.g.: `ghost_constraint(T: A + B + ..., [requires(...), ...])`")); +fn with_type_cond_spec_example(mut err: syn::Error) -> syn::Error { + err.combine(error(err.span(), "expected where constraint and specifications in brackets, e.g.: `refine_spec(where T: A + B, U: C, [requires(...), ...])`")); err } -/// A specification enclosed in another specification (e.g. in spec entailments or ghost constraints) +/// A specification enclosed in another specification (e.g. in spec entailments or type-conditional spec refinements) #[derive(Debug)] pub enum NestedSpec { Requires(T), @@ -602,27 +655,37 @@ fn translate_spec_ent( let generic_res = TokenTree::Ident(proc_macro2::Ident::new("GR", span)); let extract_args = (0..arg_count) - .map(|i| TokenTree::Ident(proc_macro2::Ident::new(&format!("__extract_arg{}", i), span))) + .map(|i| { + TokenTree::Ident(proc_macro2::Ident::new( + &format!("__extract_arg{}", i), + span, + )) + }) .collect::>(); - let extract_args_decl = extract_args.iter() + let extract_args_decl = extract_args + .iter() .zip(generics_args.iter()) - .map(|(ident, arg_type)| quote_spanned! { span => - #[prusti::spec_only] - fn #ident< - #(#generics_args),* , - #generic_res, - F: FnOnce( #(#generics_args),* ) -> #generic_res - >(_f: &F) -> #arg_type { unreachable!() } + .map(|(ident, arg_type)| { + quote_spanned! { span => + #[prusti::spec_only] + fn #ident< + #(#generics_args),* , + #generic_res, + F: FnOnce( #(#generics_args),* ) -> #generic_res + >(_f: &F) -> #arg_type { unreachable!() } + } }) .collect::>(); - let preconds = contract.iter() + let preconds = contract + .iter() .filter_map(|spec| match spec { NestedSpec::Requires(stream) => Some(stream.clone()), _ => None, }) .collect::>(); - let postconds = contract.into_iter() + let postconds = contract + .into_iter() .filter_map(|spec| match spec { NestedSpec::Ensures(stream) => Some(stream), _ => None, @@ -666,11 +729,13 @@ impl Quantifier { body: TokenStream, ) -> TokenStream { // TODO: refer to forall and exists with prusti_contracts:: prefix - let trigger_sets = triggers.into_iter() + let trigger_sets = triggers + .into_iter() .map(|set| { - let triggers = TokenStream::from_iter(set.into_iter() - .map(|trigger| quote_spanned! { trigger.span() => - #[prusti::spec_only] | #args | ( #trigger ), })); + let triggers = TokenStream::from_iter(set.into_iter().map(|trigger| { + quote_spanned! { trigger.span() => + #[prusti::spec_only] | #args | ( #trigger ), } + })); quote_spanned! { span => ( #triggers ) } }) //.map(|set| quote_spanned! { span => @@ -696,26 +761,16 @@ impl Quantifier { // // "==>" should still have the expected spacing of [Joint, Joint, Alone] // even though "==" and ">" are separate Rust operators. -fn operator2( - op: &str, - p1: &Punct, - p2: &Punct, -) -> bool { +fn operator2(op: &str, p1: &Punct, p2: &Punct) -> bool { let chars = op.chars().collect::>(); - [p1.as_char(), p2.as_char()] == chars[0..2] - && p1.spacing() == Joint - && p2.spacing() == Alone + [p1.as_char(), p2.as_char()] == chars[0..2] && p1.spacing() == Joint && p2.spacing() == Alone } -fn operator3( - op: &str, - p1: &Punct, - p2: &Punct, - p3: &Punct, -) -> bool { +fn operator3(op: &str, p1: &Punct, p2: &Punct, p3: &Punct) -> bool { let chars = op.chars().collect::>(); [p1.as_char(), p2.as_char(), p3.as_char()] == chars[0..3] - && p1.spacing() == Joint && p2.spacing() == Joint + && p1.spacing() == Joint + && p2.spacing() == Joint && p3.spacing() == Alone } @@ -737,69 +792,68 @@ impl PrustiToken { if p.as_char() == '|' && p.spacing() == proc_macro2::Spacing::Alone) } - fn parse_op2( - p1: &Punct, - p2: &Punct, - ) -> Option { + fn parse_op2(p1: &Punct, p2: &Punct) -> Option { let span = join_spans(p1.span(), p2.span()); - Some(Self::BinOp(span, if operator2("&&", p1, p2) { - PrustiBinaryOp::And - } else if operator2("||", p1, p2) { - PrustiBinaryOp::Or - } else if operator2("->", p1, p2) { - PrustiBinaryOp::Implies - } else if operator2("..", p1, p2) { - PrustiBinaryOp::Rust(RustOp::Range) - } else if operator2("+=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::AddAssign) - } else if operator2("-=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::SubtractAssign) - } else if operator2("*=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::MultiplyAssign) - } else if operator2("/=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::DivideAssign) - } else if operator2("%=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::ModuloAssign) - } else if operator2("&=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::BitAndAssign) - //} else if operator2("|=", p1, p2) { - // PrustiBinaryOp::Rust(RustOp::BitOrAssign) - } else if operator2("^=", p1, p2) { - PrustiBinaryOp::Rust(RustOp::BitXorAssign) - } else if operator2("=>", p1, p2) { - PrustiBinaryOp::Rust(RustOp::Arrow) - } else if operator2("|=", p1, p2) { - return Some(Self::SpecEnt(span, false)); - } else if operator2("~>", p1, p2) { - return Some(Self::CallDesc(span, false)); - } else { - return None; - })) + Some(Self::BinOp( + span, + if operator2("&&", p1, p2) { + PrustiBinaryOp::And + } else if operator2("||", p1, p2) { + PrustiBinaryOp::Or + } else if operator2("->", p1, p2) { + PrustiBinaryOp::Implies + } else if operator2("..", p1, p2) { + PrustiBinaryOp::Rust(RustOp::Range) + } else if operator2("+=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::AddAssign) + } else if operator2("-=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::SubtractAssign) + } else if operator2("*=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::MultiplyAssign) + } else if operator2("/=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::DivideAssign) + } else if operator2("%=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::ModuloAssign) + } else if operator2("&=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::BitAndAssign) + //} else if operator2("|=", p1, p2) { + // PrustiBinaryOp::Rust(RustOp::BitOrAssign) + } else if operator2("^=", p1, p2) { + PrustiBinaryOp::Rust(RustOp::BitXorAssign) + } else if operator2("=>", p1, p2) { + PrustiBinaryOp::Rust(RustOp::Arrow) + } else if operator2("|=", p1, p2) { + return Some(Self::SpecEnt(span, false)); + } else if operator2("~>", p1, p2) { + return Some(Self::CallDesc(span, false)); + } else { + return None; + }, + )) } - fn parse_op3( - p1: &Punct, - p2: &Punct, - p3: &Punct, - ) -> Option { + fn parse_op3(p1: &Punct, p2: &Punct, p3: &Punct) -> Option { let span = join_spans(join_spans(p1.span(), p2.span()), p3.span()); - Some(Self::BinOp(span, if operator3("==>", p1, p2, p3) { - PrustiBinaryOp::Implies - } else if operator3("===", p1, p2, p3) { - PrustiBinaryOp::SnapEq - } else if operator3("..=", p1, p2, p3) { - PrustiBinaryOp::Rust(RustOp::RangeInclusive) - } else if operator3("<<=", p1, p2, p3) { - PrustiBinaryOp::Rust(RustOp::LeftShiftAssign) - } else if operator3(">>=", p1, p2, p3) { - PrustiBinaryOp::Rust(RustOp::RightShiftAssign) - } else if operator3("|=!", p1, p2, p3) { - return Some(Self::SpecEnt(span, true)); - } else if operator3("~>!", p1, p2, p3) { - return Some(Self::CallDesc(span, true)); - } else { - return None; - })) + Some(Self::BinOp( + span, + if operator3("==>", p1, p2, p3) { + PrustiBinaryOp::Implies + } else if operator3("===", p1, p2, p3) { + PrustiBinaryOp::SnapEq + } else if operator3("..=", p1, p2, p3) { + PrustiBinaryOp::Rust(RustOp::RangeInclusive) + } else if operator3("<<=", p1, p2, p3) { + PrustiBinaryOp::Rust(RustOp::LeftShiftAssign) + } else if operator3(">>=", p1, p2, p3) { + PrustiBinaryOp::Rust(RustOp::RightShiftAssign) + } else if operator3("|=!", p1, p2, p3) { + return Some(Self::SpecEnt(span, true)); + } else if operator3("~>!", p1, p2, p3) { + return Some(Self::CallDesc(span, true)); + } else { + return None; + }, + )) } } @@ -824,25 +878,25 @@ impl PrustiBinaryOp { } } - fn translate( - &self, - span: Span, - lhs: TokenStream, - rhs: TokenStream, - ) -> TokenStream { + fn translate(&self, span: Span, raw_lhs: TokenStream, raw_rhs: TokenStream) -> TokenStream { + let lhs = quote_spanned! { raw_lhs.span() => (#raw_lhs) }; + let rhs = quote_spanned! { raw_rhs.span() => (#raw_rhs) }; match self { - Self::Rust(op) => op.translate(span, lhs, rhs), + Self::Rust(op) => op.translate(span, raw_lhs, raw_rhs), // implication is desugared into this form to avoid evaluation // order issues: `f(a, b)` makes Rust evaluate both `a` and `b` // before the `f` call Self::Implies => { // preserve span of LHS - let not_lhs = quote_spanned! { lhs.span() => !(#lhs) }; - quote_spanned! { span => (#not_lhs || (#rhs)) } + let not_lhs = quote_spanned! { lhs.span() => !#lhs }; + quote_spanned! { span => #not_lhs || #rhs } } Self::Or => quote_spanned! { span => #lhs || #rhs }, Self::And => quote_spanned! { span => #lhs && #rhs }, - Self::SnapEq => quote_spanned! { span => snapshot_equality(&#lhs, &#rhs) }, + Self::SnapEq => { + let joined_span = join_spans(lhs.span(), rhs.span()); + quote_spanned! { joined_span => snapshot_equality(&#lhs, &#rhs) } + } } } } @@ -869,20 +923,12 @@ enum RustOp { } impl RustOp { - fn translate( - &self, - span: Span, - lhs: TokenStream, - rhs: TokenStream, - ) -> TokenStream { + fn translate(&self, span: Span, lhs: TokenStream, rhs: TokenStream) -> TokenStream { let op = self.to_tokens(span); quote! { #lhs #op #rhs } } - fn to_tokens( - self, - span: Span, - ) -> TokenStream { + fn to_tokens(self, span: Span) -> TokenStream { match self { Self::RangeInclusive => quote_spanned! { span => ..= }, Self::LeftShiftAssign => quote_spanned! { span => <<= }, @@ -905,14 +951,16 @@ impl RustOp { } fn join_spans(s1: Span, s2: Span) -> Span { - // Tests don't run in the proc macro context - let is_proc_macro = std::panic::catch_unwind(|| s1.unwrap()).is_ok(); - if is_proc_macro { - // This works even when compiled with stable - s1.unwrap().join(s2.unwrap()).expect("Failed to join spans!").into() - } else { + // Tests don't run in the proc macro context, so this gets a little funky for them + if cfg!(test) { // During tests we don't care so much about returning a default s1.join(s2).unwrap_or(s1) + } else { + // This works even when compiled with stable, unlike `s1.join(s2)` + s1.unwrap() + .join(s2.unwrap()) + .expect("Failed to join spans!") + .into() } } @@ -921,49 +969,63 @@ mod tests { use super::*; macro_rules! assert_error { - ( $result:expr, $expected:expr ) => { - { - let _res = $result; - assert!(_res.is_err()); - let _err = _res.unwrap_err(); - assert_eq!(_err.to_string(), $expected); - } - }; + ( $result:expr, $expected:expr ) => {{ + let _res = $result; + assert!(_res.is_err()); + let _err = _res.unwrap_err(); + assert_eq!(_err.to_string(), $expected); + }}; } #[test] fn test_preparser() { assert_eq!( - parse_prusti("a ==> b".parse().unwrap()).unwrap().to_string(), - "(! (a) || (b))", + parse_prusti("a ==> b".parse().unwrap()) + .unwrap() + .to_string(), + "! (a) || (b)", ); assert_eq!( - parse_prusti("a ==> b ==> c".parse().unwrap()).unwrap().to_string(), - "(! (a) || ((! (b) || (c))))", + parse_prusti("a === b + c".parse().unwrap()) + .unwrap() + .to_string(), + "snapshot_equality (& (a) , & (b + c))", ); assert_eq!( - parse_prusti("(a ==> b && c) ==> d || e".parse().unwrap()).unwrap().to_string(), - "(! (((! (a) || (b && c)))) || (d || e))", + parse_prusti("a ==> b ==> c".parse().unwrap()) + .unwrap() + .to_string(), + "! (a) || (! (b) || (c))", ); assert_eq!( - parse_prusti("forall(|x: i32| a ==> b)".parse().unwrap()).unwrap().to_string(), - "forall (() , # [prusti :: spec_only] | x : i32 | -> bool { (((! (a) || (b))) : bool) })", + parse_prusti("(a ==> b && c) ==> d || e".parse().unwrap()) + .unwrap() + .to_string(), + "! ((! (a) || ((b) && (c)))) || ((d) || (e))", + ); + assert_eq!( + parse_prusti("forall(|x: i32| a ==> b)".parse().unwrap()) + .unwrap() + .to_string(), + "forall (() , # [prusti :: spec_only] | x : i32 | -> bool { ((! (a) || (b)) : bool) })", ); assert_eq!( parse_prusti("exists(|x: i32| a === b)".parse().unwrap()).unwrap().to_string(), - "exists (() , # [prusti :: spec_only] | x : i32 | -> bool { ((snapshot_equality (& a , & b)) : bool) })", + "exists (() , # [prusti :: spec_only] | x : i32 | -> bool { ((snapshot_equality (& (a) , & (b))) : bool) })", ); assert_eq!( parse_prusti("forall(|x: i32| a ==> b, triggers = [(c,), (d, e)])".parse().unwrap()).unwrap().to_string(), - "forall (((# [prusti :: spec_only] | x : i32 | (c) ,) , (# [prusti :: spec_only] | x : i32 | (d) , # [prusti :: spec_only] | x : i32 | (e) ,) ,) , # [prusti :: spec_only] | x : i32 | -> bool { (((! (a) || (b))) : bool) })", + "forall (((# [prusti :: spec_only] | x : i32 | (c) ,) , (# [prusti :: spec_only] | x : i32 | (d) , # [prusti :: spec_only] | x : i32 | (e) ,) ,) , # [prusti :: spec_only] | x : i32 | -> bool { ((! (a) || (b)) : bool) })", ); assert_eq!( - parse_prusti("assert!(a === b ==> b)".parse().unwrap()).unwrap().to_string(), - "assert ! ((! (snapshot_equality (& a , & b)) || (b)))", + parse_prusti("assert!(a === b ==> b)".parse().unwrap()) + .unwrap() + .to_string(), + "assert ! (! (snapshot_equality (& (a) , & (b))) || (b))", ); } - mod ghost_constraints { + mod type_cond_specs { use std::assert_matches::assert_matches; use super::*; @@ -971,20 +1033,57 @@ mod tests { #[test] fn invalid_args() { let err_invalid_bounds = "expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime"; - assert_error!(parse_ghost_constraint(quote!{ [requires(false)] }), err_invalid_bounds); - assert_error!(parse_ghost_constraint(quote!{ }), format!("unexpected end of input, {}", err_invalid_bounds)); - assert_error!(parse_ghost_constraint(quote!{T: A }), "expected `,`"); - assert_error!(parse_ghost_constraint(quote!{T: A, [requires(false)], "nope" }), "unexpected extra tokens"); - assert_error!(parse_ghost_constraint(quote!{[requires(false)], T: A }), err_invalid_bounds); - assert_error!(parse_ghost_constraint(quote!{T: A, }), "expected nested specification in brackets"); - assert_error!(parse_ghost_constraint(quote!{T: A, {} }), "expected nested specification in brackets"); + assert_error!( + parse_type_cond_spec(quote! { [requires(false)] }), + "expected `where`" + ); + assert_error!( + parse_type_cond_spec(quote! { where [requires(false)] }), + err_invalid_bounds + ); + assert_error!( + parse_type_cond_spec(quote! { [requires(false)], T: A }), + "expected `where`" + ); + assert_error!( + parse_type_cond_spec(quote! { where [requires(false)], T: A }), + err_invalid_bounds + ); + assert_error!( + parse_type_cond_spec(quote! {}), + format!("unexpected end of input, {}", "expected `where`") + ); + assert_error!(parse_type_cond_spec(quote! { T: A }), "expected `where`"); + assert_error!(parse_type_cond_spec(quote! { where T: A }), "expected `,`"); + assert_error!( + parse_type_cond_spec(quote! { where T: A, }), + "expected nested specification in brackets" + ); + assert_error!( + parse_type_cond_spec(quote! { where T: A, {} }), + err_invalid_bounds + ); + assert_error!( + parse_type_cond_spec(quote! { where T: A [requires(false)] }), + "expected `,`" + ); + assert_error!( + parse_type_cond_spec(quote! { where T: A, [requires(false)], "nope" }), + "unexpected extra tokens" + ); } #[test] fn multiple_bounds_multiple_specs() { - let constraint = parse_ghost_constraint(quote!{ T: A+B+Foo, [requires(true), ensures(false), pure]}).unwrap(); - - assert_bounds_eq(constraint.trait_bounds, quote!{ T : A + B + Foo < i32 > }); + let constraint = parse_type_cond_spec( + quote! { where T: A+B+Foo, U: C, [requires(true), ensures(false), pure]}, + ) + .unwrap(); + + assert_bounds_eq( + &constraint.trait_bounds, + &[quote! { T : A + B + Foo < i32 > }, quote! { U : C }], + ); match &constraint.specs[0] { NestedSpec::Requires(ts) => assert_eq!(ts.to_string(), "true"), _ => panic!(), @@ -999,28 +1098,35 @@ mod tests { #[test] fn no_specs() { - let constraint = parse_ghost_constraint(quote!{ T: A, []}).unwrap(); - assert_bounds_eq(constraint.trait_bounds, quote!{ T : A }); + let constraint = parse_type_cond_spec(quote! { where T: A, []}).unwrap(); + assert_bounds_eq(&constraint.trait_bounds, &[quote! { T : A }]); assert!(constraint.specs.is_empty()); } #[test] fn fully_qualified_trait_path() { - let constraint = parse_ghost_constraint(quote!{ T: path::to::A, [requires(true)]}).unwrap(); - assert_bounds_eq(constraint.trait_bounds, quote!{ T : path :: to :: A }); + let constraint = + parse_type_cond_spec(quote! { where T: path::to::A, [requires(true)]}).unwrap(); + assert_bounds_eq(&constraint.trait_bounds, &[quote! { T : path :: to :: A }]); } - + #[test] fn tuple_generics() { // just check that parsing succeeds - assert!(parse_ghost_constraint(quote!{ T: Fn<(i32,), Output = i32>, []}).is_ok()); - assert!(parse_ghost_constraint(quote!{ T: Fn<(i32,)>, []}).is_ok()); - assert!(parse_ghost_constraint(quote!{ T: Fn<(i32, bool)>, []}).is_ok()); - assert!(parse_ghost_constraint(quote!{ T: Fn<(i32, bool,)>, []}).is_ok()); + assert!(parse_type_cond_spec(quote! { where T: Fn<(i32,), Output = i32>, []}).is_ok()); + assert!(parse_type_cond_spec(quote! { where T: Fn<(i32,)>, []}).is_ok()); + assert!(parse_type_cond_spec(quote! { where T: Fn<(i32, bool)>, []}).is_ok()); + assert!(parse_type_cond_spec(quote! { where T: Fn<(i32, bool,)>, []}).is_ok()); } - - fn assert_bounds_eq(parsed: syn::PredicateType, quote: TokenStream) { - assert_eq!(syn::WherePredicate::Type(parsed), syn::parse_quote!{ #quote }); + + fn assert_bounds_eq(parsed: &[syn::PredicateType], quotes: &[TokenStream]) { + assert_eq!(parsed.len(), quotes.len()); + for (parsed, quote) in parsed.iter().zip(quotes.iter()) { + assert_eq!( + syn::WherePredicate::Type(parsed.clone()), + syn::parse_quote! { #quote } + ); + } } } } diff --git a/prusti-contracts/prusti-specs/src/specifications/untyped.rs b/prusti-contracts/prusti-specs/src/specifications/untyped.rs index 7ad57070b7a..0d434409f2e 100644 --- a/prusti-contracts/prusti-specs/src/specifications/untyped.rs +++ b/prusti-contracts/prusti-specs/src/specifications/untyped.rs @@ -1,10 +1,12 @@ -use proc_macro2::{TokenStream}; +use crate::common::HasSignature; +use proc_macro2::TokenStream; use quote::ToTokens; use syn::Signature; -use crate::common::HasSignature; -pub use super::common::{SpecType, SpecificationId}; -pub use super::preparser::Arg; +pub use super::{ + common::{SpecType, SpecificationId}, + preparser::Arg, +}; /// An abstraction over all kinds of function items. #[derive(Debug, PartialEq, Eq)] @@ -24,7 +26,7 @@ impl syn::parse::Parse for AnyFnItem { // We have an item Fn. input.advance_to(&fork); Ok(AnyFnItem::Fn(res)) - }, + } Err(_) => { // It is not a valid ItemFn. let item_method = input.parse()?; @@ -62,7 +64,7 @@ impl AnyFnItem { pub fn expect_impl_item(self) -> syn::ImplItemMethod { match self { AnyFnItem::ImplMethod(i) => i, - _ => unreachable!() + _ => unreachable!(), } } } @@ -93,4 +95,4 @@ impl ToTokens for AnyFnItem { AnyFnItem::ImplMethod(item) => item.to_tokens(tokens), } } -} \ No newline at end of file +} diff --git a/prusti-contracts/prusti-specs/src/ghost_constraints/mod.rs b/prusti-contracts/prusti-specs/src/type_cond_specs/mod.rs similarity index 60% rename from prusti-contracts/prusti-specs/src/ghost_constraints/mod.rs rename to prusti-contracts/prusti-specs/src/type_cond_specs/mod.rs index d7b8145d4c8..bb6b8348d36 100644 --- a/prusti-contracts/prusti-specs/src/ghost_constraints/mod.rs +++ b/prusti-contracts/prusti-specs/src/type_cond_specs/mod.rs @@ -1,6 +1,6 @@ use crate::{ - generate_for_ensures, generate_for_requires, do_generate_for_pure, parse_ghost_constraint, untyped, GeneratedResult, - NestedSpec, + generate_for_ensures, generate_for_pure_refinements, generate_for_requires, + parse_type_cond_spec, untyped, GeneratedResult, NestedSpec, }; use proc_macro2::TokenStream; use syn::{parse_quote_spanned, spanned::Spanned}; @@ -8,17 +8,17 @@ use syn::{parse_quote_spanned, spanned::Spanned}; pub fn generate(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult { let tokens_span = attr.span(); - // Parse ghost constraint information - let ghost_constraint = parse_ghost_constraint(attr)?; + // Parse type-conditional spec refinements information + let type_cond_spec = parse_type_cond_spec(attr)?; let mut new_items = vec![]; let mut new_attrs = vec![]; - for nested_spec in ghost_constraint.specs { + for nested_spec in type_cond_spec.specs { let (mut generated_items, generated_attrs) = match nested_spec { NestedSpec::Ensures(tokens) => generate_for_ensures(tokens, item)?, NestedSpec::Requires(tokens) => generate_for_requires(tokens, item)?, - NestedSpec::Pure => do_generate_for_pure(item)?, + NestedSpec::Pure => generate_for_pure_refinements(item)?, }; for generated_item in generated_items.iter_mut() { @@ -29,13 +29,13 @@ pub fn generate(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult // Add bounds as a where clause item_fn.sig.generics.where_clause = Some(generate_where_clause_for_spec( - &ghost_constraint.trait_bounds, + &type_cond_spec.trait_bounds, item_fn.sig.generics.where_clause.as_ref(), )); // Add attribute to mark this as a "specification with constraint" (used for processing the contract in `SpecCollector`) item_fn.attrs.push(parse_quote_spanned! {tokens_span=> - #[prusti::ghost_constraint_trait_bounds_in_where_clause] + #[prusti::type_cond_spec_trait_bounds_in_where_clause] }); } @@ -47,18 +47,19 @@ pub fn generate(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult } fn generate_where_clause_for_spec( - trait_bounds: &syn::PredicateType, + trait_bounds: &[syn::PredicateType], existing_where_clause: Option<&syn::WhereClause>, ) -> syn::WhereClause { - let span = trait_bounds.span(); - if let Some(mut where_clause) = existing_where_clause.cloned() { - where_clause.predicates.push(parse_quote_spanned! {span=> - #trait_bounds - }); - where_clause - } else { - parse_quote_spanned! {span=> - where #trait_bounds - } - } + let mut where_clause = existing_where_clause + .cloned() + .unwrap_or_else(|| syn::parse_quote!(where)); + where_clause + .predicates + .extend(trait_bounds.iter().map(|bound| -> syn::WherePredicate { + let span = bound.span(); + parse_quote_spanned! {span=> + #bound + } + })); + where_clause } diff --git a/prusti-contracts/prusti-specs/src/type_model/mod.rs b/prusti-contracts/prusti-specs/src/type_model/mod.rs index 50a61eda086..5d59e04d404 100644 --- a/prusti-contracts/prusti-specs/src/type_model/mod.rs +++ b/prusti-contracts/prusti-specs/src/type_model/mod.rs @@ -19,7 +19,8 @@ use crate::{ common::add_phantom_data_for_generic_params, user_provided_type_params::{ UserAnnotatedTypeParam, UserAnnotatedTypeParamParser, UserAnnotatedTypeParamParserError, - }, SPECS_VERSION, + }, + SPECS_VERSION, }; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; @@ -30,9 +31,11 @@ use uuid::Uuid; pub fn rewrite(item_struct: syn::ItemStruct) -> syn::Result> { let res = rewrite_internal(item_struct); match res { - Ok(result) => { - Ok(vec![syn::Item::Struct(result.model_struct), syn::Item::Trait(result.to_model_trait), syn::Item::Impl(result.model_impl)]) - }, + Ok(result) => Ok(vec![ + syn::Item::Struct(result.model_struct), + syn::Item::Trait(result.to_model_trait), + syn::Item::Impl(result.model_impl), + ]), Err(err) => Err(err.into()), } } @@ -135,7 +138,7 @@ impl ToModelTrait { .collect(); let model_path = &model_struct.path; - + let to_model_trait_ident = &idents.to_model_trait_ident; let item = parse_quote_spanned! {item_struct.span()=> #[allow(non_camel_case_types)] diff --git a/prusti-contracts/prusti-specs/src/user_provided_type_params.rs b/prusti-contracts/prusti-specs/src/user_provided_type_params.rs index 0a76f568e67..2ec67ca7903 100644 --- a/prusti-contracts/prusti-specs/src/user_provided_type_params.rs +++ b/prusti-contracts/prusti-specs/src/user_provided_type_params.rs @@ -1,7 +1,7 @@ +use crate::common::HasGenerics; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{spanned::Spanned, TypeParam}; -use crate::common::HasGenerics; #[derive(Debug)] pub(crate) enum UserAnnotatedTypeParam { diff --git a/prusti-interface/src/environment/body.rs b/prusti-interface/src/environment/body.rs index 8c02460b3a1..615b44e2f9f 100644 --- a/prusti-interface/src/environment/body.rs +++ b/prusti-interface/src/environment/body.rs @@ -292,7 +292,7 @@ impl<'tcx> EnvBody<'tcx> { /// prior to requesting their bodies with `get_spec_body` or exporting with `CrossCrateBodies::from`! pub(crate) fn load_spec_body(&mut self, def_id: LocalDefId) { // The same `def_id` may be referenced twice, e.g. see fn `constrained_contract_inherits_posts` in - // the `ghost-constraints-extend-base-attributes.rs` test case + // the `refinements-extend-base-attributes.rs` test case if self.specs.local.contains_key(&def_id) { return; } diff --git a/prusti-interface/src/environment/query.rs b/prusti-interface/src/environment/query.rs index 16bb1e0d950..51addde1275 100644 --- a/prusti-interface/src/environment/query.rs +++ b/prusti-interface/src/environment/query.rs @@ -321,23 +321,28 @@ impl<'tcx> EnvQuery<'tcx> { called_def_id: impl IntoParam, // what are we calling? call_substs: SubstsRef<'tcx>, ) -> (ProcedureDefId, SubstsRef<'tcx>) { - use prusti_rustc_interface::middle::ty::TypeVisitable; let called_def_id = called_def_id.into_param(); - // avoids a compiler-internal panic - if call_substs.needs_infer() { - return (called_def_id, call_substs); - } - - let param_env = self.tcx.param_env(caller_def_id.into_param()); - self.tcx - .resolve_instance(param_env.and((called_def_id, call_substs))) - .map(|opt_instance| { - opt_instance - .map(|instance| (instance.def_id(), instance.substs)) - .unwrap_or((called_def_id, call_substs)) - }) - .unwrap_or((called_def_id, call_substs)) + (|| { + // trait resolution does not depend on lifetimes, and in fact fails in the presence of uninferred regions + let clean_substs = self.tcx.erase_regions(call_substs); + + let param_env = self.tcx.param_env(caller_def_id.into_param()); + let instance = self + .tcx + .resolve_instance(param_env.and((called_def_id, clean_substs))) + .ok()??; + let resolved_def_id = instance.def_id(); + let resolved_substs = if resolved_def_id == called_def_id { + // if no trait resolution occurred, we can keep the non-erased substs + call_substs + } else { + instance.substs + }; + + Some((resolved_def_id, resolved_substs)) + })() + .unwrap_or((called_def_id, call_substs)) } /// Checks whether `ty` is copy. diff --git a/prusti-interface/src/specs/external.rs b/prusti-interface/src/specs/external.rs index 96163ea6855..960ba33d9ce 100644 --- a/prusti-interface/src/specs/external.rs +++ b/prusti-interface/src/specs/external.rs @@ -178,8 +178,14 @@ impl<'tcx> ExternSpecResolver<'tcx> { // TODO: there is more that we could check, e.g. that trait // constraints are the same (otherwise specs might not make sense) let (resolved_gens, current_gens) = ( - self.env_query.identity_substs(resolved_def_id).len(), - self.env_query.identity_substs(current_def_id).len(), + self.env_query + .identity_substs(resolved_def_id) + .non_erasable_generics() + .count(), + self.env_query + .identity_substs(current_def_id) + .non_erasable_generics() + .count(), ); if resolved_gens != current_gens { let diff = resolved_gens as isize - current_gens as isize; diff --git a/prusti-interface/src/specs/mod.rs b/prusti-interface/src/specs/mod.rs index f959a2566a9..d81d28f0185 100644 --- a/prusti-interface/src/specs/mod.rs +++ b/prusti-interface/src/specs/mod.rs @@ -120,7 +120,11 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { for (local_id, refs) in self.procedure_specs.iter() { let mut spec = SpecGraph::new(ProcedureSpecification::empty(local_id.to_def_id())); - let mut kind = refs.into(); + // We do not want to create an empty kind. + // This would lead to refinement inheritance if there is a trait involved. + // Instead, we require the user to explicitly make annotations. + spec.set_kind(refs.into()); + let mut kind_override = None; for spec_id_ref in &refs.spec_id_refs { match spec_id_ref { @@ -133,6 +137,9 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { self.env, ); } + SpecIdRef::Purity(spec_id) => { + spec.add_purity(*self.spec_functions.get(spec_id).unwrap(), self.env); + } SpecIdRef::Pledge { lhs, rhs } => { spec.add_pledge(typed::Pledge { reference: None, // FIXME: Currently only `result` is supported. @@ -143,9 +150,9 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { }); } SpecIdRef::Predicate(spec_id) => { - kind = ProcedureSpecificationKind::Predicate(Some( + kind_override = Some(ProcedureSpecificationKind::Predicate(Some( self.spec_functions.get(spec_id).unwrap().to_def_id(), - )); + ))); } SpecIdRef::Terminates(spec_id) => { spec.set_terminates(*self.spec_functions.get(spec_id).unwrap()); @@ -155,26 +162,15 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { spec.set_trusted(refs.trusted); - // We do not want to create an empty kind. - // This would lead to refinement inheritance if there is a trait involved. - // Instead, we require the user to explicitly make annotations. - spec.set_kind(kind); + if let Some(kind) = kind_override { + spec.set_kind(kind); + } - if !spec.specs_with_constraints.is_empty() - && !prusti_common::config::enable_ghost_constraints() + if !spec.specs_with_constraints.is_empty() && !*spec.base_spec.trusted.expect_inherent() { let span = self.env.query.get_def_span(*local_id); PrustiError::unsupported( - "Ghost constraints need to be enabled with the feature flag `enable_ghost_constraints`", - MultiSpan::from(span), - ) - .emit(&self.env.diagnostic); - } else if !spec.specs_with_constraints.is_empty() - && !*spec.base_spec.trusted.expect_inherent() - { - let span = self.env.query.get_def_span(*local_id); - PrustiError::unsupported( - "Ghost constraints can only be used on trusted functions", + "Type-conditional spec refinements can only be applied to trusted functions", MultiSpan::from(span), ) .emit(&self.env.diagnostic); @@ -325,6 +321,11 @@ fn get_procedure_spec_ids(def_id: DefId, attrs: &[ast::Attribute]) -> Option>, pub trusted: SpecificationItem, pub terminates: SpecificationItem>, + pub purity: SpecificationItem>, // for type-conditional spec refinements } impl ProcedureSpecification { @@ -217,6 +218,7 @@ impl ProcedureSpecification { pledges: SpecificationItem::Empty, trusted: SpecificationItem::Inherent(false), terminates: SpecificationItem::Inherent(None), + purity: SpecificationItem::Inherent(None), } } } @@ -353,7 +355,7 @@ impl SpecGraph { /// trait SomeTrait { /// #[requires(x > 0)] /// #[ensures(x > 0)] -/// #[ghost_constraint(T: MarkerTrait, [ +/// #[refine_spec(where T: MarkerTrait, [ /// requires(x > 10), /// ensures(x > 10), /// ])] @@ -365,7 +367,7 @@ impl SpecGraph { /// impl SomeTrait for SomeStruct { /// #[requires(x >= 0)] /// #[ensures(x > 10)] -/// #[ghost_constraint(T: MarkerTrait, [ +/// #[refine_spec(where T: MarkerTrait, [ /// requires(x >= -5), /// ensures(x > 20), /// ])] @@ -436,6 +438,23 @@ impl SpecGraph { } } + pub fn add_purity<'tcx>(&mut self, purity: LocalDefId, env: &Environment<'tcx>) { + match self.get_constraint(purity, env) { + None => { + unreachable!( + "separate purity defs are only used for type-conditional spec refinements" + ) + } + Some(constraint) => { + let constrained_spec = self.get_constrained_spec_mut(constraint); + constrained_spec.kind = + SpecificationItem::Inherent(ProcedureSpecificationKind::Pure); + // need to store this as well, since without pres or posts we couldn't find any def id with the trait bounds + constrained_spec.purity.set(Some(purity.to_def_id())); + } + } + } + /// Attaches the `pledge` to the base spec and all constrained specs. pub fn add_pledge(&mut self, pledge: Pledge) { self.base_spec.pledges.push(pledge.clone()); @@ -489,7 +508,7 @@ impl SpecGraph { env: &Environment<'tcx>, ) -> Option { let attrs = env.query.get_local_attributes(spec); - if has_trait_bounds_ghost_constraint(attrs) { + if has_trait_bounds_type_cond_spec(attrs) { return Some(SpecConstraintKind::ResolveGenericParamTraitBounds); } None @@ -752,6 +771,7 @@ impl Refinable for ProcedureSpecification { kind: self.kind.refine(&other.kind), trusted: self.trusted.refine(&other.trusted), terminates: self.terminates.refine(&other.terminates), + purity: self.purity.refine(&other.purity), } } } diff --git a/prusti-interface/src/utils.rs b/prusti-interface/src/utils.rs index bb8192048e7..268ab7ada70 100644 --- a/prusti-interface/src/utils.rs +++ b/prusti-interface/src/utils.rs @@ -245,8 +245,8 @@ pub fn has_to_model_impl_attr(attrs: &[ast::Attribute]) -> bool { has_prusti_attr(attrs, "type_models_to_model_impl") } -pub fn has_trait_bounds_ghost_constraint(attrs: &[ast::Attribute]) -> bool { - has_prusti_attr(attrs, "ghost_constraint_trait_bounds_in_where_clause") +pub fn has_trait_bounds_type_cond_spec(attrs: &[ast::Attribute]) -> bool { + has_prusti_attr(attrs, "type_cond_spec_trait_bounds_in_where_clause") } pub fn has_abstract_predicate_attr(attrs: &[ast::Attribute]) -> bool { diff --git a/prusti-tests/tests/cargo_verify/prusti_toml/output.stdout b/prusti-tests/tests/cargo_verify/prusti_toml/output.stdout index 9b0a422476a..0931f816dea 100644 --- a/prusti-tests/tests/cargo_verify/prusti_toml/output.stdout +++ b/prusti-tests/tests/cargo_verify/prusti_toml/output.stdout @@ -25,4 +25,4 @@ pub fn test2() { if !false { ::core::panicking::panic("assertion failed: false") }; } pub fn test3(x: usize) { let _y: usize = 1 - x; } -ProcedureSpecification { source: DefId(0:7 ~ prusti_toml[..]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ prusti_toml[..]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ prusti_toml[..]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ prusti_toml[..]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/fail/extern-spec/mods/legacy-use-statement.rs b/prusti-tests/tests/parse/fail/extern-spec/mods/legacy-use-statement.rs new file mode 100644 index 00000000000..cd0d3066a4c --- /dev/null +++ b/prusti-tests/tests/parse/fail/extern-spec/mods/legacy-use-statement.rs @@ -0,0 +1,17 @@ +use prusti_contracts::*; + +fn main() {} + +#[extern_spec] +mod example { + // parsing continues even after the first error + use crate::*; //~ ERROR: `use` statements have no effect in #[extern_spec] modules; module contents share the outer scope. + use crate::*; //~ ERROR: `use` statements have no effect in #[extern_spec] modules; module contents share the outer scope. + + #[pure] + fn foo(); +} + +mod example { + pub fn foo() {} +} diff --git a/prusti-tests/tests/parse/fail/extern-spec/no-bodies.rs b/prusti-tests/tests/parse/fail/extern-spec/no-bodies.rs new file mode 100644 index 00000000000..319d00f08a9 --- /dev/null +++ b/prusti-tests/tests/parse/fail/extern-spec/no-bodies.rs @@ -0,0 +1,27 @@ +use prusti_contracts::*; + +#[extern_spec] +impl Option { + #[pure] + fn is_some(&self) -> bool {} //~ ERROR: Unexpected method body. (Extern specs only define specifications.) +} + +#[extern_spec] +trait Clone { + #[pure] + fn clone(&self) -> Self {} //~ ERROR: Unexpected method body. (Extern specs only define specifications.) +} + +#[extern_spec] +impl Clone for i32 { + #[pure] + fn clone(&self) -> Self {} //~ ERROR: Unexpected method body. (Extern specs only define specifications.) +} + +#[extern_spec] +mod core { + mod mem { + #[pure] + pub fn size_of() -> usize {} //~ ERROR: Unexpected method body. (Extern specs only define specifications.) + } +} diff --git a/prusti-tests/tests/parse/fail/extern-spec/traits/default-methods-invalid.rs b/prusti-tests/tests/parse/fail/extern-spec/traits/default-methods-invalid.rs index baa521b4356..9d7b4d5763c 100644 --- a/prusti-tests/tests/parse/fail/extern-spec/traits/default-methods-invalid.rs +++ b/prusti-tests/tests/parse/fail/extern-spec/traits/default-methods-invalid.rs @@ -10,10 +10,10 @@ trait MyTrait { #[extern_spec] trait MyTrait { #[ensures(result == 42)] - fn foo(&self) -> i32 { //~ ERROR: Default methods in external trait specs are invalid + fn foo(&self) -> i32 { //~ ERROR: Unexpected method body. (Extern specs only define specifications.) 43 } } fn main() { -} \ No newline at end of file +} diff --git a/prusti-tests/tests/parse/fail/extern-spec/traits/legacy-generic-attributes.rs b/prusti-tests/tests/parse/fail/extern-spec/traits/legacy-generic-attributes.rs new file mode 100644 index 00000000000..744bd14e24a --- /dev/null +++ b/prusti-tests/tests/parse/fail/extern-spec/traits/legacy-generic-attributes.rs @@ -0,0 +1,23 @@ +use prusti_contracts::*; + +/// External traits +trait MyTrait { + fn get_value(&self) -> T; +} +/// External traits + +#[extern_spec] +trait MyTrait<#[concrete] i32> { + //~^ ERROR: The `#[concrete]` and `#[generic]` attributes are deprecated. To refine specs for specific concrete types, use type-conditional spec refinements instead. + #[ensures(result == 42)] + fn get_value(&self) -> i32; +} + +#[extern_spec] +trait MyTrait<#[generic] T> { + //~^ ERROR: The `#[concrete]` and `#[generic]` attributes are deprecated. To refine specs for specific concrete types, use type-conditional spec refinements instead. + #[ensures(true)] + fn get_value(&self) -> T; +} + +fn main() {} diff --git a/prusti-tests/tests/parse/fail/extern-spec/traits/where-clause-disallowed.rs b/prusti-tests/tests/parse/fail/extern-spec/traits/where-clause-disallowed.rs deleted file mode 100644 index 7da9ec3f102..00000000000 --- a/prusti-tests/tests/parse/fail/extern-spec/traits/where-clause-disallowed.rs +++ /dev/null @@ -1,18 +0,0 @@ -use prusti_contracts::*; - -/// External traits -trait MyTrait { - fn foo(&self); -} -/// External traits - -trait A {} -trait B {} -trait C {} - -#[extern_spec] -trait MyTrait where Self: A + B, Self: C { //~ ERROR: Where clauses for extern traits specs are not supported - fn foo(&self); -} - -fn main() {} \ No newline at end of file diff --git a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-1.rs b/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-1.rs deleted file mode 100644 index ac5cf5fba03..00000000000 --- a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-1.rs +++ /dev/null @@ -1,13 +0,0 @@ -use prusti_contracts::*; - -trait A { } - -#[ghost_constraint(T: 'static + A, [ //~ ERROR: lifetimes are not allowed in ghost constraint trait bounds - ensures(result > 0) -])] -fn foo(_x: T) -> i32 { - 42 -} - -fn main() { -} diff --git a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-2.rs b/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-2.rs deleted file mode 100644 index c4c494fe881..00000000000 --- a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-2.rs +++ /dev/null @@ -1,13 +0,0 @@ -use prusti_contracts::*; - -trait A { } - -#[ghost_constraint(T: for<'a> A<&'a i32>, [ //~ ERROR: lifetimes are not allowed in ghost constraint trait bounds - ensures(result > 0) -])] -fn foo(_x: T) -> i32 { - 42 -} - -fn main() { -} diff --git a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-1.rs b/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-1.rs deleted file mode 100644 index 9c8dff2396a..00000000000 --- a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-1.rs +++ /dev/null @@ -1,12 +0,0 @@ -use prusti_contracts::*; - -#[ghost_constraint([ - ensures(result > 0) //~ ERROR: expected `,` -])] -//~| ERROR: expected a trait bound and specifications in brackets, e.g.: `ghost_constraint(T: A + B + ..., [requires(...), ...])` -fn foo(_x: T) -> i32 { - 42 -} - -fn main() { -} diff --git a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-2.rs b/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-2.rs deleted file mode 100644 index c610009df8a..00000000000 --- a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-invalid-macro-arguments-2.rs +++ /dev/null @@ -1,12 +0,0 @@ -use prusti_contracts::*; - -#[ghost_constraint(42, [ //~ ERROR: expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime - ensures(result > 0) -])] -//~| ERROR: expected a trait bound and specifications in brackets, e.g.: `ghost_constraint(T: A + B + ..., [requires(...), ...])` -fn foo(_x: T) -> i32 { - 42 -} - -fn main() { -} diff --git a/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-1.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-1.rs new file mode 100644 index 00000000000..95268dc13ec --- /dev/null +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-1.rs @@ -0,0 +1,13 @@ +use prusti_contracts::*; + +trait A { } + +#[refine_spec(where T: 'static + A, [ //~ ERROR: lifetimes are not allowed in type-conditional spec refinement trait bounds + ensures(result > 0) +])] +fn foo(_x: T) -> i32 { + 42 +} + +fn main() { +} diff --git a/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-2.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-2.rs new file mode 100644 index 00000000000..9332f03fb3b --- /dev/null +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-2.rs @@ -0,0 +1,13 @@ +use prusti_contracts::*; + +trait A { } + +#[refine_spec(where T: for<'a> A<&'a i32> [ //~ ERROR: lifetimes are not allowed in type-conditional spec refinement trait bounds + ensures(result > 0) +])] +fn foo(_x: T) -> i32 { + 42 +} + +fn main() { +} diff --git a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-3.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-3.rs similarity index 63% rename from prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-3.rs rename to prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-3.rs index 0c19fddfbd9..15c3eb32bd4 100644 --- a/prusti-tests/tests/parse/fail/ghost-constraints/trait-bounds-illegal-3.rs +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-illegal-3.rs @@ -5,9 +5,10 @@ struct MyStruct { } impl MyStruct { - #[ghost_constraint(Self: MyStruct, [ //~ ERROR: expected trait, found struct `MyStruct` + #[refine_spec(where elf: MyStruct, [ //~ ERROR: expected trait, found struct `MyStruct` requires(x > 0) ])] + //~| ERROR: cannot find type `elf` in this scope [E0412] fn set_x(&mut self, x: T) { self.x = x; } diff --git a/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-1.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-1.rs new file mode 100644 index 00000000000..0320a38a263 --- /dev/null +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-1.rs @@ -0,0 +1,12 @@ +use prusti_contracts::*; + +#[refine_spec([ //~ ERROR: expected `where` + ensures(result > 0) +])] +//~| ERROR: expected where constraint and specifications in brackets, e.g.: `refine_spec(where T: A + B, U: C, [requires(...), ...])` +fn foo(_x: T) -> i32 { + 42 +} + +fn main() { +} diff --git a/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-2.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-2.rs new file mode 100644 index 00000000000..a8748bf2cfe --- /dev/null +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-2.rs @@ -0,0 +1,12 @@ +use prusti_contracts::*; + +#[refine_spec(where 42, [ //~ ERROR: expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + ensures(result > 0) +])] +//~| ERROR: expected where constraint and specifications in brackets, e.g.: `refine_spec(where T: A + B, U: C, [requires(...), ...])` +fn foo(_x: T) -> i32 { + 42 +} + +fn main() { +} diff --git a/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-3.rs b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-3.rs new file mode 100644 index 00000000000..18794677db2 --- /dev/null +++ b/prusti-tests/tests/parse/fail/type-cond-specs/trait-bounds-invalid-macro-arguments-3.rs @@ -0,0 +1,12 @@ +use prusti_contracts::*; + +#[refine_spec(where T: Copy [ //~ ERROR: expected `,` + ensures(result > 0) +])] +//~| ERROR: expected where constraint and specifications in brackets, e.g.: `refine_spec(where T: A + B, U: C, [requires(...), ...])` +fn foo(_x: T) -> i32 { + 42 +} + +fn main() { +} diff --git a/prusti-tests/tests/parse/ui/after_expiry.stdout b/prusti-tests/tests/parse/ui/after_expiry.stdout index 8173ddffd00..d941c81ba15 100644 --- a/prusti-tests/tests/parse/ui/after_expiry.stdout +++ b/prusti-tests/tests/parse/ui/after_expiry.stdout @@ -51,6 +51,6 @@ fn prusti_pledge_item_test3_$(NUM_UUID)(x: u32, #[prusti::specs_version = $(SPECS_VERSION)] fn test3(x: u32) -> u32 { 1 } fn main() {} -ProcedureSpecification { source: DefId(0:6 ~ after_expiry[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:5 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ after_expiry[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:7 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ after_expiry[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:9 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:6 ~ after_expiry[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:5 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ after_expiry[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:7 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ after_expiry[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:9 ~ after_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/and.stdout b/prusti-tests/tests/parse/ui/and.stdout index ac4ee486d3f..e587bf8d92e 100644 --- a/prusti-tests/tests/parse/ui/and.stdout +++ b/prusti-tests/tests/parse/ui/and.stdout @@ -24,7 +24,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test1_$(NUM_UUID)() -> bool { - !!((true && true): bool) + !!(((true) && (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -34,7 +34,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { - !!((true && true && true): bool) + !!((((true) && (true)) && (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -44,7 +44,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { - !!((true && (true && true)): bool) + !!(((true) && (((true) && (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -54,7 +54,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { - !!(((true && true) && true): bool) + !!(((((true) && (true))) && (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -64,14 +64,14 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test5_$(NUM_UUID)() -> bool { - !!(((true && true) && (true && true)): bool) + !!(((((true) && (true))) && (((true) && (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test5() {} fn main() {} -ProcedureSpecification { source: DefId(0:6 ~ and[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ and[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ and[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ and[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ and[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ and[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ and[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ and[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ and[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ and[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:6 ~ and[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ and[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ and[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ and[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ and[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ and[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ and[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ and[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ and[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ and[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/assert_on_expiry.stdout b/prusti-tests/tests/parse/ui/assert_on_expiry.stdout index f0c5df8cba5..dd95f8bd8ca 100644 --- a/prusti-tests/tests/parse/ui/assert_on_expiry.stdout +++ b/prusti-tests/tests/parse/ui/assert_on_expiry.stdout @@ -79,6 +79,6 @@ fn prusti_pledge_item_test3_$(NUM_UUID)(x: u32, #[prusti::specs_version = $(SPECS_VERSION)] fn test3(x: u32) -> u32 { 1 } fn main() {} -ProcedureSpecification { source: DefId(0:7 ~ assert_on_expiry[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:5 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID))), rhs: DefId(0:6 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ assert_on_expiry[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:8 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID))), rhs: DefId(0:9 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ assert_on_expiry[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:11 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID))), rhs: DefId(0:12 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ assert_on_expiry[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:5 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID))), rhs: DefId(0:6 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test1_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ assert_on_expiry[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:8 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID))), rhs: DefId(0:9 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test2_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ assert_on_expiry[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: Some(DefId(0:11 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID))), rhs: DefId(0:12 ~ assert_on_expiry[$(CRATE_ID)]::prusti_pledge_item_test3_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/composite.stdout b/prusti-tests/tests/parse/ui/composite.stdout index 5b1b6fe0b17..d0ded9256c8 100644 --- a/prusti-tests/tests/parse/ui/composite.stdout +++ b/prusti-tests/tests/parse/ui/composite.stdout @@ -43,7 +43,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test1_$(NUM_UUID)() -> bool { - !!(((!(true && true) || (true && true))): bool) + !!((!((true) && (true)) || ((true) && (true))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -53,7 +53,8 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { - !!((true && ((!(true) || (true))) && (true || true) && true): bool) + !!(((((true) && ((!(true) || (true)))) && (((true) || (true)))) && + (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -63,7 +64,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { - !!(((!((true && true)) || (true))): bool) + !!((!(((true) && (true))) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -73,7 +74,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { - !!(((!(((!(true) || (true))) && true) || (true))): bool) + !!((!(((!(true) || (true))) && (true)) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -83,8 +84,8 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test5_$(NUM_UUID)() -> bool { - !!((((!(true) || (true))) && ((!(true) || (true && (true || true))))): - bool) + !!((((!(true) || (true))) && + ((!(true) || ((true) && (((true) || (true))))))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -94,8 +95,8 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { - !!(((!((true && true)) || - ((!(true) || ((!(true) || ((!(true) || (true))))))))): bool) + !!((!(((true) && (true))) || + (!(true) || (!(true) || (!(true) || (true))))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -105,8 +106,8 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test7_$(NUM_UUID)() -> bool { - !!(((!((true && true)) || ((!((true && true)) || ((true && true)))))): - bool) + !!((!(((true) && (true))) || + (!(((true) && (true))) || (((true) && (true))))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -116,7 +117,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test8_$(NUM_UUID)() -> bool { - !!(((!((true || true)) || ((true || true)))): bool) + !!((!(((true) || (true))) || (((true) || (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -126,7 +127,8 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test9_$(NUM_UUID)() -> bool { - !!(((!((true || true)) || ((true || (true && (true || true)))))): bool) + !!((!(((true) || (true))) || + (((true) || (((true) && (((true) || (true)))))))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -136,10 +138,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test10_$(NUM_UUID)() -> bool { - !!((true && - forall((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })): bool) + !!(((true) && + (forall((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -161,10 +163,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test13_$(NUM_UUID)() -> bool { - !!(((!(true) || - ((!(forall((), - #[prusti::spec_only] |a: i32, b: i32| -> bool - { ((a == 5): bool) })) || (true))))): bool) + !!((!(true) || + (!(forall((), + #[prusti::spec_only] |a: i32, b: i32| -> bool + { ((a == 5): bool) })) || (true))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -174,10 +176,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test14_$(NUM_UUID)() -> bool { - !!(((!(true) || - (forall((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })))): bool) + !!((!(true) || + (forall((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -187,9 +189,9 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test15_$(NUM_UUID)() -> bool { - !!(((!(forall((), - #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) - || (true))): bool) + !!((!(forall((), + #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) + || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -199,13 +201,13 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test16_$(NUM_UUID)() -> bool { - !!(((!(forall((), - #[prusti::spec_only] |b: i32| -> bool - { ((b == 10): bool) })) || - ((!(true) || - (forall((), - #[prusti::spec_only] |a: u32, b: u32| -> bool - { ((a == 5): bool) })))))): bool) + !!((!(forall((), + #[prusti::spec_only] |b: i32| -> bool + { ((b == 10): bool) })) || + (!(true) || + (forall((), + #[prusti::spec_only] |a: u32, b: u32| -> bool + { ((a == 5): bool) })))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -215,10 +217,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test17_$(NUM_UUID)() -> bool { - !!((true && - exists((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })): bool) + !!(((true) && + (exists((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -240,10 +242,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test20_$(NUM_UUID)() -> bool { - !!(((!(true) || - ((!(exists((), - #[prusti::spec_only] |a: i32, b: i32| -> bool - { ((a == 5): bool) })) || (true))))): bool) + !!((!(true) || + (!(exists((), + #[prusti::spec_only] |a: i32, b: i32| -> bool + { ((a == 5): bool) })) || (true))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -253,10 +255,10 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test21_$(NUM_UUID)() -> bool { - !!(((!(true) || - (exists((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })))): bool) + !!((!(true) || + (exists((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -266,9 +268,9 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test22_$(NUM_UUID)() -> bool { - !!(((!(exists((), - #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) - || (true))): bool) + !!((!(exists((), + #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) + || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -278,13 +280,13 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test23_$(NUM_UUID)() -> bool { - !!(((!(exists((), - #[prusti::spec_only] |b: i32| -> bool - { ((b == 10): bool) })) || - ((!(true) || - (exists((), - #[prusti::spec_only] |a: u32, b: u32| -> bool - { ((a == 5): bool) })))))): bool) + !!((!(exists((), + #[prusti::spec_only] |b: i32| -> bool + { ((b == 10): bool) })) || + (!(true) || + (exists((), + #[prusti::spec_only] |a: u32, b: u32| -> bool + { ((a == 5): bool) })))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -294,7 +296,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test24_$(NUM_UUID)() -> bool { - !!((true && true || true): bool) + !!((((true) && (true)) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -304,11 +306,12 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test25_$(NUM_UUID)() -> bool { - !!((forall((), #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) }) + !!(((forall((), + #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) || - forall((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })): bool) + (forall((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -318,37 +321,38 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test26_$(NUM_UUID)() -> bool { - !!((exists((), #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) }) + !!(((exists((), + #[prusti::spec_only] |a: i32| -> bool { ((a == 5): bool) })) || - exists((), - #[prusti::spec_only] |a: i32| -> bool - { ((a == 5): bool) })): bool) + (exists((), + #[prusti::spec_only] |a: i32| -> bool + { ((a == 5): bool) }))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test26() {} fn main() {} -ProcedureSpecification { source: DefId(0:6 ~ composite[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ composite[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ composite[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ composite[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ composite[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ composite[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ composite[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ composite[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ composite[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ composite[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:16 ~ composite[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:15 ~ composite[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:18 ~ composite[$(CRATE_ID)]::test7), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ composite[$(CRATE_ID)]::prusti_pre_item_test7_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:20 ~ composite[$(CRATE_ID)]::test8), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ composite[$(CRATE_ID)]::prusti_pre_item_test8_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:22 ~ composite[$(CRATE_ID)]::test9), kind: Inherent(Impure), pres: Inherent([DefId(0:21 ~ composite[$(CRATE_ID)]::prusti_pre_item_test9_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:25 ~ composite[$(CRATE_ID)]::test10), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ composite[$(CRATE_ID)]::prusti_pre_item_test10_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:28 ~ composite[$(CRATE_ID)]::test12), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ composite[$(CRATE_ID)]::prusti_pre_item_test12_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:31 ~ composite[$(CRATE_ID)]::test13), kind: Inherent(Impure), pres: Inherent([DefId(0:29 ~ composite[$(CRATE_ID)]::prusti_pre_item_test13_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:34 ~ composite[$(CRATE_ID)]::test14), kind: Inherent(Impure), pres: Inherent([DefId(0:32 ~ composite[$(CRATE_ID)]::prusti_pre_item_test14_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:37 ~ composite[$(CRATE_ID)]::test15), kind: Inherent(Impure), pres: Inherent([DefId(0:35 ~ composite[$(CRATE_ID)]::prusti_pre_item_test15_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:41 ~ composite[$(CRATE_ID)]::test16), kind: Inherent(Impure), pres: Inherent([DefId(0:38 ~ composite[$(CRATE_ID)]::prusti_pre_item_test16_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:44 ~ composite[$(CRATE_ID)]::test17), kind: Inherent(Impure), pres: Inherent([DefId(0:42 ~ composite[$(CRATE_ID)]::prusti_pre_item_test17_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:47 ~ composite[$(CRATE_ID)]::test19), kind: Inherent(Impure), pres: Inherent([DefId(0:45 ~ composite[$(CRATE_ID)]::prusti_pre_item_test19_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:50 ~ composite[$(CRATE_ID)]::test20), kind: Inherent(Impure), pres: Inherent([DefId(0:48 ~ composite[$(CRATE_ID)]::prusti_pre_item_test20_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:53 ~ composite[$(CRATE_ID)]::test21), kind: Inherent(Impure), pres: Inherent([DefId(0:51 ~ composite[$(CRATE_ID)]::prusti_pre_item_test21_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:56 ~ composite[$(CRATE_ID)]::test22), kind: Inherent(Impure), pres: Inherent([DefId(0:54 ~ composite[$(CRATE_ID)]::prusti_pre_item_test22_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:60 ~ composite[$(CRATE_ID)]::test23), kind: Inherent(Impure), pres: Inherent([DefId(0:57 ~ composite[$(CRATE_ID)]::prusti_pre_item_test23_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:62 ~ composite[$(CRATE_ID)]::test24), kind: Inherent(Impure), pres: Inherent([DefId(0:61 ~ composite[$(CRATE_ID)]::prusti_pre_item_test24_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:66 ~ composite[$(CRATE_ID)]::test25), kind: Inherent(Impure), pres: Inherent([DefId(0:63 ~ composite[$(CRATE_ID)]::prusti_pre_item_test25_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:70 ~ composite[$(CRATE_ID)]::test26), kind: Inherent(Impure), pres: Inherent([DefId(0:67 ~ composite[$(CRATE_ID)]::prusti_pre_item_test26_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:6 ~ composite[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ composite[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ composite[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ composite[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ composite[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ composite[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ composite[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ composite[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ composite[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ composite[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:16 ~ composite[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:15 ~ composite[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:18 ~ composite[$(CRATE_ID)]::test7), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ composite[$(CRATE_ID)]::prusti_pre_item_test7_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:20 ~ composite[$(CRATE_ID)]::test8), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ composite[$(CRATE_ID)]::prusti_pre_item_test8_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:22 ~ composite[$(CRATE_ID)]::test9), kind: Inherent(Impure), pres: Inherent([DefId(0:21 ~ composite[$(CRATE_ID)]::prusti_pre_item_test9_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:25 ~ composite[$(CRATE_ID)]::test10), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ composite[$(CRATE_ID)]::prusti_pre_item_test10_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:28 ~ composite[$(CRATE_ID)]::test12), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ composite[$(CRATE_ID)]::prusti_pre_item_test12_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:31 ~ composite[$(CRATE_ID)]::test13), kind: Inherent(Impure), pres: Inherent([DefId(0:29 ~ composite[$(CRATE_ID)]::prusti_pre_item_test13_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:34 ~ composite[$(CRATE_ID)]::test14), kind: Inherent(Impure), pres: Inherent([DefId(0:32 ~ composite[$(CRATE_ID)]::prusti_pre_item_test14_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:37 ~ composite[$(CRATE_ID)]::test15), kind: Inherent(Impure), pres: Inherent([DefId(0:35 ~ composite[$(CRATE_ID)]::prusti_pre_item_test15_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:41 ~ composite[$(CRATE_ID)]::test16), kind: Inherent(Impure), pres: Inherent([DefId(0:38 ~ composite[$(CRATE_ID)]::prusti_pre_item_test16_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:44 ~ composite[$(CRATE_ID)]::test17), kind: Inherent(Impure), pres: Inherent([DefId(0:42 ~ composite[$(CRATE_ID)]::prusti_pre_item_test17_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:47 ~ composite[$(CRATE_ID)]::test19), kind: Inherent(Impure), pres: Inherent([DefId(0:45 ~ composite[$(CRATE_ID)]::prusti_pre_item_test19_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:50 ~ composite[$(CRATE_ID)]::test20), kind: Inherent(Impure), pres: Inherent([DefId(0:48 ~ composite[$(CRATE_ID)]::prusti_pre_item_test20_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:53 ~ composite[$(CRATE_ID)]::test21), kind: Inherent(Impure), pres: Inherent([DefId(0:51 ~ composite[$(CRATE_ID)]::prusti_pre_item_test21_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:56 ~ composite[$(CRATE_ID)]::test22), kind: Inherent(Impure), pres: Inherent([DefId(0:54 ~ composite[$(CRATE_ID)]::prusti_pre_item_test22_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:60 ~ composite[$(CRATE_ID)]::test23), kind: Inherent(Impure), pres: Inherent([DefId(0:57 ~ composite[$(CRATE_ID)]::prusti_pre_item_test23_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:62 ~ composite[$(CRATE_ID)]::test24), kind: Inherent(Impure), pres: Inherent([DefId(0:61 ~ composite[$(CRATE_ID)]::prusti_pre_item_test24_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:66 ~ composite[$(CRATE_ID)]::test25), kind: Inherent(Impure), pres: Inherent([DefId(0:63 ~ composite[$(CRATE_ID)]::prusti_pre_item_test25_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:70 ~ composite[$(CRATE_ID)]::test26), kind: Inherent(Impure), pres: Inherent([DefId(0:67 ~ composite[$(CRATE_ID)]::prusti_pre_item_test26_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/exists.stdout b/prusti-tests/tests/parse/ui/exists.stdout index 98e6b8565f9..b59e5d02e58 100644 --- a/prusti-tests/tests/parse/ui/exists.stdout +++ b/prusti-tests/tests/parse/ui/exists.stdout @@ -40,7 +40,7 @@ fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { !!((exists((), #[prusti::spec_only] |a: i32, b: i32| -> bool { - (((a + b == a + b && true) == (a + b == a + b)): bool) + ((((a + b == a + b) && (true)) == (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] @@ -53,8 +53,7 @@ non_snake_case)] fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { !!((exists((), #[prusti::spec_only] |a: i32, b: i32| -> bool - { (((!(a + b == a + b) || (a + b == a + b))): bool) })): - bool) + { ((!(a + b == a + b) || (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -65,7 +64,7 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { !!((exists(((#[prusti::spec_only] |a: i32| (1), - #[prusti::spec_only] |a: i32| (2 == 2 && true)),), + #[prusti::spec_only] |a: i32| ((2 == 2) && (true))),), #[prusti::spec_only] |a: i32| -> bool { ((a + a == a + a): bool) })): bool) } @@ -98,16 +97,15 @@ fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { #[prusti::spec_only] |a: i32, b: i32| (2)), (#[prusti::spec_only] |a: i32, b: i32| (1),)), #[prusti::spec_only] |a: i32, b: i32| -> bool - { (((!(a + b == a + b) || (a + b == a + b))): bool) })): - bool) + { ((!(a + b == a + b) || (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test6() {} fn main() {} -ProcedureSpecification { source: DefId(0:7 ~ exists[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ exists[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ exists[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ exists[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ exists[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ exists[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:18 ~ exists[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ exists[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ exists[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ exists[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:33 ~ exists[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:25 ~ exists[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ exists[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ exists[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ exists[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ exists[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ exists[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ exists[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:18 ~ exists[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ exists[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ exists[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ exists[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:33 ~ exists[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:25 ~ exists[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/expression.stdout b/prusti-tests/tests/parse/ui/expression.stdout index 13f9b2807e9..a675007c16a 100644 --- a/prusti-tests/tests/parse/ui/expression.stdout +++ b/prusti-tests/tests/parse/ui/expression.stdout @@ -21,7 +21,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test1_$(NUM_UUID)() -> bool { - !!((1 == 1 && 1 != 2): bool) + !!(((1 == 1) && (1 != 2)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -32,11 +32,11 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_post_item_test2_$(NUM_UUID)(result: ()) -> bool { - !!((1 == 1 || 1 == 2): bool) + !!(((1 == 1) || (1 == 2)): bool) } #[prusti::post_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test2() {} fn main() {} -ProcedureSpecification { source: DefId(0:6 ~ expression[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ expression[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ expression[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:7 ~ expression[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:6 ~ expression[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ expression[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ expression[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:7 ~ expression[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/forall.stdout b/prusti-tests/tests/parse/ui/forall.stdout index e7325f0a726..ff2e6c925c2 100644 --- a/prusti-tests/tests/parse/ui/forall.stdout +++ b/prusti-tests/tests/parse/ui/forall.stdout @@ -40,7 +40,7 @@ fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { !!((forall((), #[prusti::spec_only] |a: i32, b: i32| -> bool { - (((a + b == a + b && true) == (a + b == a + b)): bool) + ((((a + b == a + b) && (true)) == (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] @@ -53,8 +53,7 @@ non_snake_case)] fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { !!((forall((), #[prusti::spec_only] |a: i32, b: i32| -> bool - { (((!(a + b == a + b) || (a + b == a + b))): bool) })): - bool) + { ((!(a + b == a + b) || (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -65,7 +64,7 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { !!((forall(((#[prusti::spec_only] |a: i32| (1), - #[prusti::spec_only] |a: i32| (2 == 2 && true)),), + #[prusti::spec_only] |a: i32| ((2 == 2) && (true))),), #[prusti::spec_only] |a: i32| -> bool { ((a + a == a + a): bool) })): bool) } @@ -98,16 +97,15 @@ fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { #[prusti::spec_only] |a: i32, b: i32| (2)), (#[prusti::spec_only] |a: i32, b: i32| (1),)), #[prusti::spec_only] |a: i32, b: i32| -> bool - { (((!(a + b == a + b) || (a + b == a + b))): bool) })): - bool) + { ((!(a + b == a + b) || (a + b == a + b)): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test6() {} fn main() {} -ProcedureSpecification { source: DefId(0:7 ~ forall[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ forall[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ forall[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ forall[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ forall[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:18 ~ forall[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ forall[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ forall[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ forall[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:33 ~ forall[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:25 ~ forall[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ forall[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ forall[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ forall[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ forall[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ forall[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:18 ~ forall[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ forall[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ forall[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ forall[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:33 ~ forall[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:25 ~ forall[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/implies.stdout b/prusti-tests/tests/parse/ui/implies.stdout index 93a6903de92..89bdf502e21 100644 --- a/prusti-tests/tests/parse/ui/implies.stdout +++ b/prusti-tests/tests/parse/ui/implies.stdout @@ -29,7 +29,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test1_$(NUM_UUID)() -> bool { - !!(((!(true) || (true))): bool) + !!((!(true) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -39,7 +39,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { - !!(((!(true) || ((!(true) || (true))))): bool) + !!((!(true) || (!(true) || (true))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -49,7 +49,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { - !!(((!(true) || (((!(true) || (true)))))): bool) + !!((!(true) || ((!(true) || (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -59,7 +59,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { - !!(((!(((!(true) || (true)))) || (true))): bool) + !!((!((!(true) || (true))) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -69,7 +69,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test5_$(NUM_UUID)() -> bool { - !!(((!(((!(true) || (true)))) || (((!(true) || (true)))))): bool) + !!((!((!(true) || (true))) || ((!(true) || (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -79,7 +79,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test21_$(NUM_UUID)() -> bool { - !!(((!(true) || (true))): bool) + !!((!(true) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -89,7 +89,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test22_$(NUM_UUID)() -> bool { - !!(((!(true) || ((!(true) || (true))))): bool) + !!((!(true) || (!(true) || (true))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -99,7 +99,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test23_$(NUM_UUID)() -> bool { - !!(((!(true) || (((!(true) || (true)))))): bool) + !!((!(true) || ((!(true) || (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -109,7 +109,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test24_$(NUM_UUID)() -> bool { - !!(((!(((!(true) || (true)))) || (true))): bool) + !!((!((!(true) || (true))) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -119,19 +119,19 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test25_$(NUM_UUID)() -> bool { - !!(((!(((!(true) || (true)))) || (((!(true) || (true)))))): bool) + !!((!((!(true) || (true))) || ((!(true) || (true)))): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test25() {} fn main() {} -ProcedureSpecification { source: DefId(0:6 ~ implies[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ implies[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ implies[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ implies[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ implies[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ implies[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ implies[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ implies[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ implies[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ implies[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:16 ~ implies[$(CRATE_ID)]::test21), kind: Inherent(Impure), pres: Inherent([DefId(0:15 ~ implies[$(CRATE_ID)]::prusti_pre_item_test21_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:18 ~ implies[$(CRATE_ID)]::test22), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ implies[$(CRATE_ID)]::prusti_pre_item_test22_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:20 ~ implies[$(CRATE_ID)]::test23), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ implies[$(CRATE_ID)]::prusti_pre_item_test23_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:22 ~ implies[$(CRATE_ID)]::test24), kind: Inherent(Impure), pres: Inherent([DefId(0:21 ~ implies[$(CRATE_ID)]::prusti_pre_item_test24_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ implies[$(CRATE_ID)]::test25), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ implies[$(CRATE_ID)]::prusti_pre_item_test25_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:6 ~ implies[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ implies[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ implies[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:7 ~ implies[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ implies[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ implies[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ implies[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ implies[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ implies[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ implies[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:16 ~ implies[$(CRATE_ID)]::test21), kind: Inherent(Impure), pres: Inherent([DefId(0:15 ~ implies[$(CRATE_ID)]::prusti_pre_item_test21_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:18 ~ implies[$(CRATE_ID)]::test22), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ implies[$(CRATE_ID)]::prusti_pre_item_test22_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:20 ~ implies[$(CRATE_ID)]::test23), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ implies[$(CRATE_ID)]::prusti_pre_item_test23_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:22 ~ implies[$(CRATE_ID)]::test24), kind: Inherent(Impure), pres: Inherent([DefId(0:21 ~ implies[$(CRATE_ID)]::prusti_pre_item_test24_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ implies[$(CRATE_ID)]::test25), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ implies[$(CRATE_ID)]::prusti_pre_item_test25_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/predicates-visibility.stdout b/prusti-tests/tests/parse/ui/predicates-visibility.stdout index 41e571fd3f9..2567cbcb096 100644 --- a/prusti-tests/tests/parse/ui/predicates-visibility.stdout +++ b/prusti-tests/tests/parse/ui/predicates-visibility.stdout @@ -50,5 +50,5 @@ fn prusti_pre_item_test_pub_pred_$(NUM_UUID)() -> bool { #[prusti::specs_version = $(SPECS_VERSION)] fn test_pub_pred() {} fn main() {} -ProcedureSpecification { source: DefId(0:15 ~ predicates_visibility[$(CRATE_ID)]::foo::pred1), kind: Inherent(Predicate(Some(DefId(0:13 ~ predicates_visibility[$(CRATE_ID)]::foo::prusti_pred_item_pred1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:17 ~ predicates_visibility[$(CRATE_ID)]::test_pub_pred), kind: Inherent(Impure), pres: Inherent([DefId(0:16 ~ predicates_visibility[$(CRATE_ID)]::prusti_pre_item_test_pub_pred_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:15 ~ predicates_visibility[$(CRATE_ID)]::foo::pred1), kind: Inherent(Predicate(Some(DefId(0:13 ~ predicates_visibility[$(CRATE_ID)]::foo::prusti_pred_item_pred1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:17 ~ predicates_visibility[$(CRATE_ID)]::test_pub_pred), kind: Inherent(Impure), pres: Inherent([DefId(0:16 ~ predicates_visibility[$(CRATE_ID)]::prusti_pre_item_test_pub_pred_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/predicates.stdout b/prusti-tests/tests/parse/ui/predicates.stdout index 0e612f1f6d7..1eb1368fe57 100644 --- a/prusti-tests/tests/parse/ui/predicates.stdout +++ b/prusti-tests/tests/parse/ui/predicates.stdout @@ -84,7 +84,7 @@ fn prusti_pred_item_forall_implication_$(NUM_UUID)() -> bool { !!((forall((), #[prusti::spec_only] |x: usize| -> bool - { (((!((x != 0)) || (x * 2 != 0))): bool) })): bool) + { ((!((x != 0)) || (x * 2 != 0)): bool) })): bool) } #[allow(unused_must_use, unused_variables, dead_code)] #[prusti::pred_spec_id_ref = "$(NUM_UUID)"] @@ -102,7 +102,7 @@ fn prusti_pred_item_exists_implication_$(NUM_UUID)() -> bool { !!((exists((), #[prusti::spec_only] |x: usize| -> bool - { (((!((x != 0)) || (x * 2 != 0))): bool) })): bool) + { ((!((x != 0)) || (x * 2 != 0)): bool) })): bool) } #[allow(unused_must_use, unused_variables, dead_code)] #[prusti::pred_spec_id_ref = "$(NUM_UUID)"] @@ -113,9 +113,9 @@ fn exists_implication() -> bool { &[]))])) } fn main() {} -ProcedureSpecification { source: DefId(0:7 ~ predicates[$(CRATE_ID)]::pred1), kind: Inherent(Predicate(Some(DefId(0:5 ~ predicates[$(CRATE_ID)]::prusti_pred_item_pred1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ predicates[$(CRATE_ID)]::pred2), kind: Inherent(Predicate(Some(DefId(0:10 ~ predicates[$(CRATE_ID)]::prusti_pred_item_pred2_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:9 ~ predicates[$(CRATE_ID)]::use_pred1), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ predicates[$(CRATE_ID)]::prusti_pre_item_use_pred1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ predicates[$(CRATE_ID)]::use_pred2), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ predicates[$(CRATE_ID)]::prusti_pre_item_use_pred2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:17 ~ predicates[$(CRATE_ID)]::forall_implication), kind: Inherent(Predicate(Some(DefId(0:15 ~ predicates[$(CRATE_ID)]::prusti_pred_item_forall_implication_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:20 ~ predicates[$(CRATE_ID)]::exists_implication), kind: Inherent(Predicate(Some(DefId(0:18 ~ predicates[$(CRATE_ID)]::prusti_pred_item_exists_implication_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ predicates[$(CRATE_ID)]::pred1), kind: Inherent(Predicate(Some(DefId(0:5 ~ predicates[$(CRATE_ID)]::prusti_pred_item_pred1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ predicates[$(CRATE_ID)]::pred2), kind: Inherent(Predicate(Some(DefId(0:10 ~ predicates[$(CRATE_ID)]::prusti_pred_item_pred2_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:9 ~ predicates[$(CRATE_ID)]::use_pred1), kind: Inherent(Impure), pres: Inherent([DefId(0:8 ~ predicates[$(CRATE_ID)]::prusti_pre_item_use_pred1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ predicates[$(CRATE_ID)]::use_pred2), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ predicates[$(CRATE_ID)]::prusti_pre_item_use_pred2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:17 ~ predicates[$(CRATE_ID)]::forall_implication), kind: Inherent(Predicate(Some(DefId(0:15 ~ predicates[$(CRATE_ID)]::prusti_pred_item_forall_implication_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:20 ~ predicates[$(CRATE_ID)]::exists_implication), kind: Inherent(Predicate(Some(DefId(0:18 ~ predicates[$(CRATE_ID)]::prusti_pred_item_exists_implication_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/trait-bounds.stdout b/prusti-tests/tests/parse/ui/trait-bounds.stdout index 4dc68f76af4..dffbf72abcd 100644 --- a/prusti-tests/tests/parse/ui/trait-bounds.stdout +++ b/prusti-tests/tests/parse/ui/trait-bounds.stdout @@ -47,4 +47,4 @@ impl<'a, T: PartialEq, const L : usize> } } fn main() {} -ProcedureSpecification { source: DefId(0:31 ~ trait_bounds[$(CRATE_ID)]::{impl#1}::bar), kind: Inherent(Pure), pres: Empty, posts: Inherent([DefId(0:29 ~ trait_bounds[$(CRATE_ID)]::{impl#1}::prusti_post_item_bar_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(true), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:31 ~ trait_bounds[$(CRATE_ID)]::{impl#1}::bar), kind: Inherent(Pure), pres: Empty, posts: Inherent([DefId(0:29 ~ trait_bounds[$(CRATE_ID)]::{impl#1}::prusti_post_item_bar_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(true), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/traits.stdout b/prusti-tests/tests/parse/ui/traits.stdout index b60b60c9e89..48b8cc30964 100644 --- a/prusti-tests/tests/parse/ui/traits.stdout +++ b/prusti-tests/tests/parse/ui/traits.stdout @@ -203,15 +203,15 @@ trait Test4 { fn test2(&self); } fn main() {} -ProcedureSpecification { source: DefId(0:10 ~ traits[$(CRATE_ID)]::Test1::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ traits[$(CRATE_ID)]::Test1::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ traits[$(CRATE_ID)]::Test1::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ traits[$(CRATE_ID)]::Test1::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ traits[$(CRATE_ID)]::Test3::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ traits[$(CRATE_ID)]::Test3::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:28 ~ traits[$(CRATE_ID)]::Test3::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:27 ~ traits[$(CRATE_ID)]::Test3::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ traits[$(CRATE_ID)]::Test1::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:11 ~ traits[$(CRATE_ID)]::Test1::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:16 ~ traits[$(CRATE_ID)]::Test1::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:15 ~ traits[$(CRATE_ID)]::Test1::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:26 ~ traits[$(CRATE_ID)]::Test3::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:25 ~ traits[$(CRATE_ID)]::Test3::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:30 ~ traits[$(CRATE_ID)]::Test3::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:29 ~ traits[$(CRATE_ID)]::Test3::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:19 ~ traits[$(CRATE_ID)]::Test2::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ traits[$(CRATE_ID)]::Test2::prusti_pre_item_test1_$(NUM_UUID))]), posts: Inherent([DefId(0:18 ~ traits[$(CRATE_ID)]::Test2::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:22 ~ traits[$(CRATE_ID)]::Test2::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:20 ~ traits[$(CRATE_ID)]::Test2::prusti_pre_item_test2_$(NUM_UUID))]), posts: Inherent([DefId(0:21 ~ traits[$(CRATE_ID)]::Test2::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:33 ~ traits[$(CRATE_ID)]::Test4::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:31 ~ traits[$(CRATE_ID)]::Test4::prusti_pre_item_test1_$(NUM_UUID))]), posts: Inherent([DefId(0:32 ~ traits[$(CRATE_ID)]::Test4::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:36 ~ traits[$(CRATE_ID)]::Test4::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:34 ~ traits[$(CRATE_ID)]::Test4::prusti_pre_item_test2_$(NUM_UUID))]), posts: Inherent([DefId(0:35 ~ traits[$(CRATE_ID)]::Test4::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ traits[$(CRATE_ID)]::Test1::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ traits[$(CRATE_ID)]::Test1::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ traits[$(CRATE_ID)]::Test1::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ traits[$(CRATE_ID)]::Test1::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ traits[$(CRATE_ID)]::Test3::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:23 ~ traits[$(CRATE_ID)]::Test3::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:28 ~ traits[$(CRATE_ID)]::Test3::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:27 ~ traits[$(CRATE_ID)]::Test3::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ traits[$(CRATE_ID)]::Test1::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:11 ~ traits[$(CRATE_ID)]::Test1::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:16 ~ traits[$(CRATE_ID)]::Test1::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:15 ~ traits[$(CRATE_ID)]::Test1::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:26 ~ traits[$(CRATE_ID)]::Test3::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:25 ~ traits[$(CRATE_ID)]::Test3::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:30 ~ traits[$(CRATE_ID)]::Test3::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:29 ~ traits[$(CRATE_ID)]::Test3::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:19 ~ traits[$(CRATE_ID)]::Test2::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ traits[$(CRATE_ID)]::Test2::prusti_pre_item_test1_$(NUM_UUID))]), posts: Inherent([DefId(0:18 ~ traits[$(CRATE_ID)]::Test2::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:22 ~ traits[$(CRATE_ID)]::Test2::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:20 ~ traits[$(CRATE_ID)]::Test2::prusti_pre_item_test2_$(NUM_UUID))]), posts: Inherent([DefId(0:21 ~ traits[$(CRATE_ID)]::Test2::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:33 ~ traits[$(CRATE_ID)]::Test4::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:31 ~ traits[$(CRATE_ID)]::Test4::prusti_pre_item_test1_$(NUM_UUID))]), posts: Inherent([DefId(0:32 ~ traits[$(CRATE_ID)]::Test4::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:36 ~ traits[$(CRATE_ID)]::Test4::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:34 ~ traits[$(CRATE_ID)]::Test4::prusti_pre_item_test2_$(NUM_UUID))]), posts: Inherent([DefId(0:35 ~ traits[$(CRATE_ID)]::Test4::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/true.stdout b/prusti-tests/tests/parse/ui/true.stdout index 7e33a60fdfd..38d8e2be6c2 100644 --- a/prusti-tests/tests/parse/ui/true.stdout +++ b/prusti-tests/tests/parse/ui/true.stdout @@ -94,6 +94,6 @@ fn test4() { fn main() {} Invariant(DefId(0:10 ~ true[$(CRATE_ID)]::test3::{closure#0})) Invariant(DefId(0:14 ~ true[$(CRATE_ID)]::test4::{closure#0})) -ProcedureSpecification { source: DefId(0:7 ~ true[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:6 ~ true[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:9 ~ true[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:8 ~ true[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ true[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ true[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Inherent([DefId(0:12 ~ true[$(CRATE_ID)]::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ true[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:6 ~ true[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:9 ~ true[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:8 ~ true[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ true[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ true[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Inherent([DefId(0:12 ~ true[$(CRATE_ID)]::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/parse/ui/trusted.stdout b/prusti-tests/tests/parse/ui/trusted.stdout index 71129ed2063..ee4abe3abf6 100644 --- a/prusti-tests/tests/parse/ui/trusted.stdout +++ b/prusti-tests/tests/parse/ui/trusted.stdout @@ -32,4 +32,4 @@ impl Test2<> { } fn main() {} TypeSpecification { source: DefId(0:7 ~ trusted[$(CRATE_ID)]::Test2), invariant: Inherent([]), trusted: Inherent(true), model: None, counterexample_print: [] } -ProcedureSpecification { source: DefId(0:5 ~ trusted[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(true), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:5 ~ trusted[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(true), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/typecheck/ui/forall_encode_typeck.stdout b/prusti-tests/tests/typecheck/ui/forall_encode_typeck.stdout index 038dc972238..40aea930ac0 100644 --- a/prusti-tests/tests/typecheck/ui/forall_encode_typeck.stdout +++ b/prusti-tests/tests/typecheck/ui/forall_encode_typeck.stdout @@ -45,5 +45,5 @@ fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { #[prusti::specs_version = $(SPECS_VERSION)] fn test2() {} fn main() {} -ProcedureSpecification { source: DefId(0:10 ~ forall_encode_typeck[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall_encode_typeck[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:16 ~ forall_encode_typeck[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ forall_encode_typeck[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ forall_encode_typeck[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall_encode_typeck[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:16 ~ forall_encode_typeck[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:11 ~ forall_encode_typeck[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/typecheck/ui/forall_triggers.stdout b/prusti-tests/tests/typecheck/ui/forall_triggers.stdout index 6d2e1660992..0acb86afb16 100644 --- a/prusti-tests/tests/typecheck/ui/forall_triggers.stdout +++ b/prusti-tests/tests/typecheck/ui/forall_triggers.stdout @@ -39,7 +39,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { - !!((forall(((#[prusti::spec_only] |a: i32| (a == a && true),),), + !!((forall(((#[prusti::spec_only] |a: i32| ((a == a) && (true)),),), #[prusti::spec_only] |a: i32| -> bool { ((forall((), @@ -71,7 +71,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test4_$(NUM_UUID)() -> bool { - !!((forall(((#[prusti::spec_only] |a: i32| (a == a && true),),), + !!((forall(((#[prusti::spec_only] |a: i32| ((a == a) && (true)),),), #[prusti::spec_only] |a: i32| -> bool { ((forall(((#[prusti::spec_only] |b: i32| (a == b),),), @@ -99,7 +99,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { - !!((exists(((#[prusti::spec_only] |a: i32| (a == a && true),),), + !!((exists(((#[prusti::spec_only] |a: i32| ((a == a) && (true)),),), #[prusti::spec_only] |a: i32| -> bool { ((exists((), @@ -131,7 +131,7 @@ non_snake_case)] #[prusti::spec_only] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_test8_$(NUM_UUID)() -> bool { - !!((exists(((#[prusti::spec_only] |a: i32| (a == a && true),),), + !!((exists(((#[prusti::spec_only] |a: i32| ((a == a) && (true)),),), #[prusti::spec_only] |a: i32| -> bool { ((exists(((#[prusti::spec_only] |b: i32| (a == b),),), @@ -143,11 +143,11 @@ fn prusti_pre_item_test8_$(NUM_UUID)() -> bool { #[prusti::specs_version = $(SPECS_VERSION)] fn test8() {} fn main() {} -ProcedureSpecification { source: DefId(0:8 ~ forall_triggers[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ forall_triggers[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:19 ~ forall_triggers[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:25 ~ forall_triggers[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:20 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:29 ~ forall_triggers[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:34 ~ forall_triggers[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:30 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:40 ~ forall_triggers[$(CRATE_ID)]::test7), kind: Inherent(Impure), pres: Inherent([DefId(0:35 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test7_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:46 ~ forall_triggers[$(CRATE_ID)]::test8), kind: Inherent(Impure), pres: Inherent([DefId(0:41 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test8_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ forall_triggers[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ forall_triggers[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:19 ~ forall_triggers[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:25 ~ forall_triggers[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:20 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:29 ~ forall_triggers[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:34 ~ forall_triggers[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:30 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:40 ~ forall_triggers[$(CRATE_ID)]::test7), kind: Inherent(Impure), pres: Inherent([DefId(0:35 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test7_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:46 ~ forall_triggers[$(CRATE_ID)]::test8), kind: Inherent(Impure), pres: Inherent([DefId(0:41 ~ forall_triggers[$(CRATE_ID)]::prusti_pre_item_test8_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/typecheck/ui/nested_forall.stdout b/prusti-tests/tests/typecheck/ui/nested_forall.stdout index 3c15cade16c..4e65b8f4798 100644 --- a/prusti-tests/tests/typecheck/ui/nested_forall.stdout +++ b/prusti-tests/tests/typecheck/ui/nested_forall.stdout @@ -47,7 +47,7 @@ fn prusti_pre_item_test2_$(NUM_UUID)() -> bool { { ((forall((), #[prusti::spec_only] |b: i32| -> bool - { (((!(a == a) || (b == b))): bool) })): bool) + { ((!(a == a) || (b == b)): bool) })): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] @@ -66,7 +66,7 @@ fn prusti_pre_item_test3_$(NUM_UUID)() -> bool { { ((forall((), #[prusti::spec_only] |c: i32| -> bool - { ((a == a && b == b): bool) })): bool) + { (((a == a) && (b == b)): bool) })): bool) })): bool) })): bool) } @@ -99,7 +99,7 @@ fn prusti_pre_item_test5_$(NUM_UUID)() -> bool { { ((exists((), #[prusti::spec_only] |b: i32| -> bool - { (((!(a == a) || (b == b))): bool) })): bool) + { ((!(a == a) || (b == b)): bool) })): bool) })): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] @@ -118,7 +118,7 @@ fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { { ((exists((), #[prusti::spec_only] |c: i32| -> bool - { ((a == a && b == b): bool) })): bool) + { (((a == a) && (b == b)): bool) })): bool) })): bool) })): bool) } @@ -126,9 +126,9 @@ fn prusti_pre_item_test6_$(NUM_UUID)() -> bool { #[prusti::specs_version = $(SPECS_VERSION)] fn test6() {} fn main() {} -ProcedureSpecification { source: DefId(0:8 ~ nested_forall[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:12 ~ nested_forall[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:17 ~ nested_forall[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:21 ~ nested_forall[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:18 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:25 ~ nested_forall[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:22 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:30 ~ nested_forall[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ nested_forall[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Inherent([DefId(0:5 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:12 ~ nested_forall[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Inherent([DefId(0:9 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:17 ~ nested_forall[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Inherent([DefId(0:13 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test3_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:21 ~ nested_forall[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Inherent([DefId(0:18 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test4_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:25 ~ nested_forall[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Inherent([DefId(0:22 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test5_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:30 ~ nested_forall[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Inherent([DefId(0:26 ~ nested_forall[$(CRATE_ID)]::prusti_pre_item_test6_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/fail/type-cond-specs/purity.rs b/prusti-tests/tests/verify/fail/type-cond-specs/purity.rs new file mode 100644 index 00000000000..3243593734c --- /dev/null +++ b/prusti-tests/tests/verify/fail/type-cond-specs/purity.rs @@ -0,0 +1,14 @@ +use prusti_contracts::*; + +#[refine_spec(where T: Copy, [pure])] +#[trusted] +fn test(_t: T) -> bool { + true +} + +#[derive(PartialEq, Eq)] +struct Copyrighted; // not Copy + +fn main() { + prusti_assert!(test(Copyrighted) == test(Copyrighted)); //~ ERROR: [Prusti: invalid specification] use of impure function "test" in pure code is not allowed +} diff --git a/prusti-tests/tests/verify/fail/ghost-constraints/traits-1.rs b/prusti-tests/tests/verify/fail/type-cond-specs/traits-1.rs similarity index 73% rename from prusti-tests/tests/verify/fail/ghost-constraints/traits-1.rs rename to prusti-tests/tests/verify/fail/type-cond-specs/traits-1.rs index 8867f846448..2825640927a 100644 --- a/prusti-tests/tests/verify/fail/ghost-constraints/traits-1.rs +++ b/prusti-tests/tests/verify/fail/type-cond-specs/traits-1.rs @@ -1,11 +1,9 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} trait MyTrait { - #[ghost_constraint(Self: A, [ensures(result > 0)])] + #[refine_spec(where Self: A, [ensures(result > 0)])] #[trusted] fn foo(&self) -> i32; } @@ -21,4 +19,4 @@ impl MyTrait for MyStruct { fn main() { let s = MyStruct; assert!(s.foo() > 0); //~ ERROR: the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify/pass/extern-spec/module-arg.rs b/prusti-tests/tests/verify/pass/extern-spec/module-arg.rs new file mode 100644 index 00000000000..ed32fa1f210 --- /dev/null +++ b/prusti-tests/tests/verify/pass/extern-spec/module-arg.rs @@ -0,0 +1,78 @@ +use prusti_contracts::*; + +fn main() { + use module::inner::*; + + fn _trait_test_1() { + assert!(T::example() == 42) + } + + fn _trait_test_2, U: Copy>() { + prusti_assert!(T::example() === T::example()) + } + + assert!(free_1() == 1); + assert!(free_2() == 2); + + let s = Struct; + assert!(s.method() == 42); +} + +#[extern_spec(module::inner)] +trait Example { + #[ensures(result == 42)] + fn example() -> i32; +} + +#[extern_spec(module::inner)] +trait Advanced +where + T: Copy, +{ + #[pure] + fn example() -> T; +} + +#[extern_spec(module)] +mod inner { + #[ensures(result == 1)] + fn free_1() -> i32; +} + +#[extern_spec(module::inner)] +#[ensures(result == 2)] +fn free_2() -> i32; + +#[extern_spec] +impl module::inner::Struct { + #[ensures(result == 42)] + fn method(&self) -> i32; +} + +mod module { + pub mod inner { + pub trait Example { + fn example() -> i32; + } + + pub trait Advanced { + fn example() -> T; + } + + pub fn free_1() -> i32 { + 1 + } + + pub fn free_2() -> i32 { + 2 + } + + pub struct Struct; + + impl Struct { + pub fn method(&self) -> i32 { + 42 + } + } + } +} diff --git a/prusti-tests/tests/verify/pass/extern-spec/trait-impl-generics.rs b/prusti-tests/tests/verify/pass/extern-spec/trait-impl-generics.rs new file mode 100644 index 00000000000..94c510345fc --- /dev/null +++ b/prusti-tests/tests/verify/pass/extern-spec/trait-impl-generics.rs @@ -0,0 +1,50 @@ +use prusti_contracts::*; + +fn main() { + let value = 42; + assert!(value.combine(5) == 47); + assert!(value.combine(true) == 42); + assert!(value.combine(false) == 0); + let t = true; + assert!(t.combine(value)); +} + +trait Combine { + fn combine(&self, other: T) -> Self; +} + +impl Combine for i32 { + fn combine(&self, other: i32) -> Self { + self + other + } +} + +impl Combine for i32 { + fn combine(&self, other: bool) -> Self { + if other { *self } else { 0 } + } +} + +impl Combine for bool { + fn combine(&self, _other: T) -> Self { + *self + } +} + +#[extern_spec] +impl Combine for i32 { + #[ensures(result == *self + other)] + fn combine(&self, other: i32) -> Self; +} + +#[extern_spec] +impl Combine for i32 { + #[ensures(result == if other { *self } else { 0 })] + fn combine(&self, other: bool) -> Self; +} + +#[extern_spec] +impl Combine for bool { + #[ensures(result == *self)] + fn combine(&self, _other: T) -> Self; +} diff --git a/prusti-tests/tests/verify/pass/trait-resolution/references.rs b/prusti-tests/tests/verify/pass/trait-resolution/references.rs new file mode 100644 index 00000000000..d9b6c06d4e7 --- /dev/null +++ b/prusti-tests/tests/verify/pass/trait-resolution/references.rs @@ -0,0 +1,37 @@ +use prusti_contracts::*; + +trait Trait { + fn foo(value: T) -> i32 { + 1 + } +} + +struct Example; + +#[refine_trait_spec] +impl Trait<&i32> for Example { + #[ensures(result == *value)] + fn foo(value: &i32) -> i32 { + *value + } +} + +struct PureExample; + +#[refine_trait_spec] +impl Trait<&i32> for PureExample { + #[pure] + #[ensures(result == *value)] + fn foo(value: &i32) -> i32 { + *value + } +} + +fn main() { + let x = 5; + let y = 6; + assert!(Example::foo(&x) == x); + assert!(Example::foo(&y) == y); + assert!(PureExample::foo(&x) == x); + prusti_assert!(PureExample::foo(&x) == x); +} diff --git a/prusti-tests/tests/verify/pass/ghost-constraints/extern-spec/traits-1.rs b/prusti-tests/tests/verify/pass/type-cond-specs/extern-spec/traits-1.rs similarity index 88% rename from prusti-tests/tests/verify/pass/ghost-constraints/extern-spec/traits-1.rs rename to prusti-tests/tests/verify/pass/type-cond-specs/extern-spec/traits-1.rs index 04169626183..a23cc354a71 100644 --- a/prusti-tests/tests/verify/pass/ghost-constraints/extern-spec/traits-1.rs +++ b/prusti-tests/tests/verify/pass/type-cond-specs/extern-spec/traits-1.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait HasContract { @@ -15,7 +13,7 @@ trait MyTrait { #[extern_spec] trait MyTrait { - #[ghost_constraint(Self: HasContract, [ + #[refine_spec(where Self: HasContract, [ requires(self.pre()), ensures(self.post()) ])] @@ -49,4 +47,4 @@ fn main() { let mut s = MyStruct { x: 10 }; s.foo(); assert!(s.x >= 20); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify/pass/type-cond-specs/purity.rs b/prusti-tests/tests/verify/pass/type-cond-specs/purity.rs new file mode 100644 index 00000000000..02d9831d636 --- /dev/null +++ b/prusti-tests/tests/verify/pass/type-cond-specs/purity.rs @@ -0,0 +1,11 @@ +use prusti_contracts::*; + +#[refine_spec(where T: Copy, [pure])] +#[trusted] +fn test(_t: T) -> bool { + true +} + +fn main() { + assert!(test(5) == test(5)); +} diff --git a/prusti-tests/tests/verify/ui/calls.stdout b/prusti-tests/tests/verify/ui/calls.stdout index 0023158dc07..6b3c7be3f31 100644 --- a/prusti-tests/tests/verify/ui/calls.stdout +++ b/prusti-tests/tests/verify/ui/calls.stdout @@ -30,7 +30,7 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_post_item_max_$(NUM_UUID)(a: i32, b: i32, result: i32) -> bool { - !!((result == a || result == b): bool) + !!(((result == a) || (result == b)): bool) } #[allow(unused_must_use, unused_parens, unused_variables, dead_code, non_snake_case)] @@ -38,7 +38,7 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_post_item_max_$(NUM_UUID)(a: i32, b: i32, result: i32) -> bool { - !!((result >= a && result >= b): bool) + !!(((result >= a) && (result >= b)): bool) } #[prusti::post_spec_id_ref = "$(NUM_UUID)"] #[prusti::post_spec_id_ref = "$(NUM_UUID)"] @@ -62,11 +62,12 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_post_item_test_max3_$(NUM_UUID)(result: i32) -> bool { - !!((true && ((!(true) || (result == 3))) && (true || false)): bool) + !!((((true) && ((!(true) || (result == 3)))) && (((true) || (false)))): + bool) } #[prusti::post_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] fn test_max3() -> i32 { let a = 4; let b = 3; max(a, b) } fn main() {} -ProcedureSpecification { source: DefId(0:11 ~ calls[$(CRATE_ID)]::test_max3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:10 ~ calls[$(CRATE_ID)]::prusti_post_item_test_max3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:9 ~ calls[$(CRATE_ID)]::max), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:7 ~ calls[$(CRATE_ID)]::prusti_post_item_max_$(NUM_UUID)), DefId(0:8 ~ calls[$(CRATE_ID)]::prusti_post_item_max_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:11 ~ calls[$(CRATE_ID)]::test_max3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:10 ~ calls[$(CRATE_ID)]::prusti_post_item_test_max3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:9 ~ calls[$(CRATE_ID)]::max), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:7 ~ calls[$(CRATE_ID)]::prusti_post_item_max_$(NUM_UUID)), DefId(0:8 ~ calls[$(CRATE_ID)]::prusti_post_item_max_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/failing-postcondition.rs b/prusti-tests/tests/verify/ui/failing-postcondition.rs index 866eb1d5ae4..3eeff1ecce4 100644 --- a/prusti-tests/tests/verify/ui/failing-postcondition.rs +++ b/prusti-tests/tests/verify/ui/failing-postcondition.rs @@ -10,10 +10,17 @@ fn client(a: u32) {} #[pure] #[ensures(result)] -fn test1() -> bool { false } +fn test1() -> bool { + false +} #[pure] #[ensures(x)] -fn test2(x: bool) -> bool { x } +fn test2(x: bool) -> bool { + x +} + +#[ensures(a === b)] +fn test3(a: T, b: T) {} fn main() {} diff --git a/prusti-tests/tests/verify/ui/failing-postcondition.stderr b/prusti-tests/tests/verify/ui/failing-postcondition.stderr index a250b4a4545..54424aa4380 100644 --- a/prusti-tests/tests/verify/ui/failing-postcondition.stderr +++ b/prusti-tests/tests/verify/ui/failing-postcondition.stderr @@ -19,20 +19,36 @@ error: [Prusti: verification error] postcondition might not hold. note: the error originates here --> $DIR/failing-postcondition.rs:13:1 | -13 | fn test1() -> bool { false } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +13 | / fn test1() -> bool { +14 | | false +15 | | } + | |_^ error: [Prusti: verification error] postcondition might not hold. - --> $DIR/failing-postcondition.rs:16:11 + --> $DIR/failing-postcondition.rs:18:11 | -16 | #[ensures(x)] +18 | #[ensures(x)] | ^ | note: the error originates here - --> $DIR/failing-postcondition.rs:17:1 + --> $DIR/failing-postcondition.rs:19:1 | -17 | fn test2(x: bool) -> bool { x } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +19 | / fn test2(x: bool) -> bool { +20 | | x +21 | | } + | |_^ -error: aborting due to 3 previous errors +error: [Prusti: verification error] postcondition might not hold. + --> $DIR/failing-postcondition.rs:23:11 + | +23 | #[ensures(a === b)] + | ^^^^^^^ + | +note: the error originates here + --> $DIR/failing-postcondition.rs:24:1 + | +24 | fn test3(a: T, b: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/prusti-tests/tests/verify/ui/false.stdout b/prusti-tests/tests/verify/ui/false.stdout index 0384e0b3e6d..85324948ecc 100644 --- a/prusti-tests/tests/verify/ui/false.stdout +++ b/prusti-tests/tests/verify/ui/false.stdout @@ -31,4 +31,4 @@ fn test2() { if !false { ::core::panicking::panic("assertion failed: false") }; } fn main() {} -ProcedureSpecification { source: DefId(0:7 ~ false[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ false[$(CRATE_ID)]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:7 ~ false[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ false[$(CRATE_ID)]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/forall_verify.stdout b/prusti-tests/tests/verify/ui/forall_verify.stdout index c96ade87879..323e483fb27 100644 --- a/prusti-tests/tests/verify/ui/forall_verify.stdout +++ b/prusti-tests/tests/verify/ui/forall_verify.stdout @@ -114,10 +114,10 @@ fn prusti_post_item_test6_$(NUM_UUID)(result: ()) #[prusti::specs_version = $(SPECS_VERSION)] fn test6() {} fn main() {} -ProcedureSpecification { source: DefId(0:5 ~ forall_verify[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ forall_verify[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:11 ~ forall_verify[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:9 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ forall_verify[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:12 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:17 ~ forall_verify[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:15 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ forall_verify[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:22 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test6_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:21 ~ forall_verify[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:18 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test5_$(NUM_UUID)), DefId(0:19 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test5_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:5 ~ forall_verify[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ forall_verify[$(CRATE_ID)]::test1), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:6 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test1_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:11 ~ forall_verify[$(CRATE_ID)]::test2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:9 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ forall_verify[$(CRATE_ID)]::test3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:12 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:17 ~ forall_verify[$(CRATE_ID)]::test4), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:15 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ forall_verify[$(CRATE_ID)]::test6), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:22 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test6_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:21 ~ forall_verify[$(CRATE_ID)]::test5), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:18 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test5_$(NUM_UUID)), DefId(0:19 ~ forall_verify[$(CRATE_ID)]::prusti_post_item_test5_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/pledges.stdout b/prusti-tests/tests/verify/ui/pledges.stdout index bf3f38c770a..b8567ce292a 100644 --- a/prusti-tests/tests/verify/ui/pledges.stdout +++ b/prusti-tests/tests/verify/ui/pledges.stdout @@ -66,4 +66,4 @@ fn reborrow_caller(a: T) { if !(a.f == 5) { ::core::panicking::panic("assertion failed: a.f == 5") }; } fn main() {} -ProcedureSpecification { source: DefId(0:14 ~ pledges[$(CRATE_ID)]::reborrow), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:12 ~ pledges[$(CRATE_ID)]::prusti_pledge_item_reborrow_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ pledges[$(CRATE_ID)]::reborrow), kind: Inherent(Impure), pres: Empty, posts: Empty, pledges: Inherent([Pledge { reference: None, lhs: None, rhs: DefId(0:12 ~ pledges[$(CRATE_ID)]::prusti_pledge_item_reborrow_$(NUM_UUID)) }]), trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/predicate.stdout b/prusti-tests/tests/verify/ui/predicate.stdout index 49489765fa3..d29771497a1 100644 --- a/prusti-tests/tests/verify/ui/predicate.stdout +++ b/prusti-tests/tests/verify/ui/predicate.stdout @@ -159,7 +159,7 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_pre_item_precond_or_correctly_$(NUM_UUID)() -> bool { - !!((false_p() || true): bool) + !!(((false_p()) || (true)): bool) } #[prusti::pre_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -172,12 +172,12 @@ fn main() { test_identity_2(); precond_or_correctly(); } -ProcedureSpecification { source: DefId(0:5 ~ predicate[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ predicate[$(CRATE_ID)]::true_p1), kind: Inherent(Predicate(Some(DefId(0:6 ~ predicate[$(CRATE_ID)]::prusti_pred_item_true_p1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:11 ~ predicate[$(CRATE_ID)]::true_p2), kind: Inherent(Predicate(Some(DefId(0:9 ~ predicate[$(CRATE_ID)]::prusti_pred_item_true_p2_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:26 ~ predicate[$(CRATE_ID)]::false_p), kind: Inherent(Predicate(Some(DefId(0:25 ~ predicate[$(CRATE_ID)]::prusti_pred_item_false_p_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:14 ~ predicate[$(CRATE_ID)]::forall_identity), kind: Inherent(Predicate(Some(DefId(0:12 ~ predicate[$(CRATE_ID)]::prusti_pred_item_forall_identity_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:18 ~ predicate[$(CRATE_ID)]::exists_identity), kind: Inherent(Predicate(Some(DefId(0:15 ~ predicate[$(CRATE_ID)]::prusti_pred_item_exists_identity_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:28 ~ predicate[$(CRATE_ID)]::precond_or_correctly), kind: Inherent(Impure), pres: Inherent([DefId(0:27 ~ predicate[$(CRATE_ID)]::prusti_pre_item_precond_or_correctly_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:21 ~ predicate[$(CRATE_ID)]::test_identity_1), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_1_$(NUM_UUID)), DefId(0:20 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:24 ~ predicate[$(CRATE_ID)]::test_identity_2), kind: Inherent(Impure), pres: Inherent([DefId(0:22 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_2_$(NUM_UUID)), DefId(0:23 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:5 ~ predicate[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ predicate[$(CRATE_ID)]::true_p1), kind: Inherent(Predicate(Some(DefId(0:6 ~ predicate[$(CRATE_ID)]::prusti_pred_item_true_p1_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:11 ~ predicate[$(CRATE_ID)]::true_p2), kind: Inherent(Predicate(Some(DefId(0:9 ~ predicate[$(CRATE_ID)]::prusti_pred_item_true_p2_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:26 ~ predicate[$(CRATE_ID)]::false_p), kind: Inherent(Predicate(Some(DefId(0:25 ~ predicate[$(CRATE_ID)]::prusti_pred_item_false_p_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:14 ~ predicate[$(CRATE_ID)]::forall_identity), kind: Inherent(Predicate(Some(DefId(0:12 ~ predicate[$(CRATE_ID)]::prusti_pred_item_forall_identity_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:18 ~ predicate[$(CRATE_ID)]::exists_identity), kind: Inherent(Predicate(Some(DefId(0:15 ~ predicate[$(CRATE_ID)]::prusti_pred_item_exists_identity_$(NUM_UUID))))), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:28 ~ predicate[$(CRATE_ID)]::precond_or_correctly), kind: Inherent(Impure), pres: Inherent([DefId(0:27 ~ predicate[$(CRATE_ID)]::prusti_pre_item_precond_or_correctly_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:21 ~ predicate[$(CRATE_ID)]::test_identity_1), kind: Inherent(Impure), pres: Inherent([DefId(0:19 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_1_$(NUM_UUID)), DefId(0:20 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_1_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:24 ~ predicate[$(CRATE_ID)]::test_identity_2), kind: Inherent(Impure), pres: Inherent([DefId(0:22 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_2_$(NUM_UUID)), DefId(0:23 ~ predicate[$(CRATE_ID)]::prusti_pre_item_test_identity_2_$(NUM_UUID))]), posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/predicates/abstract-predicate-with-body.stderr b/prusti-tests/tests/verify/ui/predicates/abstract-predicate-with-body.stderr deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/prusti-tests/tests/verify/ui/pure.stdout b/prusti-tests/tests/verify/ui/pure.stdout index 7c6a71bfc78..9a3c5be52ce 100644 --- a/prusti-tests/tests/verify/ui/pure.stdout +++ b/prusti-tests/tests/verify/ui/pure.stdout @@ -69,7 +69,8 @@ non_snake_case)] #[prusti::spec_id = "$(NUM_UUID)"] fn prusti_post_item_test_max3_$(NUM_UUID)(result: i32) -> bool { - !!((true && ((!(true) || (result == 3))) && (true || false)): bool) + !!((((true) && ((!(true) || (result == 3)))) && (((true) || (false)))): + bool) } #[prusti::post_spec_id_ref = "$(NUM_UUID)"] #[prusti::specs_version = $(SPECS_VERSION)] @@ -115,9 +116,9 @@ fn prusti_post_item_test_max5_$(NUM_UUID)(a: i32, b: i32, #[prusti::specs_version = $(SPECS_VERSION)] fn test_max5(a: i32, b: i32) -> i32 { a } fn main() {} -ProcedureSpecification { source: DefId(0:11 ~ pure[$(CRATE_ID)]::max), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:8 ~ pure[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:13 ~ pure[$(CRATE_ID)]::test_max3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:12 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:10 ~ pure[$(CRATE_ID)]::test_identity2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:9 ~ pure[$(CRATE_ID)]::prusti_post_item_test_identity2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:16 ~ pure[$(CRATE_ID)]::test_max4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ pure[$(CRATE_ID)]::prusti_pre_item_test_max4_$(NUM_UUID))]), posts: Inherent([DefId(0:15 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } -ProcedureSpecification { source: DefId(0:19 ~ pure[$(CRATE_ID)]::test_max5), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ pure[$(CRATE_ID)]::prusti_pre_item_test_max5_$(NUM_UUID))]), posts: Inherent([DefId(0:18 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max5_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None) } +ProcedureSpecification { source: DefId(0:11 ~ pure[$(CRATE_ID)]::max), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:8 ~ pure[$(CRATE_ID)]::identity), kind: Inherent(Pure), pres: Empty, posts: Empty, pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:13 ~ pure[$(CRATE_ID)]::test_max3), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:12 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max3_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:10 ~ pure[$(CRATE_ID)]::test_identity2), kind: Inherent(Impure), pres: Empty, posts: Inherent([DefId(0:9 ~ pure[$(CRATE_ID)]::prusti_post_item_test_identity2_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:16 ~ pure[$(CRATE_ID)]::test_max4), kind: Inherent(Impure), pres: Inherent([DefId(0:14 ~ pure[$(CRATE_ID)]::prusti_pre_item_test_max4_$(NUM_UUID))]), posts: Inherent([DefId(0:15 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max4_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } +ProcedureSpecification { source: DefId(0:19 ~ pure[$(CRATE_ID)]::test_max5), kind: Inherent(Impure), pres: Inherent([DefId(0:17 ~ pure[$(CRATE_ID)]::prusti_pre_item_test_max5_$(NUM_UUID))]), posts: Inherent([DefId(0:18 ~ pure[$(CRATE_ID)]::prusti_post_item_test_max5_$(NUM_UUID))]), pledges: Empty, trusted: Inherent(false), terminates: Inherent(None), purity: Inherent(None) } diff --git a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.rs b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.rs similarity index 86% rename from prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.rs rename to prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.rs index 99e6621988d..af9c3a8df5e 100644 --- a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.rs +++ b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait HasContract { @@ -15,7 +13,7 @@ trait MyTrait { #[extern_spec] trait MyTrait { - #[ghost_constraint(Self: HasContract, [ + #[refine_spec(where Self: HasContract, [ requires(self.pre()), ensures(self.post()) ])] fn foo(&mut self); @@ -46,4 +44,4 @@ impl HasContract for MyStruct { } fn main() { -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.stderr b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.stderr similarity index 58% rename from prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.stderr rename to prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.stderr index 788c061004b..d0770a95a9f 100644 --- a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-1.stderr +++ b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-1.stderr @@ -1,12 +1,12 @@ error: [Prusti: verification error] the method's precondition may not be a valid weakening of the trait's precondition. - --> $DIR/invalid-trait-refinement-1.rs:18:30 + --> $DIR/invalid-trait-refinement-1.rs:16:31 | -18 | #[ghost_constraint(Self: HasContract, [ - | ______________________________^ -19 | | requires(self.pre()), ensures(self.post()) +16 | #[refine_spec(where Self: HasContract, [ + | _______________________________^ +17 | | requires(self.pre()), ensures(self.post()) | |___________________________^ ... -30 | #[requires(self.x >= 15)] +28 | #[requires(self.x >= 15)] | ^^^^^^^^^^^^ | = help: The trait's precondition should imply the implemented method's precondition. diff --git a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.rs b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.rs similarity index 80% rename from prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.rs rename to prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.rs index b8489c8f18f..87599686b30 100644 --- a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.rs +++ b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait HasContract { @@ -15,8 +13,8 @@ trait MyTrait { #[extern_spec] trait MyTrait { - #[ghost_constraint(Self: HasContract, [ - requires(self.pre()), ensures(self.post()) + #[refine_spec(where Self: HasContract, [ + requires(self.pre()), ensures(self.post()) ])] fn foo(&mut self); } @@ -46,4 +44,4 @@ impl HasContract for MyStruct { } fn main() { -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.stderr b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.stderr similarity index 51% rename from prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.stderr rename to prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.stderr index 3df59fbe094..7218d1af1ad 100644 --- a/prusti-tests/tests/verify/ui/ghost-constraints/invalid-trait-refinement-2.stderr +++ b/prusti-tests/tests/verify/ui/type-cond-specs/invalid-trait-refinement-2.stderr @@ -1,12 +1,12 @@ error: [Prusti: verification error] the method's postcondition may not be a valid strengthening of the trait's postcondition. - --> $DIR/invalid-trait-refinement-2.rs:18:30 + --> $DIR/invalid-trait-refinement-2.rs:16:31 | -18 | #[ghost_constraint(Self: HasContract, [ - | ______________________________^ -19 | | requires(self.pre()), ensures(self.post()) - | |_____________________________________________^ +16 | #[refine_spec(where Self: HasContract, [ + | _______________________________^ +17 | | requires(self.pre()), ensures(self.post()) + | |_________________________________________________^ ... -30 | #[ensures(self.x >= 15)] +28 | #[ensures(self.x >= 15)] | ^^^^^^^^^^^^ | = help: The implemented method's postcondition should imply the trait's postcondition. diff --git a/prusti-tests/tests/verify_overflow/fail/extern-spec/traits/generics-1.rs b/prusti-tests/tests/verify_overflow/fail/extern-spec/traits/generics-1.rs deleted file mode 100644 index 66acae2c5e1..00000000000 --- a/prusti-tests/tests/verify_overflow/fail/extern-spec/traits/generics-1.rs +++ /dev/null @@ -1,37 +0,0 @@ -// ignore-test #[concrete] currently not supported - -use prusti_contracts::*; - -trait MyTrait { - fn get_value(&self) -> T; -} - -#[extern_spec] -trait MyTrait<#[concrete] i32> { - #[ensures(result == 42)] - fn get_value(&self) -> i32; -} - -#[extern_spec] -trait MyTrait<#[concrete] u32> { - #[ensures(result == 43)] //~ ERROR: duplicate specification for MyTrait::get_value - fn get_value(&self) -> u32; -} - -struct Impl; -impl MyTrait for Impl { - fn get_value(&self) -> i32 { - 42 - } -} -impl MyTrait for Impl { - fn get_value(&self) -> u32 { - 43 - } -} - -fn main() { - let s = Impl {}; - assert!(MyTrait::::get_value(&s) == 42); - assert!(MyTrait::::get_value(&s) == 43); -} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-non-trusted-function.rs b/prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-non-trusted-function.rs deleted file mode 100644 index e81fdc1549e..00000000000 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-non-trusted-function.rs +++ /dev/null @@ -1,13 +0,0 @@ -// compile-flags: -Penable_ghost_constraints=true - -use prusti_contracts::*; - -trait A {} - -#[ghost_constraint(T: A, [ -ensures(true) -])] -fn foo() {} //~ ERROR: Ghost constraints can only be used on trusted functions - -fn main() { -} \ No newline at end of file diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-1.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-1.rs similarity index 71% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-1.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-1.rs index c42497d77d6..26a225ae10e 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-1.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-1.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -11,8 +10,8 @@ impl A for Foo { } #[trusted] -#[ghost_constraint(T: A , [ -ensures(result > 0) +#[refine_spec(where T: A, [ + ensures(result > 0) ])] fn foo(x: T) -> i32 { 42 @@ -23,4 +22,4 @@ fn main() { let f = Foo; let res = foo(f); assert!(res > 0); //~ ERROR: the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-2.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-2.rs similarity index 76% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-2.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-2.rs index 785a5c94e61..3582791a9b1 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-2.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-2.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -24,8 +23,8 @@ impl SomeTrait for Foo { #[extern_spec] trait SomeTrait { - #[ghost_constraint(Self: A::AssocType> , [ - ensures(result > 0) + #[refine_spec(where Self: A::AssocType>, [ + ensures(result > 0) ])] fn foo(&self) -> i32; } @@ -34,4 +33,4 @@ fn main() { let f = Foo; let res = f.foo(); assert!(res > 0); //~ ERROR: [Prusti: verification error] the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-3.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-3.rs similarity index 78% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-3.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-3.rs index c95f86d42c4..5e8fa08ae9e 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-3.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-3.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -26,8 +25,8 @@ impl SomeTrait for Foo { #[extern_spec] trait SomeTrait { - #[ghost_constraint(Self: A::AssocType> , [ - ensures(result > 0) + #[refine_spec(where Self: A::AssocType>, [ + ensures(result > 0) ])] fn foo(&self) -> i32; } @@ -38,4 +37,4 @@ fn main() { }; let res = f.foo(); assert!(res > 0); //~ ERROR: [Prusti: verification error] the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-4.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-4.rs similarity index 73% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-4.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-4.rs index 8b742478a66..cc3c9317e79 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-4.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-4.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -23,9 +22,9 @@ impl SomeTrait for Foo { } #[extern_spec] -trait SomeTrait<#[generic] T> { - #[ghost_constraint(Self: A>::AssocType> , [ - ensures(result > 0) +trait SomeTrait { + #[refine_spec(where Self: A>::AssocType>, [ + ensures(result > 0) ])] fn foo(&self, x: T) -> i32; } @@ -34,4 +33,4 @@ fn main() { let f = Foo; let res = f.foo(42 as u32); assert!(res > 0); //~ ERROR: [Prusti: verification error] the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-5.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-5.rs similarity index 72% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-5.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-5.rs index 877741e5244..37976542539 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-5.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-5.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -11,8 +10,8 @@ impl A for Foo { } #[trusted] -#[ghost_constraint(T: A , [ -ensures(result > 0) +#[refine_spec(where T: A, [ + ensures(result > 0) ])] fn foo(x: T) -> i32 { 42 @@ -22,4 +21,4 @@ fn main() { let f = Foo; let res = foo(f); assert!(res > 0); //~ ERROR: [Prusti: verification error] the asserted expression might not hold -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-6.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-6.rs similarity index 88% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-6.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-6.rs index afd0aa47ccd..1bde1157949 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-6.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-6.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -22,7 +21,7 @@ impl A for FooNoMatch2 { #[trusted] -#[ghost_constraint(T: A , [ +#[refine_spec(where T: A, [ ensures(result > 0) ])] fn foo(x: T) -> i32 { @@ -45,4 +44,4 @@ fn verify_no_match_2() { } fn main() { -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-7.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-7.rs similarity index 76% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-7.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-7.rs index c9eb6cbc754..81d440ae13a 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/associated-types-7.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/associated-types-7.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -16,7 +15,9 @@ impl A for FooMatch { } impl SomeTrait for FooMatch { type AssocType = i32; - fn foo(&self) -> i32 { 42 } + fn foo(&self) -> i32 { + 42 + } } struct FooNoMatch1; @@ -25,7 +26,9 @@ impl A for FooNoMatch1 { } impl SomeTrait for FooNoMatch1 { type AssocType = i32; - fn foo(&self) -> i32 { 42 } + fn foo(&self) -> i32 { + 42 + } } struct FooNoMatch2; @@ -34,13 +37,15 @@ impl A for FooNoMatch2 { } impl SomeTrait for FooNoMatch2 { type AssocType = i16; - fn foo(&self) -> i32 { 42 } + fn foo(&self) -> i32 { + 42 + } } #[extern_spec] -trait SomeTrait<#[generic] X, #[generic] Y> { - #[ghost_constraint(Self: A>::AssocType> , [ - ensures(result > 0) +trait SomeTrait { + #[refine_spec(where Self: A>::AssocType>, [ + ensures(result > 0) ])] fn foo(&self) -> i32; } @@ -60,5 +65,4 @@ fn verify_no_match_2() { assert!(f.foo() > 0); //~ ERROR: [Prusti: verification error] the asserted expression might not hold } -fn main() { -} \ No newline at end of file +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/different-bounds.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/different-bounds.rs similarity index 50% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/different-bounds.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/different-bounds.rs index 4be7038043e..76ad0c87e08 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/different-bounds.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/different-bounds.rs @@ -1,6 +1,4 @@ -// compile-flags: -Penable_ghost_constraints=true - -// Note: For now, it is completely fine to _declare_ two ghost constraints with different bounds +// Note: For now, it is completely fine to _declare_ two type-conditional spec refinements with different bounds // since resolving happens for specific callsites. That is, without the call in main, this // file verifies. use prusti_contracts::*; @@ -9,11 +7,11 @@ trait A {} trait B {} #[trusted] -#[ghost_constraint(T: A, [ //~ ERROR: [Prusti: unsupported feature] Multiple ghost constraints with different bounds defined +#[refine_spec(where T: A, [ //~ ERROR: [Prusti: unsupported feature] Multiple type-conditional spec refinements with different bounds defined requires(true), ensures(true), ])] -#[ghost_constraint(T: B, [ +#[refine_spec(where T: B, [ requires(true), ensures(true), ])] diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-local-refinement-disallowed.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-local-refinement-disallowed.rs similarity index 59% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-local-refinement-disallowed.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-local-refinement-disallowed.rs index 4bbad810115..ae8c48de354 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/in-local-refinement-disallowed.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-local-refinement-disallowed.rs @@ -1,13 +1,11 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} trait MyTrait { #[ensures(result > 0)] - #[ghost_constraint(Self: A, [ - ensures(result % 2 == 0) + #[refine_spec(where Self: A, [ + ensures(result % 2 == 0) ])] fn foo(&self) -> i32; } @@ -17,8 +15,8 @@ struct MyStruct; impl MyTrait for MyStruct { #[ensures(result > 10)] - #[ghost_constraint(Self: A, [ //~ ERROR: Ghost constraints in trait spec refinements not supported - ensures(result % 6 == 0) + #[refine_spec(where Self: A, [ //~ ERROR: Type-conditional spec refinements in trait spec refinements not supported + ensures(result % 6 == 0) ])] fn foo(&self) -> i32 { 42 diff --git a/prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-non-trusted-function.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-non-trusted-function.rs new file mode 100644 index 00000000000..88d3df95e32 --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/in-non-trusted-function.rs @@ -0,0 +1,10 @@ +use prusti_contracts::*; + +trait A {} + +#[refine_spec(where T: A, [ + ensures(true) +])] +fn foo() {} //~ ERROR: Type-conditional spec refinements can only be applied to trusted functions + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/nested-generics.rs b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/nested-generics.rs similarity index 78% rename from prusti-tests/tests/verify_overflow/fail/ghost-constraints/nested-generics.rs rename to prusti-tests/tests/verify_overflow/fail/type-cond-specs/nested-generics.rs index 37affbfcf6d..cbf4e9fb242 100644 --- a/prusti-tests/tests/verify_overflow/fail/ghost-constraints/nested-generics.rs +++ b/prusti-tests/tests/verify_overflow/fail/type-cond-specs/nested-generics.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} @@ -11,7 +9,7 @@ impl A for i32 {} impl B for i32 {} #[trusted] -#[ghost_constraint(T: A + B + B , [ +#[refine_spec(where T: A + B + B, [ ensures(result > 0) ])] fn foo(_x: T) -> i32 { diff --git a/prusti-tests/tests/verify_overflow/pass/extern-spec/swap.rs b/prusti-tests/tests/verify_overflow/pass/extern-spec/swap.rs index 405cbc03da0..95098299184 100644 --- a/prusti-tests/tests/verify_overflow/pass/extern-spec/swap.rs +++ b/prusti-tests/tests/verify_overflow/pass/extern-spec/swap.rs @@ -7,8 +7,6 @@ use prusti_contracts::*; #[extern_spec] mod std { mod mem { - use prusti_contracts::*; - #[ensures(*a == old(*b) && *b == old(*a))] pub fn swap(a: &mut T, b: &mut T); } diff --git a/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-1.rs b/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-1.rs index e3196427729..58b6b110c0e 100644 --- a/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-1.rs +++ b/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-1.rs @@ -6,7 +6,7 @@ trait MyTrait { } /// External traits #[extern_spec] -trait MyTrait<#[generic] T > { +trait MyTrait { #[ensures(true)] fn get_value(&self) -> T; } @@ -20,8 +20,7 @@ impl MyTrait for Impl { } } - fn main() { - let s = Impl{}; + let s = Impl {}; assert!(MyTrait::::get_value(&s) == 42 as i32); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-2.rs b/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-2.rs index 6bf4a113f7b..c804586f1d8 100644 --- a/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-2.rs +++ b/prusti-tests/tests/verify_overflow/pass/extern-spec/traits/generics-2.rs @@ -1,5 +1,3 @@ -// ignore-test #[concrete] is troublesome - use prusti_contracts::*; /// External traits @@ -9,9 +7,12 @@ trait MyTrait { /// External traits #[extern_spec] -trait MyTrait<#[concrete] i32 > { - #[ensures(result == 42)] - fn get_value(&self) -> i32; +trait MyTrait { + // no equality constraints yet + #[refine_spec(where T: SpecifiedGeneric, [ + ensures(result === T::my_trait__get_value()), + ])] + fn get_value(&self) -> T; } struct Impl; @@ -22,7 +23,20 @@ impl MyTrait for Impl { } fn main() { - let s = Impl{}; + let s = Impl {}; assert!(MyTrait::::get_value(&s) == 42); assert!(s.get_value() == 42); } + +// equality constraint workaround (in the general case, this would take an `&impl MyTrait` as arg) +trait SpecifiedGeneric { + #[pure] + fn my_trait__get_value() -> Self; +} + +impl SpecifiedGeneric for i32 { + #[pure] + fn my_trait__get_value() -> Self { + 42 + } +} diff --git a/prusti-tests/tests/verify_overflow/pass/issues/issue-286.rs b/prusti-tests/tests/verify_overflow/pass/issues/issue-286.rs index 3caa76479e4..5422fd04ff5 100644 --- a/prusti-tests/tests/verify_overflow/pass/issues/issue-286.rs +++ b/prusti-tests/tests/verify_overflow/pass/issues/issue-286.rs @@ -6,7 +6,7 @@ struct A { } #[extern_spec] -trait PartialOrd<#[generic] Rhs> { +trait PartialOrd { #[pure] fn lt(&self, other: &Rhs) -> bool; } diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-1.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-1.rs similarity index 67% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-1.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-1.rs index c8119cebba5..a00b3befa00 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-1.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-1.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -11,8 +10,8 @@ impl A for Foo { } #[trusted] -#[ghost_constraint(T: A , [ -ensures(result > 0) +#[refine_spec(where T: A, [ + ensures(result > 0) ])] fn foo(x: T) -> i32 { 42 @@ -23,4 +22,4 @@ fn main() { let f = Foo; let res = foo(f); assert!(res > 0); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-2.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-2.rs similarity index 66% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-2.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-2.rs index 19023db9664..15db557c8bc 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-2.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-2.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -24,8 +23,8 @@ impl SomeTrait for Foo { #[extern_spec] trait SomeTrait { - #[ghost_constraint(Self: A::AssocType> , [ - ensures(result > 0) + #[refine_spec(where Self: A::AssocType>, [ + ensures(result > 0) ])] fn foo(&self) -> i32; } @@ -33,5 +32,5 @@ trait SomeTrait { fn main() { let f = Foo; let res = f.foo(); - assert!(res > 0); // Ghost constraint satisfied! -} \ No newline at end of file + assert!(res > 0); // Type-conditional spec refinement satisfied! +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-3.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-3.rs similarity index 75% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-3.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-3.rs index 54d8025c503..081f253bee1 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-3.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-3.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -26,8 +25,8 @@ impl SomeTrait for Foo { #[extern_spec] trait SomeTrait { - #[ghost_constraint(Self: A::AssocType> , [ - ensures(result > 0) + #[refine_spec(where Self: A::AssocType>, [ + ensures(result > 0) ])] fn foo(&self) -> i32; } @@ -38,4 +37,4 @@ fn main() { }; let res = f.foo(); assert!(res > 0); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-4.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-4.rs similarity index 70% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-4.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-4.rs index 8970ca1108d..c49a28fea98 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-4.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-4.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -23,9 +22,9 @@ impl SomeTrait for Foo { } #[extern_spec] -trait SomeTrait<#[generic] T> { - #[ghost_constraint(Self: A>::AssocType> , [ - ensures(result > 0) +trait SomeTrait { + #[refine_spec(where Self: A>::AssocType>, [ + ensures(result > 0) ])] fn foo(&self, x: T) -> i32; } @@ -34,4 +33,4 @@ fn main() { let f = Foo; let res = f.foo(42 as i32); assert!(res > 0); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-5.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-5.rs similarity index 67% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-5.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-5.rs index fba24fb2457..7bda6bc285a 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/associated-types-5.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/associated-types-5.rs @@ -1,4 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true use prusti_contracts::*; trait A { @@ -11,8 +10,8 @@ impl A for Foo { } #[trusted] -#[ghost_constraint(T: A , [ -ensures(result > 0) +#[refine_spec(where T: A, [ + ensures(result > 0) ])] fn foo(x: T) -> i32 { 42 @@ -22,4 +21,4 @@ fn main() { let f = Foo; let res = foo(f); assert!(res > 0); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/merge-where-clause.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/merge-where-clause.rs similarity index 87% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/merge-where-clause.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/merge-where-clause.rs index 8e39686c423..4e3c159c176 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/merge-where-clause.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/merge-where-clause.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A { @@ -22,7 +20,7 @@ impl B for i32 { // The specs under the constraint `T: B` are also aware of the fact that `T: A` (defined on the function) #[trusted] -#[ghost_constraint(T: B, [ +#[refine_spec(where T: B, [ requires(x.a() == 42), ensures(result == x.b()) ])] diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/nested-generics.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/nested-generics.rs similarity index 73% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/nested-generics.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/nested-generics.rs index 6ceed16ac0b..ebec2142813 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/nested-generics.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/nested-generics.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} @@ -11,7 +9,7 @@ impl B for i32 {} impl B for i32 {} #[trusted] -#[ghost_constraint(T: A + B + B , [ +#[refine_spec(where T: A + B + B, [ ensures(result > 0) ])] fn foo(_x: T) -> i32 { diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/normalize-associated-types.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/normalize-associated-types.rs similarity index 73% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/normalize-associated-types.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/normalize-associated-types.rs index 090fdac046f..acacc406d17 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/normalize-associated-types.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/normalize-associated-types.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A { @@ -12,9 +10,9 @@ trait A { trait B { type BType; - #[ghost_constraint(Self: A::BType> , [ - requires(self.foo(&arg)), - ensures(self.foo(&arg)) + #[refine_spec(where Self: A::BType>, [ + requires(self.foo(&arg)), + ensures(self.foo(&arg)) ])] #[trusted] fn bar(&self, arg: Self::BType); @@ -41,4 +39,4 @@ impl B for S { fn main() { let s = S; s.bar(43); -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/ghost-constraints-extend-base-attributes.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/refinements-extend-base-attributes.rs similarity index 78% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/ghost-constraints-extend-base-attributes.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/refinements-extend-base-attributes.rs index d6818ad2352..07460e92af7 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/ghost-constraints-extend-base-attributes.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/refinements-extend-base-attributes.rs @@ -1,5 +1,4 @@ -// compile-flags: -Penable_ghost_constraints=true -// This test demonstrates that ghost constraints inherit the specs from the function. +// This test demonstrates that type-conditional spec refinements inherit the specs from the function. use prusti_contracts::*; @@ -8,7 +7,7 @@ impl A for i32 {} #[pure] #[trusted] -#[ghost_constraint(T: A, [ +#[refine_spec(where T: A, [ ensures(result == 42) ])] fn constrained_contract_stays_pure(_x: &T) -> i32 { @@ -20,7 +19,7 @@ fn verify_constrained_contract_stays_pure(a: i32) {} #[trusted] #[ensures(result % 2 == 0)] -#[ghost_constraint(T: A, [ +#[refine_spec(where T: A, [ ensures(result == 42) ])] fn constrained_contract_inherits_posts(_x: T) -> i32 { @@ -37,4 +36,4 @@ fn verify_constrained_contract_inherits_posts() { } fn main() { -} \ No newline at end of file +} diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/split-constraint.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/split-constraint.rs similarity index 71% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/split-constraint.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/split-constraint.rs index 0490013cf96..311a84e545e 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/split-constraint.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/split-constraint.rs @@ -1,5 +1,3 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} @@ -7,10 +5,10 @@ trait A {} impl A for i32 {} #[trusted] -#[ghost_constraint(T: A, [ +#[refine_spec(where T: A, [ ensures(result % 2 == 0) ])] -#[ghost_constraint(T: A, [ +#[refine_spec(where T: A, [ ensures(result > 0) ])] fn foo(_x: T) -> i32 { diff --git a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/traits-1.rs b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/traits-1.rs similarity index 71% rename from prusti-tests/tests/verify_overflow/pass/ghost-constraints/traits-1.rs rename to prusti-tests/tests/verify_overflow/pass/type-cond-specs/traits-1.rs index 995c10a3513..aefcb74eb12 100644 --- a/prusti-tests/tests/verify_overflow/pass/ghost-constraints/traits-1.rs +++ b/prusti-tests/tests/verify_overflow/pass/type-cond-specs/traits-1.rs @@ -1,11 +1,9 @@ -// compile-flags: -Penable_ghost_constraints=true - use prusti_contracts::*; trait A {} trait MyTrait { - #[ghost_constraint(Self: A, [ensures(result > 0)])] + #[refine_spec(where Self: A, [ensures(result > 0)])] #[trusted] fn foo(&self) -> i32; } @@ -23,4 +21,4 @@ impl A for MyStruct {} fn main() { let s = MyStruct; assert!(s.foo() > 0); -} \ No newline at end of file +} diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index 00c4b046b33..7951192173d 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -138,7 +138,6 @@ lazy_static::lazy_static! { settings.set_default::>("dump_fold_unfold_state_of_blocks", None).unwrap(); settings.set_default("print_hash", false).unwrap(); settings.set_default("enable_cache", true).unwrap(); - settings.set_default("enable_ghost_constraints", false).unwrap(); settings.set_default("cargo_path", "cargo").unwrap(); settings.set_default("cargo_command", "check").unwrap(); @@ -995,18 +994,6 @@ pub fn intern_names() -> bool { read_setting("intern_names") } -/// When enabled, ghost constraints can be used in Prusti specifications. -/// -/// Ghost constraints allow for specifications which are only active if a -/// certain "constraint" (i.e. a trait bound on a generic type parameter) is -/// satisfied. -/// -/// **This is an experimental feature**, because it is currently possible to -/// introduce unsound verification behavior. -pub fn enable_ghost_constraints() -> bool { - read_setting("enable_ghost_constraints") -} - /// Determines which cargo `cargo-prusti` should run (e.g. if "cargo" isn't in /// the path can point to it directly). Not relevant when only running as `prusti=rustc`. pub fn cargo_path() -> String { diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs b/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs index 39917ea3c41..036ec0626b5 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs @@ -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; let (called_def_id, call_substs) = - self.encoder - .env() - .query - .resolve_method_call(self.def_id, called_def_id, call_substs); + query.resolve_method_call(self.def_id, called_def_id, call_substs); // find static lifetime to exhale let mut lifetimes_to_exhale_inhale: Vec = Vec::new(); @@ -1505,29 +1503,25 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { } assert_eq!(lifetimes_to_exhale_inhale.len(), 1); // there must be exactly one static lifetime - // find generic argument lifetimes - let mut subst_lifetimes: Vec = call_substs - .iter() - .filter_map(|generic| match generic.unpack() { - ty::subst::GenericArgKind::Lifetime(region) => Some(region.to_text()), - _ => None, - }) - .collect(); - if subst_lifetimes.is_empty() { - // If subst_lifetimes is empty, check args for a lifetime + // find lifetimes for function args + let has_erased_regions = call_substs.regions().any(|r| r.is_erased()); + let mut subst_regions = call_substs.regions().peekable(); + if !has_erased_regions && subst_regions.peek().is_some() { + // use generic argument lifetimes + lifetimes_to_exhale_inhale.extend(subst_regions.map(|r| r.to_text())); + } else { + // if we find any erased regions, cancel everything and fall back on resolving lifetimes from args directly + // this happens e.g. when working with the result of trait method resolution, which erases lifetimes for arg in args { if let &mir::Operand::Move(place) = arg { - let place_high = self.encode_place(place, None)?; - let lifetimes = place_high.get_lifetimes(); - for lifetime in lifetimes { - subst_lifetimes.push(lifetime.name.clone()); + let encoded_place = self.encode_place(place, None)?; + let place_lifetimes = encoded_place.get_lifetimes(); + for lifetime in place_lifetimes { + lifetimes_to_exhale_inhale.push(lifetime.name.clone()); } } } } - for lifetime in subst_lifetimes { - lifetimes_to_exhale_inhale.push(lifetime); - } // construct function lifetime self.function_call_ctr += 1; diff --git a/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs b/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs index 63a0aa206a4..1f6240a0374 100644 --- a/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs +++ b/prusti-viper/src/encoder/mir/pure/pure_functions/interface.rs @@ -23,6 +23,7 @@ use vir_crate::{common::identifier::WithIdentifier, high as vir_high, polymorphi /// to account for different monomorphisations resulting from the function /// being called from callers (with different parameter environments). Each /// variant of a pure function will be encoded as a separate Viper function. +/// Lifetimes/regions are erased. type Key<'tcx> = (ProcedureDefId, SubstsRef<'tcx>, ty::PolyFnSig<'tcx>); /// Compute the key for the given call. @@ -32,9 +33,10 @@ fn compute_key<'v, 'tcx: 'v>( caller_def_id: ProcedureDefId, substs: SubstsRef<'tcx>, ) -> SpannedEncodingResult> { + let tcx = encoder.env().tcx(); Ok(( proc_def_id, - substs, + tcx.erase_regions(substs), encoder .env() .query diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index a6b676a90df..ce794c51fca 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -80,9 +80,15 @@ pub(super) fn inline_spec_item<'tcx>( parent_def_id: DefId, substs: SubstsRef<'tcx>, ) -> SpannedEncodingResult { + // each non-lifetime parameter should be matched with a subst assert_eq!( - substs.len(), - encoder.env().query.identity_substs(def_id).len() + substs.non_erasable_generics().count(), + encoder + .env() + .query + .identity_substs(def_id) + .non_erasable_generics() + .count() ); let mir = encoder diff --git a/prusti-viper/src/encoder/mir/specifications/constraints.rs b/prusti-viper/src/encoder/mir/specifications/constraints.rs index d3da4823b5d..2f592b6bd9a 100644 --- a/prusti-viper/src/encoder/mir/specifications/constraints.rs +++ b/prusti-viper/src/encoder/mir/specifications/constraints.rs @@ -47,18 +47,13 @@ impl<'spec, 'env: 'spec, 'tcx: 'env> ConstraintResolver<'spec, 'env, 'tcx> ) -> Result<&'spec ProcedureSpecification, PrustiError> { debug!("Resolving spec constraints for {query:?}"); - if !prusti_common::config::enable_ghost_constraints() { - trace!("Ghost constraints are disabled, using base spec"); - return Ok(&self.base_spec); - } - if self.specs_with_constraints.is_empty() { trace!("Spec has no constraints, using base spec"); return Ok(&self.base_spec); } let context = match query { - SpecQuery::GetProcKind(_, _) | SpecQuery::FetchSpan(_) => { + SpecQuery::FetchSpan(_) => { trace!("No need to resolve obligations for cause {:?}", query); return Ok(&self.base_spec); } @@ -72,9 +67,10 @@ impl<'spec, 'env: 'spec, 'tcx: 'env> ConstraintResolver<'spec, 'env, 'tcx> substs: call_substs, }, // Obligations are resolved for function definition encodings to account - // for ghost constraints on traits (behavioral subtyping rules will be checked + // for type-conditional spec refinements on traits (behavioral subtyping rules will be checked // against the resolved spec). - SpecQuery::FunctionDefEncoding(proc_def_id, substs) => ConstraintSolvingContext { + SpecQuery::FunctionDefEncoding(proc_def_id, substs) + | SpecQuery::GetProcKind(proc_def_id, substs) => ConstraintSolvingContext { proc_def_id: *proc_def_id, substs, caller_proc_def_id: None, @@ -131,9 +127,9 @@ fn constraint_fulfilled<'spec, 'env: 'spec, 'tcx: 'env>( } } -pub mod trait_bounds { +mod trait_bounds { use super::*; - use prusti_interface::{utils::has_trait_bounds_ghost_constraint, PrustiError}; + use prusti_interface::{utils::has_trait_bounds_type_cond_spec, PrustiError}; use rustc_hash::FxHashMap; pub(super) fn resolve<'spec, 'env: 'spec, 'tcx: 'env>( @@ -163,7 +159,7 @@ pub mod trait_bounds { .iter() .all(|predicate| { // Normalize any associated type projections. - // This needs to be done because ghost constraints might contain "deeply nested" + // This needs to be done because type-conditional spec refinements might contain "deeply nested" // associated types, e.g. `T: A::OtherAssocType` // where `::OtherAssocType` can be normalized to some concrete type. let normalized_predicate = @@ -231,11 +227,16 @@ pub mod trait_bounds { .expect_empty_or_inherent() .cloned() .unwrap_or_default(); - for spec_id in pres.iter().chain(posts.iter()) { + let purity = spec + .purity + .expect_empty_or_inherent() + .cloned() + .unwrap_or_default(); + for spec_id in pres.iter().chain(posts.iter()).chain(purity.iter()) { let param_env = env.tcx().param_env(spec_id); let spec_span = env.query.get_def_span(spec_id); let attrs = env.query.get_attributes(*spec_id); - if has_trait_bounds_ghost_constraint(attrs) { + if has_trait_bounds_type_cond_spec(attrs) { param_envs.entry(param_env).or_default().push(spec_span); } } @@ -248,7 +249,7 @@ pub mod trait_bounds { if param_envs.len() > 1 { let spans = param_envs.values().flatten().cloned().collect(); PrustiError::unsupported( - "Multiple ghost constraints with different bounds defined", + "Multiple type-conditional spec refinements with different bounds defined", MultiSpan::from_spans(spans), ) .add_note("This is currently not supported.", None) diff --git a/prusti-viper/src/encoder/mir/specifications/specs.rs b/prusti-viper/src/encoder/mir/specifications/specs.rs index acc47eafd9a..b02e97cfa17 100644 --- a/prusti-viper/src/encoder/mir/specifications/specs.rs +++ b/prusti-viper/src/encoder/mir/specifications/specs.rs @@ -57,8 +57,8 @@ pub(super) struct Specifications<'tcx> { user_typed_specs: DefSpecificationMap, /// A refinement can be different based on the query. - /// The query can resolve to different [ProcedureSpecification]s due to ghost constraints. - /// Since Prusti does currently not support refinements of ghost constraints, we + /// The query can resolve to different [ProcedureSpecification]s due to type-conditional spec refinements. + /// Since Prusti does currently not support refinements of type-conditional spec refinements, we /// store different refined versions for different queries. refined_specs: FxHashMap, ProcedureSpecification>, } diff --git a/viper/src/cache.rs b/viper/src/cache.rs index 46d99aa6db2..6c60fa94540 100644 --- a/viper/src/cache.rs +++ b/viper/src/cache.rs @@ -27,7 +27,7 @@ pub struct PersistentCache { data: HashMap, } -const RESULT_CACHE_VERSION: u64 = 2; +const RESULT_CACHE_VERSION: u64 = 3; #[derive(Debug, serde::Serialize, serde::Deserialize)] struct ResultCache { diff --git a/x.py b/x.py index 01f21eb7bbb..3939341220a 100755 --- a/x.py +++ b/x.py @@ -41,7 +41,7 @@ 'prusti-common', 'prusti-contracts/prusti-contracts', 'prusti-contracts/prusti-contracts-proc-macros', - #'prusti-contracts/prusti-specs', + 'prusti-contracts/prusti-specs', 'prusti-contracts/prusti-std', 'prusti-contracts-build', 'prusti-interface',