Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

WIP: Iterator tracking issues #980

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4caee3a
Adjust copy check in environment to better handle regions
vl0w Apr 26, 2022
ed7dffd
Maybe we need to handle TyKind::Ref differently?
vl0w Apr 26, 2022
03b3640
Support associated types in copy check
vl0w Apr 27, 2022
138d98e
Pass type with binder to type_is_copy to better account for regions
vl0w Apr 27, 2022
a408861
Normalize function signature in PureFunctionEncoder to account for as…
vl0w Apr 28, 2022
dbbed0d
Remove check for validity of pure function in process_encoding_queue
vl0w Apr 28, 2022
161b956
Erase all regions in copy check
vl0w Apr 28, 2022
eda1445
Do not perform normalization when there are no projections
vl0w Apr 28, 2022
41aad37
Partially undo removal of code in process_encoding_queue.
vl0w Apr 28, 2022
e2a5467
Rename normalize_to, return original value if normalized value contai…
vl0w Apr 28, 2022
43a5387
Pass ParamEnv to resolve_assoc_types, make it fallible
vl0w Apr 29, 2022
b604cd4
Small cleanups
vl0w Apr 29, 2022
5dc9705
Add custom iterator tests
vl0w Apr 20, 2022
f8f2859
Add flag to add experimental iterator support
vl0w Apr 21, 2022
0da3287
Move predicate normalization in constraint solver for better debugging
vl0w Apr 27, 2022
c1cbead
Do not copy preconditions of base spec to constrained spec
vl0w Apr 27, 2022
59bc78c
Use predicate_must_hold_modulo_regions when resolving ghost constrain…
vl0w Apr 29, 2022
0af45d7
Handle lifetimes in merge_generics
vl0w Apr 29, 2022
c527dc1
Support lifetimes in type models
vl0w May 2, 2022
c8081fb
Relax needs_infer check in Environment::resolve_method_call to ignore…
vl0w May 2, 2022
78c51fb
Merge branch 'master' into iterators-feature-flag
vl0w May 2, 2022
af55da2
Support lifetimes in type models
vl0w May 3, 2022
8c7a2ab
Add first tests
vl0w May 3, 2022
fa1c14a
Verification of Iter in while loop
vl0w May 4, 2022
5e396ff
Create custom Copy/Clone impls for type models (do not derive them)
vl0w May 4, 2022
3afba7c
Adjust flag docs for iterator killswitch
vl0w May 4, 2022
70e5b0f
Normalize substs in Environment::resolve_method_call to account for n…
vl0w May 6, 2022
ed1a7a5
Fix typo
vl0w May 8, 2022
d1d4953
Support associated types in quantifiers
vl0w May 9, 2022
7e7bde4
Remove dead comment in any_type_needs_infer
vl0w May 9, 2022
e3bbbd9
Remove dead code
vl0w May 11, 2022
8e05591
Merge branch 'master' into iterators-feature-flag
vl0w May 23, 2022
116bcd6
Merge branch 'master' into iterators-feature-flag
vl0w May 30, 2022
f16f646
fix for #1033 with test cases
Pointerbender Jun 7, 2022
96ee8b9
snapshot equality
Aurel300 Jul 12, 2022
414e081
remove special Fn*::call* treatment
Aurel300 Jul 18, 2022
2b6d632
test closures using type-dependent contracts
Aurel300 Jul 18, 2022
9142078
Merge remote-tracking branch 'upstream/master' into iterators-feature…
Aurel300 Jul 19, 2022
186e97e
fix
Aurel300 Jul 19, 2022
8ed4eb2
Merge remote-tracking branch 'upstream/master' into iterators-feature…
Aurel300 Jul 22, 2022
dbd4d2d
erase regions less eagerly for method calls
Aurel300 Jul 22, 2022
1eaa7d0
Merge remote-tracking branch 'upstream/master' into iterators-feature…
Aurel300 Jul 27, 2022
c74215b
fix
Aurel300 Jul 27, 2022
e9e5c78
Merge remote-tracking branch 'upstream/master' into iterators-feature…
Aurel300 Jul 29, 2022
fa449f9
another unsafe core proof workaround for trait resolution
Aurel300 Jul 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/dev-guide/src/config/flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
| [`DUMP_VIPER_PROGRAM`](#dump_viper_program) | `bool` | `false` |
| [`ENABLE_CACHE`](#enable_cache) | `bool` | `true` |
| [`ENABLE_GHOST_CONSTRAINTS`](#enable_ghost_constraints) | `bool` | `false` |
| [`ENABLE_ITERATORS`](#enable_iterators) | `bool` | `false` |
| [`ENABLE_PURIFICATION_OPTIMIZATION`](#enable_purification_optimization) | `bool` | `false` |
| [`ENABLE_VERIFY_ONLY_BASIC_BLOCK_PATH`](#enable_verify_only_basic_block_path) | `bool` | `false` |
| [`ENCODE_BITVECTORS`](#encode_bitvectors) | `bool` | `false` |
Expand Down Expand Up @@ -141,6 +142,12 @@ on a generic type parameter) is satisfied.

**This is an experimental feature**, because it is currently possible to introduce unsound verification behavior.

## `ENABLE_ITERATORS`

Enables support for iterators.
Aurel300 marked this conversation as resolved.
Show resolved Hide resolved

**This is an experimental feature**, iterator support is still experimental.

## `ENABLE_PURIFICATION_OPTIMIZATION`

When enabled, impure methods are optimized using the purification optimization, which tries to convert heap operations to pure (snapshot-based) operations.
Expand Down
6 changes: 6 additions & 0 deletions prusti-common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ lazy_static! {
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("enable_iterators", false).unwrap();

// Flags for debugging Prusti that can change verification results.
settings.set_default("disable_name_mangling", false).unwrap();
Expand Down Expand Up @@ -599,4 +600,9 @@ pub fn intern_names() -> bool {
/// Enables ghost constraint parsing and resolving.
pub fn enable_ghost_constraints() -> bool {
read_setting("enable_ghost_constraints")
}

/// Enables (experimental) iterator support
Aurel300 marked this conversation as resolved.
Show resolved Hide resolved
pub fn enable_iterators() -> bool {
read_setting("enable_iterators")
}
41 changes: 35 additions & 6 deletions prusti-interface/src/environment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,10 @@ impl<'tcx> Environment<'tcx> {
called_def_id: ProcedureDefId, // what are we calling?
call_substs: SubstsRef<'tcx>,
) -> (ProcedureDefId, SubstsRef<'tcx>) {
use crate::rustc_middle::ty::TypeFoldable;

// avoids a compiler-internal panic
if call_substs.needs_infer() {
// Avoids a compiler-internal panic
// this check ignores any lifetimes/regions, which at this point would
// need inference. They are thus ignored.
if self.any_type_needs_infer(call_substs) {
return (called_def_id, call_substs);
}

Expand Down Expand Up @@ -539,7 +539,7 @@ impl<'tcx> Environment<'tcx> {
);

self.tcx.infer_ctxt().enter(|infcx| {
infcx.predicate_must_hold_considering_regions(&obligation)
infcx.predicate_must_hold_modulo_regions(&obligation)
})
}

Expand All @@ -563,4 +563,33 @@ impl<'tcx> Environment<'tcx> {
}
}
}
}

fn any_type_needs_infer<T: ty::TypeFoldable<'tcx>>(&self, t: T) -> bool {
// Helper
fn is_nested_ty(ty: ty::Ty<'_>) -> bool {
let mut walker = ty.walk();
let first = walker.next().unwrap().expect_ty(); // This is known to yield t
assert!(ty == first);
walker.next().is_some()
}

// Visitor
struct NeedsInfer;
impl<'tcx> ty::fold::TypeVisitor<'tcx> for NeedsInfer {
type BreakTy = ();

fn visit_ty(&mut self, ty: ty::Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
use crate::rustc_middle::ty::TypeFoldable;
if is_nested_ty(ty) {
ty.super_visit_with(self)
} else if ty.needs_infer() {
std::ops::ControlFlow::BREAK
} else {
std::ops::ControlFlow::CONTINUE
}
}
}

t.visit_with(&mut NeedsInfer).is_break()
}
}
11 changes: 10 additions & 1 deletion prusti-interface/src/specs/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,16 @@ impl SpecGraph<ProcedureSpecification> {
) -> &mut ProcedureSpecification {
self.specs_with_constraints
.entry(constraint)
.or_insert_with(|| self.base_spec.clone())
.or_insert_with(|| {
let mut base = self.base_spec.clone();

// Preconditions of the base spec do not appear in the constrained spec
// Any preconditions that exist on the base spec are thus pruned
// (see comment on impl block)
base.pres = SpecificationItem::Empty;

base
})
}

/// Gets the constraint of a spec function `spec`.
Expand Down
83 changes: 58 additions & 25 deletions prusti-specs/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::borrow::BorrowMut;
use std::collections::HashMap;
use proc_macro2::Ident;
use syn::{GenericParam, parse_quote, TypeParam};
use syn::{GenericParam, LifetimeDef, parse_quote, TypeParam};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use uuid::Uuid;
Expand All @@ -14,6 +14,7 @@ pub(crate) use receiver_rewriter::*;
/// Module which provides various extension traits for syn types.
/// These allow for writing of generic code over these types.
mod syn_extensions {
use proc_macro2::Ident;
use syn::{Attribute, Generics, ImplItemMacro, ImplItemMethod, ItemFn, ItemImpl, ItemStruct, ItemTrait, Macro, Signature, TraitItemMacro, TraitItemMethod};

/// Trait which signals that the corresponding syn item contains generics
Expand Down Expand Up @@ -129,6 +130,14 @@ mod syn_extensions {
&self.attrs
}
}

// Abstraction over everything that has a [syn::Ident]
pub(crate) trait HasIdent {
fn ident(&self) -> &Ident;
}

impl HasIdent for ItemTrait { fn ident(&self) -> &Ident { &self.ident } }
impl HasIdent for ItemStruct { fn ident(&self) -> &Ident { &self.ident } }
}

/// See [SelfTypeRewriter]
Expand Down Expand Up @@ -328,7 +337,7 @@ mod receiver_rewriter {
}

/// Copies the [syn::Generics] of `source` to the generics of `target`
/// **Important**: Lifetimes and const params are currently ignored.
/// **Important**: Const params are currently ignored.
/// If `source` has generic params which do not appear in `target`, they are added first.
///
/// # Example
Expand All @@ -347,25 +356,40 @@ pub(crate) fn merge_generics<T: HasGenerics>(target: &mut T, source: &T) {

// Merge all type params
let mut existing_target_type_params: HashMap<Ident, &mut TypeParam> = HashMap::new();
let mut existing_target_lifetimes: HashMap<Ident, &mut LifetimeDef> = HashMap::new();
let mut new_generic_params: Vec<GenericParam> = Vec::new();
for param_target in generics_target.params.iter_mut() {
if let GenericParam::Type(type_param_target) = param_target {
existing_target_type_params.insert(type_param_target.ident.clone(), type_param_target);
match param_target {
GenericParam::Type(t) => {
existing_target_type_params.insert(t.ident.clone(), t);
},
GenericParam::Lifetime(lt) => {
existing_target_lifetimes.insert(lt.lifetime.ident.clone(), lt);
},
_ => () // not supported
}
}

for param_source in generics_source.params.iter() {
// Lifetimes and consts are currently not handled
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);
if let Some(type_param_target) = maybe_type_param_source {
type_param_target.bounds.extend(type_param_source.bounds.clone());
} else {
new_generic_params.push(GenericParam::Type(type_param_source.clone()));
}
// Consts are currently not handled
match param_source {
GenericParam::Type(type_param_source) => {
if let Some(type_param_target) = existing_target_type_params.remove(&type_param_source.ident) {
type_param_target.bounds.extend(type_param_source.bounds.clone());
} else {
new_generic_params.push(GenericParam::Type(type_param_source.clone()));
}
},
GenericParam::Lifetime(lifetime_source) => {
if let Some(lifetime_target) = existing_target_lifetimes.remove(&lifetime_source.lifetime.ident) {
lifetime_target.bounds.extend(lifetime_source.bounds.clone());
} else {
new_generic_params.push(GenericParam::Lifetime(lifetime_source.clone()));
}
},
_ => () // not supported
}

}

// Merge the new parameters with the existing ones.
Expand Down Expand Up @@ -423,7 +447,7 @@ pub(crate) fn add_phantom_data_for_generic_params(item_struct: &mut syn::ItemStr
}
syn::GenericParam::Lifetime(lt_def) => {
let lt = &lt_def.lifetime;
let ty: syn::Type = parse_quote!(&#lt ::core::marker::PhantomData<()>);
let ty: syn::Type = parse_quote!(::core::marker::PhantomData<&#lt ()>);
Some(ty)
}
_ => None,
Expand Down Expand Up @@ -521,6 +545,21 @@ mod tests {
[impl<T> Foo for Bar where T: A {}]
}
}

#[test]
fn lifetimes() {
test_merge! {
[impl<'b> Foo for Bar {}] into
[impl<'a> Foo for Bar {}] gives
[impl<'b, 'a> Foo for Bar {}]
}

test_merge! {
[impl<'a: 'c, 'd> Foo for Bar {}] into
[impl<'a: 'b> Foo for Bar {}] gives
[impl<'d, 'a: 'b + 'c> Foo for Bar {}]
}
}
}

mod phantom_data {
Expand Down Expand Up @@ -625,16 +664,10 @@ mod tests {

fn assert_phantom_ref_for(expected_lifetime: &str, actual_field: &syn::Field) {
match &actual_field.ty {
syn::Type::Reference(type_ref) => {
let actual_lifetime = type_ref.lifetime.as_ref().expect("Expected lifetime");
assert_eq!(expected_lifetime, actual_lifetime.to_string().trim());

let ty = type_ref.elem.as_ref();
assert_eq!(
"::core::marker::PhantomData<()>",
ty.to_token_stream().to_string().replace(' ', "")
);
}
syn::Type::Path(type_path) => {
let actual_type = type_path.path.to_token_stream().to_string().replace(" ", "");
assert_eq!(format!("::core::marker::PhantomData<&{}()>", expected_lifetime), actual_type);
},
_ => panic!(),
}
}
Expand Down
Loading