Skip to content

Commit 8f6fb5d

Browse files
bors[bot]Aurel300vl0w
authored
Merge #1135
1135: Generics improvements r=Aurel300 a=Aurel300 - [x] Improved associated types resolution (thanks `@vl0w,` from #980). The hope is that this won't erase too many regions... - [x] Normalise associated types when monomorphising MIR bodies for pure functions. - [x] Cache encoded pure functions by `DefId` + (full) encoded signature, not just "type parameters". Co-authored-by: Aurel Bílý <[email protected]> Co-authored-by: Jonas <[email protected]>
2 parents aaf4cae + ee0c6fc commit 8f6fb5d

File tree

10 files changed

+457
-87
lines changed

10 files changed

+457
-87
lines changed

prusti-interface/src/environment/mod.rs

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use prusti_rustc_interface::middle::mir;
1010
use prusti_rustc_interface::hir::hir_id::HirId;
1111
use prusti_rustc_interface::hir::def_id::{DefId, LocalDefId};
12-
use prusti_rustc_interface::middle::ty::{self, Binder, BoundConstness, ImplPolarity, TraitPredicate, TraitRef, TyCtxt};
12+
use prusti_rustc_interface::middle::ty::{self, Binder, BoundConstness, ImplPolarity, TraitPredicate, TraitRef, TyCtxt, TypeSuperFoldable};
1313
use prusti_rustc_interface::middle::ty::subst::{Subst, SubstsRef};
1414
use prusti_rustc_interface::trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
1515
use prusti_rustc_interface::trait_selection::traits::{ImplSource, Obligation, ObligationCause, SelectionContext};
@@ -55,6 +55,11 @@ struct CachedBody<'tcx> {
5555
monomorphised_bodies: HashMap<SubstsRef<'tcx>, Rc<mir::Body<'tcx>>>,
5656
/// Cached borrowck information.
5757
borrowck_facts: Rc<BorrowckFacts>,
58+
/// Copies of the MIR body with the given substs applied, called from the
59+
/// given caller. This also allows for associated types to be correctly
60+
/// normalised.
61+
/// TODO: merge more nicely with monomorphised_bodies?
62+
monomorphised_bodies_with_caller: HashMap<(SubstsRef<'tcx>, LocalDefId), Rc<mir::Body<'tcx>>>,
5863
}
5964

6065
struct CachedExternalBody<'tcx> {
@@ -303,12 +308,56 @@ impl<'tcx> Environment<'tcx> {
303308
base_body: Rc::new(body),
304309
monomorphised_bodies: HashMap::new(),
305310
borrowck_facts: Rc::new(facts),
311+
monomorphised_bodies_with_caller: HashMap::new(),
306312
}
307313
});
308314
body
309315
.monomorphised_bodies
310316
.entry(substs)
311-
.or_insert_with(|| ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs))
317+
.or_insert_with(|| {
318+
let param_env = self.tcx.param_env(def_id);
319+
let substituted = ty::EarlyBinder(body.base_body.clone()).subst(self.tcx, substs);
320+
self.resolve_assoc_types(substituted.clone(), param_env)
321+
})
322+
.clone()
323+
}
324+
325+
pub fn local_mir_with_caller(
326+
&self,
327+
def_id: LocalDefId,
328+
caller_def_id: LocalDefId,
329+
substs: SubstsRef<'tcx>,
330+
) -> Rc<mir::Body<'tcx>> {
331+
// TODO: duplication ...
332+
let mut bodies = self.bodies.borrow_mut();
333+
let body = bodies.entry(def_id)
334+
.or_insert_with(|| {
335+
// SAFETY: This is safe because we are feeding in the same `tcx`
336+
// that was used to store the data.
337+
let body_with_facts = unsafe {
338+
self::mir_storage::retrieve_mir_body(self.tcx, def_id)
339+
};
340+
let body = body_with_facts.body;
341+
let facts = BorrowckFacts {
342+
input_facts: RefCell::new(Some(body_with_facts.input_facts)),
343+
output_facts: body_with_facts.output_facts,
344+
location_table: RefCell::new(Some(body_with_facts.location_table)),
345+
};
346+
347+
CachedBody {
348+
base_body: Rc::new(body),
349+
monomorphised_bodies: HashMap::new(),
350+
borrowck_facts: Rc::new(facts),
351+
monomorphised_bodies_with_caller: HashMap::new(),
352+
}
353+
});
354+
body
355+
.monomorphised_bodies_with_caller
356+
.entry((substs, caller_def_id))
357+
.or_insert_with(|| {
358+
let param_env = self.tcx.param_env(caller_def_id);
359+
self.tcx.subst_and_normalize_erasing_regions(substs, param_env, body.base_body.clone())
360+
})
312361
.clone()
313362
}
314363

@@ -538,10 +587,35 @@ impl<'tcx> Environment<'tcx> {
538587
// Normalize the type to account for associated types
539588
let ty = self.resolve_assoc_types(ty, param_env);
540589
let ty = self.tcx.erase_late_bound_regions(ty);
590+
591+
// Associated type was not normalised but it might still have a
592+
// `Copy` bound declared on it.
593+
// TODO: implement this more generally in `type_implements_trait` and
594+
// `type_implements_trait_with_trait_substs`.
595+
if let ty::TyKind::Projection(proj) = ty.kind() {
596+
let item_bounds = self.tcx.bound_item_bounds(proj.item_def_id);
597+
if item_bounds.0.iter().any(|predicate| {
598+
if let ty::PredicateKind::Trait(ty::TraitPredicate {
599+
trait_ref: ty::TraitRef { def_id: trait_def_id, .. },
600+
polarity: ty::ImplPolarity::Positive,
601+
..
602+
}) = predicate.kind().skip_binder() {
603+
trait_def_id == self.tcx.lang_items()
604+
.copy_trait()
605+
.unwrap()
606+
} else {
607+
false
608+
}
609+
}) {
610+
return true;
611+
}
612+
}
613+
541614
ty.is_copy_modulo_regions(self.tcx.at(prusti_rustc_interface::span::DUMMY_SP), param_env)
542615
}
543616

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

584658
/// Normalizes associated types in foldable types,
585659
/// 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-
);
660+
pub fn resolve_assoc_types<T: ty::TypeFoldable<'tcx> + std::fmt::Debug>(&self, normalizable: T, param_env: ty::ParamEnv<'tcx>) -> T {
661+
struct Normalizer<'a, 'tcx> {
662+
tcx: &'a ty::TyCtxt<'tcx>,
663+
param_env: ty::ParamEnv<'tcx>,
664+
}
665+
impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for Normalizer<'a, 'tcx> {
666+
fn tcx(&self) -> ty::TyCtxt<'tcx> {
667+
*self.tcx
668+
}
592669

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
670+
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
671+
// rustc by default panics when we execute this TypeFolder on a mir::* type,
672+
// but we want to resolve associated types when we retrieve a local mir::Body
673+
c
674+
}
675+
676+
fn fold_ty(&mut self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> {
677+
match ty.kind() {
678+
ty::TyKind::Projection(_) => {
679+
let normalized = self.tcx.infer_ctxt().enter(|infcx| {
680+
use prusti_rustc_interface::trait_selection::traits::{fully_normalize, FulfillmentContext};
681+
682+
let normalization_result = fully_normalize(
683+
&infcx,
684+
FulfillmentContext::new(),
685+
ObligationCause::dummy(),
686+
self.param_env,
687+
ty
688+
);
689+
690+
match normalization_result {
691+
Ok(res) => res,
692+
Err(errors) => {
693+
debug!("Error while resolving associated types: {:?}", errors);
694+
ty
695+
}
696+
}
697+
});
698+
normalized.super_fold_with(self)
699+
}
700+
_ => ty.super_fold_with(self)
701+
}
601702
}
602703
}
704+
705+
normalizable.fold_with(&mut Normalizer {tcx: &self.tcx, param_env})
603706
}
604707
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use prusti_contracts::*;
2+
3+
trait Trait {
4+
type Assoc: Copy;
5+
6+
#[pure] fn output_copy(&self) -> Self::Assoc;
7+
#[pure] fn input_copy(&self, x: Self::Assoc) -> bool;
8+
}
9+
10+
#[requires(x.output_copy() === y)]
11+
#[requires(x.input_copy(z))]
12+
fn test<U: Copy, T: Trait<Assoc = U>>(x: &mut T, y: U, z: U) {}
13+
14+
#[trusted]
15+
fn main() {}

0 commit comments

Comments
 (0)