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

Generics improvements #1135

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
132 changes: 116 additions & 16 deletions prusti-interface/src/environment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use prusti_rustc_interface::middle::mir;
use prusti_rustc_interface::hir::hir_id::HirId;
use prusti_rustc_interface::hir::def_id::{DefId, LocalDefId};
use prusti_rustc_interface::middle::ty::{self, Binder, BoundConstness, ImplPolarity, TraitPredicate, TraitRef, TyCtxt};
use prusti_rustc_interface::middle::ty::{self, Binder, BoundConstness, ImplPolarity, TraitPredicate, TraitRef, TyCtxt, TypeSuperFoldable};
use prusti_rustc_interface::middle::ty::subst::{Subst, SubstsRef};
use prusti_rustc_interface::trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
use prusti_rustc_interface::trait_selection::traits::{ImplSource, Obligation, ObligationCause, SelectionContext};
Expand Down Expand Up @@ -55,6 +55,8 @@ struct CachedBody<'tcx> {
monomorphised_bodies: HashMap<SubstsRef<'tcx>, Rc<mir::Body<'tcx>>>,
/// Cached borrowck information.
borrowck_facts: Rc<BorrowckFacts>,

monomorphised_bodies_with_caller: HashMap<(SubstsRef<'tcx>, LocalDefId), Rc<mir::Body<'tcx>>>,
}

struct CachedExternalBody<'tcx> {
Expand Down Expand Up @@ -303,12 +305,56 @@ impl<'tcx> Environment<'tcx> {
base_body: Rc::new(body),
monomorphised_bodies: HashMap::new(),
borrowck_facts: Rc::new(facts),
monomorphised_bodies_with_caller: HashMap::new(),
}
});
body
.monomorphised_bodies
.entry(substs)
.or_insert_with(|| ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs))
.or_insert_with(|| {
let param_env = self.tcx.param_env(def_id);
let substituted = ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs);
self.resolve_assoc_types(substituted.clone(), param_env)
})
.clone()
}

pub fn local_mir_with_caller(
&self,
def_id: LocalDefId,
caller_def_id: LocalDefId,
substs: SubstsRef<'tcx>,
) -> Rc<mir::Body<'tcx>> {
Comment on lines +325 to +330
Copy link
Member Author

Choose a reason for hiding this comment

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

@JonasAlaif Will this change be easy to incorporate into your refactored Environment? For proper monomorphisation (and assoc. type normalisation) I need called DefId, type substitutions (as before), but also the caller DefId.

And should I wait for your PR to land or can I merge this first (assuming the tests pass)?

// TODO: duplication ...
let mut bodies = self.bodies.borrow_mut();
let body = bodies.entry(def_id)
.or_insert_with(|| {
// SAFETY: This is safe because we are feeding in the same `tcx`
// that was used to store the data.
let body_with_facts = unsafe {
self::mir_storage::retrieve_mir_body(self.tcx, def_id)
};
let body = body_with_facts.body;
let facts = BorrowckFacts {
input_facts: RefCell::new(Some(body_with_facts.input_facts)),
output_facts: body_with_facts.output_facts,
location_table: RefCell::new(Some(body_with_facts.location_table)),
};

CachedBody {
base_body: Rc::new(body),
monomorphised_bodies: HashMap::new(),
borrowck_facts: Rc::new(facts),
monomorphised_bodies_with_caller: HashMap::new(),
}
});
body
.monomorphised_bodies_with_caller
.entry((substs, caller_def_id))
.or_insert_with(|| {
let param_env = self.tcx.param_env(caller_def_id);
self.tcx.subst_and_normalize_erasing_regions(substs, param_env, body.base_body.clone())
})
.clone()
}

Expand Down Expand Up @@ -538,10 +584,35 @@ impl<'tcx> Environment<'tcx> {
// Normalize the type to account for associated types
let ty = self.resolve_assoc_types(ty, param_env);
let ty = self.tcx.erase_late_bound_regions(ty);

// Associated type was not normalised but it might still have a
// `Copy` bound declared on it.
// TODO: implement this more generally in `type_implements_trait` and
// `type_implements_trait_with_trait_substs`.
if let ty::TyKind::Projection(proj) = ty.kind() {
let item_bounds = self.tcx.bound_item_bounds(proj.item_def_id);
if item_bounds.0.iter().any(|predicate| {
if let ty::PredicateKind::Trait(ty::TraitPredicate {
trait_ref: ty::TraitRef { def_id: trait_def_id, .. },
polarity: ty::ImplPolarity::Positive,
..
}) = predicate.kind().skip_binder() {
trait_def_id == self.tcx.lang_items()
.copy_trait()
.unwrap()
} else {
false
}
}) {
return true;
}
}

ty.is_copy_modulo_regions(self.tcx.at(prusti_rustc_interface::span::DUMMY_SP), param_env)
}

/// Checks whether the given type implements the trait with the given DefId.
/// The trait should have no type parameters.
pub fn type_implements_trait(&self, ty: ty::Ty<'tcx>, trait_def_id: DefId, param_env: ty::ParamEnv<'tcx>) -> bool {
self.type_implements_trait_with_trait_substs(ty, trait_def_id, ty::List::empty(), param_env)
}
Expand Down Expand Up @@ -583,22 +654,51 @@ impl<'tcx> Environment<'tcx> {

/// Normalizes associated types in foldable types,
/// i.e. this resolves projection types ([ty::TyKind::Projection]s)
/// **Important:** Regions while be erased during this process
pub fn resolve_assoc_types<T: ty::TypeFoldable<'tcx> + std::fmt::Debug + Copy>(&self, normalizable: T, param_env: ty::ParamEnv<'tcx>) -> T {
let norm_res = self.tcx.try_normalize_erasing_regions(
param_env,
normalizable
);
pub fn resolve_assoc_types<T: ty::TypeFoldable<'tcx> + std::fmt::Debug>(&self, normalizable: T, param_env: ty::ParamEnv<'tcx>) -> T {
struct Normalizer<'a, 'tcx> {
tcx: &'a ty::TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for Normalizer<'a, 'tcx> {
fn tcx(&self) -> ty::TyCtxt<'tcx> {
*self.tcx
}

match norm_res {
Ok(normalized) => {
debug!("Normalized {:?}: {:?}", normalizable, normalized);
normalized
},
Err(err) => {
debug!("Error while resolving associated types for {:?}: {:?}", normalizable, err);
normalizable
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
// rustc by default panics when we execute this TypeFolder on a mir::* type,
// but we want to resolve associated types when we retrieve a local mir::Body
c
}

fn fold_ty(&mut self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> {
match ty.kind() {
ty::TyKind::Projection(_) => {
let normalized = self.tcx.infer_ctxt().enter(|infcx| {
use prusti_rustc_interface::trait_selection::traits::{fully_normalize, FulfillmentContext};

let normalization_result = fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
self.param_env,
ty
);

match normalization_result {
Ok(res) => res,
Err(errors) => {
debug!("Error while resolving associated types: {:?}", errors);
ty
}
}
});
normalized.super_fold_with(self)
}
_ => ty.super_fold_with(self)
}
}
}

normalizable.fold_with(&mut Normalizer {tcx: &self.tcx, param_env})
}
}
15 changes: 15 additions & 0 deletions prusti-tests/tests/verify_overflow/pass/generic/associated-copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use prusti_contracts::*;

trait Trait {
type Assoc: Copy;

#[pure] fn output_copy(&self) -> Self::Assoc;
#[pure] fn input_copy(&self, x: Self::Assoc) -> bool;
}

#[requires(x.output_copy() === y)]
#[requires(x.input_copy(z))]
fn test<U: Copy, T: Trait<Assoc = U>>(x: &mut T, y: U, z: U) {}

#[trusted]
fn main() {}
Loading