Skip to content

Commit 9c1ac30

Browse files
bors[bot]vl0w
andauthored
Merge #980
980: WIP: Iterator tracking issues r=Aurel300 a=vl0w Some (smaller / bigger) changes needed to enable iterator support (WIP / tracking PR) - Moved normalization of constraint param env (for assoc types) for better logging - Fixed a bug in `SpecGraph`: Preconditions of the base spec were copied to the constrained spec - Ghost constraint resolving is a bit relaxed (`predicate_may_hold` instead of `predicate_must_hold_considering_regions`) to account for references in associated types. See test `associated_types_6.rs` - `merge_generics` now also merges lifetimes to accout for lifetimes appearing in `#[refine_trait_spec]` - Lifetimes in type models are not converted to the anonymous lifetime `'_` anymore - Do not `#[derive(Copy, Clone]` on type models, because the derive macro "conditionally" implements it when generics are involved. That is, `#[derive(Copy)] struct Foo<T>` adds `impl<T: Copy> Copy for Foo {}`, but we do not want to implement `Copy` only if `T` is `Copy`. - Quantified variables now support associated types: `forall(|x: Self::SomeAssocType| ...)` Co-authored-by: Jonas Hansen <[email protected]>
2 parents a78a76d + fa449f9 commit 9c1ac30

File tree

19 files changed

+1047
-315
lines changed

19 files changed

+1047
-315
lines changed

docs/dev-guide/src/config/flags.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
| [`DUMP_VIPER_PROGRAM`](#dump_viper_program) | `bool` | `false` |
2424
| [`ENABLE_CACHE`](#enable_cache) | `bool` | `true` |
2525
| [`ENABLE_GHOST_CONSTRAINTS`](#enable_ghost_constraints) | `bool` | `false` |
26+
| [`ENABLE_ITERATORS`](#enable_iterators) | `bool` | `false` |
2627
| [`ENABLE_PURIFICATION_OPTIMIZATION`](#enable_purification_optimization) | `bool` | `false` |
2728
| [`ENABLE_TYPE_INVARIANTS`](#enable_type_invariants) | `bool` | `false` |
2829
| [`ENABLE_VERIFY_ONLY_BASIC_BLOCK_PATH`](#enable_verify_only_basic_block_path) | `bool` | `false` |
@@ -174,6 +175,13 @@ on a generic type parameter) is satisfied.
174175

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

178+
## `ENABLE_ITERATORS`
179+
180+
When enabled, calls to `Iterator::next` will be encoded in the Viper program.
181+
Otherwise, an error is thrown during the encoding.
182+
183+
**This is an experimental feature**, iterator support is still experimental.
184+
177185
## `ENABLE_PURIFICATION_OPTIMIZATION`
178186

179187
When enabled, impure methods are optimized using the purification optimization, which tries to convert heap operations to pure (snapshot-based) operations.

prusti-common/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ lazy_static::lazy_static! {
131131
settings.set_default("print_hash", false).unwrap();
132132
settings.set_default("enable_cache", true).unwrap();
133133
settings.set_default("enable_ghost_constraints", false).unwrap();
134+
settings.set_default("enable_iterators", false).unwrap();
134135

135136
// Flags for testing.
136137
settings.set_default::<Option<i64>>("verification_deadline", None).unwrap();
@@ -884,3 +885,9 @@ pub fn enable_ghost_constraints() -> bool {
884885
pub fn enable_type_invariants() -> bool {
885886
read_setting("enable_type_invariants")
886887
}
888+
889+
/// When enabled, calls to `Iterator::next` will be encoded in the Viper program.
890+
/// Otherwise, an error is thrown during the encoding.
891+
pub fn enable_iterators() -> bool {
892+
read_setting("enable_iterators")
893+
}

prusti-contracts/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,12 @@ pub fn exists<T, F>(_trigger_set: T, _closure: F) -> bool {
351351
true
352352
}
353353

354+
pub fn snap<T>(_x: &T) -> T {
355+
unimplemented!()
356+
}
357+
358+
pub fn snapshot_equality<T>(_l: T, _r: T) -> bool {
359+
true
360+
}
361+
354362
pub use private::*;

prusti-interface/src/environment/mod.rs

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ use prusti_rustc_interface::hir::def_id::{DefId, LocalDefId};
1212
use prusti_rustc_interface::middle::ty::{self, Binder, BoundConstness, ImplPolarity, TraitPredicate, TraitRef, TyCtxt};
1313
use prusti_rustc_interface::middle::ty::subst::{Subst, SubstsRef};
1414
use prusti_rustc_interface::trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
15+
use prusti_rustc_interface::middle::ty::TypeSuperFoldable;
1516
use prusti_rustc_interface::trait_selection::traits::{ImplSource, Obligation, ObligationCause, SelectionContext};
1617
use std::path::PathBuf;
1718
use prusti_rustc_interface::errors::{DiagnosticBuilder, EmissionGuarantee, MultiSpan};
1819
use prusti_rustc_interface::span::{Span, symbol::Symbol};
20+
use prusti_common::config;
1921
use std::collections::HashSet;
2022
use log::{debug, trace};
2123
use std::rc::Rc;
@@ -308,7 +310,11 @@ impl<'tcx> Environment<'tcx> {
308310
body
309311
.monomorphised_bodies
310312
.entry(substs)
311-
.or_insert_with(|| ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs))
313+
.or_insert_with(|| {
314+
let param_env = self.tcx.param_env(def_id);
315+
let substituted = ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs);
316+
self.resolve_assoc_types(substituted.clone(), param_env)
317+
})
312318
.clone()
313319
}
314320

@@ -519,14 +525,31 @@ impl<'tcx> Environment<'tcx> {
519525
) -> (ProcedureDefId, SubstsRef<'tcx>) {
520526
use prusti_rustc_interface::middle::ty::TypeVisitable;
521527

522-
// avoids a compiler-internal panic
523-
if call_substs.needs_infer() {
528+
// Avoids a compiler-internal panic
529+
// this check ignores any lifetimes/regions, which at this point would
530+
// need inference. They are thus ignored.
531+
// TODO: different behaviour used for unsafe core proof
532+
let needs_infer = if config::unsafe_core_proof() {
533+
call_substs.needs_infer()
534+
} else {
535+
self.any_type_needs_infer(call_substs)
536+
};
537+
if needs_infer {
524538
return (called_def_id, call_substs);
525539
}
526540

527541
let param_env = self.tcx.param_env(caller_def_id);
528-
traits::resolve_instance(self.tcx, param_env.and((called_def_id, call_substs)))
542+
543+
// `resolve_instance` below requires normalized substs.
544+
let normalized_call_substs = self.tcx.normalize_erasing_regions(param_env, call_substs);
545+
546+
traits::resolve_instance(self.tcx, param_env.and((called_def_id, normalized_call_substs)))
529547
.map(|opt_instance| opt_instance
548+
// if the resolved instance is the same as what we queried for
549+
// anyway, ignore it: this way we keep the regions in substs
550+
// at least for non-trait-impl method calls
551+
// TODO: different behaviour used for unsafe core proof
552+
.filter(|instance| !config::unsafe_core_proof() || instance.def_id() != called_def_id)
530553
.map(|instance| (instance.def_id(), instance.substs))
531554
.unwrap_or((called_def_id, call_substs)))
532555
.unwrap_or((called_def_id, call_substs))
@@ -538,6 +561,7 @@ impl<'tcx> Environment<'tcx> {
538561
// Normalize the type to account for associated types
539562
let ty = self.resolve_assoc_types(ty, param_env);
540563
let ty = self.tcx.erase_late_bound_regions(ty);
564+
let ty = self.tcx.erase_regions(ty);
541565
ty.is_copy_modulo_regions(self.tcx.at(prusti_rustc_interface::span::DUMMY_SP), param_env)
542566
}
543567

@@ -577,28 +601,86 @@ impl<'tcx> Environment<'tcx> {
577601
);
578602

579603
self.tcx.infer_ctxt().enter(|infcx| {
580-
infcx.predicate_must_hold_considering_regions(&obligation)
604+
infcx.predicate_must_hold_modulo_regions(&obligation)
581605
})
582606
}
583607

584608
/// Normalizes associated types in foldable types,
585609
/// i.e. this resolves projection types ([ty::TyKind::Projection]s)
586-
/// **Important:** Regions while be erased during this process
587-
pub fn resolve_assoc_types<T: ty::TypeFoldable<'tcx> + std::fmt::Debug + Copy>(&self, normalizable: T, param_env: ty::ParamEnv<'tcx>) -> T {
588-
let norm_res = self.tcx.try_normalize_erasing_regions(
589-
param_env,
590-
normalizable
591-
);
610+
pub fn resolve_assoc_types<T: ty::TypeFoldable<'tcx> + std::fmt::Debug>(&self, normalizable: T, param_env: ty::ParamEnv<'tcx>) -> T {
611+
struct Normalizer<'a, 'tcx> {
612+
tcx: &'a ty::TyCtxt<'tcx>,
613+
param_env: ty::ParamEnv<'tcx>,
614+
}
615+
impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for Normalizer<'a, 'tcx> {
616+
fn tcx(&self) -> ty::TyCtxt<'tcx> {
617+
*self.tcx
618+
}
619+
620+
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
621+
// rustc by default panics when we execute this TypeFolder on a mir::* type,
622+
// but we want to resolve associated types when we retrieve a local mir::Body
623+
c
624+
}
625+
626+
fn fold_ty(&mut self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> {
627+
match ty.kind() {
628+
ty::TyKind::Projection(_) => {
629+
let normalized = self.tcx.infer_ctxt().enter(|infcx| {
630+
use prusti_rustc_interface::trait_selection::traits::{fully_normalize, FulfillmentContext};
631+
632+
let normalization_result = fully_normalize(
633+
&infcx,
634+
FulfillmentContext::new(),
635+
ObligationCause::dummy(),
636+
self.param_env,
637+
ty
638+
);
639+
640+
match normalization_result {
641+
Ok(res) => res,
642+
Err(errors) => {
643+
debug!("Error while resolving associated types: {:?}", errors);
644+
ty
645+
}
646+
}
647+
});
648+
normalized.super_fold_with(self)
649+
}
650+
_ => ty.super_fold_with(self)
651+
}
652+
}
653+
}
654+
655+
normalizable.fold_with(&mut Normalizer {tcx: &self.tcx, param_env})
656+
}
657+
658+
fn any_type_needs_infer<T: ty::TypeFoldable<'tcx>>(&self, t: T) -> bool {
659+
// Helper
660+
fn is_nested_ty(ty: ty::Ty<'_>) -> bool {
661+
let mut walker = ty.walk();
662+
let first = walker.next().unwrap().expect_ty();
663+
assert!(ty == first);
664+
walker.next().is_some()
665+
}
592666

593-
match norm_res {
594-
Ok(normalized) => {
595-
debug!("Normalized {:?}: {:?}", normalizable, normalized);
596-
normalized
597-
},
598-
Err(err) => {
599-
debug!("Error while resolving associated types for {:?}: {:?}", normalizable, err);
600-
normalizable
667+
// Visitor
668+
struct NeedsInfer;
669+
impl<'tcx> ty::TypeVisitor<'tcx> for NeedsInfer {
670+
type BreakTy = ();
671+
672+
fn visit_ty(&mut self, ty: ty::Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
673+
use prusti_rustc_interface::middle::ty::{TypeVisitable, TypeSuperVisitable};
674+
if is_nested_ty(ty) {
675+
ty.super_visit_with(self)
676+
} else if ty.needs_infer() {
677+
std::ops::ControlFlow::BREAK
678+
} else {
679+
std::ops::ControlFlow::CONTINUE
680+
}
601681
}
602682
}
683+
684+
t.visit_with(&mut NeedsInfer).is_break()
603685
}
604-
}
686+
}

prusti-interface/src/specs/typed.rs

Lines changed: 10 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,16 @@ impl SpecGraph<ProcedureSpecification> {
318318
) -> &mut ProcedureSpecification {
319319
self.specs_with_constraints
320320
.entry(constraint)
321-
.or_insert_with(|| self.base_spec.clone())
321+
.or_insert_with(|| {
322+
let mut base = self.base_spec.clone();
323+
324+
// Preconditions of the base spec do not appear in the constrained spec
325+
// Any preconditions that exist on the base spec are thus pruned
326+
// (see comment on impl block)
327+
base.pres = SpecificationItem::Empty;
328+
329+
base
330+
})
322331
}
323332

324333
/// Gets the constraint of a spec function `spec`.
@@ -462,24 +471,6 @@ pub enum ProcedureSpecificationKindError {
462471
}
463472

464473
impl SpecificationItem<ProcedureSpecificationKind> {
465-
pub fn is_pure(&self) -> Result<bool, ProcedureSpecificationKindError> {
466-
self.validate()?;
467-
468-
Ok(matches!(
469-
self.extract_with_selective_replacement(),
470-
Some(ProcedureSpecificationKind::Pure) | Some(ProcedureSpecificationKind::Predicate(_))
471-
))
472-
}
473-
474-
pub fn is_impure(&self) -> Result<bool, ProcedureSpecificationKindError> {
475-
self.validate()?;
476-
477-
Ok(match self.extract_with_selective_replacement() {
478-
Some(refined) => matches!(refined, ProcedureSpecificationKind::Impure),
479-
_ => true,
480-
})
481-
}
482-
483474
pub fn get_predicate_body(
484475
&self,
485476
) -> Result<Option<&LocalDefId>, ProcedureSpecificationKindError> {
@@ -806,69 +797,5 @@ mod tests {
806797
));
807798
}
808799
}
809-
810-
mod is_impure {
811-
use super::*;
812-
813-
macro_rules! impure_checks {
814-
($($name:ident: $value:expr,)*) => {
815-
$(
816-
#[test]
817-
fn $name() {
818-
let (item, expected) = $value;
819-
let item: SpecificationItem<ProcedureSpecificationKind> = item;
820-
let result = item.is_impure().expect("Expected impure");
821-
assert_eq!(result, expected);
822-
}
823-
)*
824-
}
825-
}
826-
827-
impure_checks!(
828-
empty: (Empty, true),
829-
inherent_impure: (Inherent(Impure), true),
830-
inherent_pure: (Inherent(Pure), false),
831-
inherent_predicate: (Inherent(Predicate(None)), false),
832-
inherited_impure: (Inherited(Impure), true),
833-
inherited_pure: (Inherited(Pure), false),
834-
inherited_predicate: (Inherited(Predicate(None)), false),
835-
refined_impure_parent_impure_child: (Refined(Impure, Impure), true),
836-
refined_impure_parent_pure_child: (Refined(Impure, Pure), false),
837-
refined_pure_parent_with_pure_child: (Refined(Pure, Pure), false),
838-
refined_predicate_parent_with_predicate_child: (Refined(Predicate(None), Predicate(None)), false),
839-
);
840-
}
841-
842-
mod is_pure {
843-
use super::*;
844-
845-
macro_rules! pure_checks {
846-
($($name:ident: $value:expr,)*) => {
847-
$(
848-
#[test]
849-
fn $name() {
850-
let (item, expected) = $value;
851-
let item: SpecificationItem<ProcedureSpecificationKind> = item;
852-
let result = item.is_pure().expect("Expected pure");
853-
assert_eq!(result, expected);
854-
}
855-
)*
856-
}
857-
}
858-
859-
pure_checks!(
860-
empty: (Empty, false),
861-
inherent_impure: (Inherent(Impure), false),
862-
inherent_pure: (Inherent(Pure), true),
863-
inherent_predicate: (Inherent(Predicate(None)), true),
864-
inherited_impure: (Inherited(Impure), false),
865-
inherited_pure: (Inherited(Pure), true),
866-
inherited_predicate: (Inherited(Predicate(None)), true),
867-
refined_impure_parent_impure_child: (Refined(Impure, Impure), false),
868-
refined_impure_parent_pure_child: (Refined(Impure, Pure), true),
869-
refined_pure_parent_with_pure: (Refined(Pure, Pure), true),
870-
refined_predicate_parent_with_predicate_child: (Refined(Predicate(None), Predicate(None)), true),
871-
);
872-
}
873800
}
874801
}

0 commit comments

Comments
 (0)