Refactor object-safety test to use def-ids only
This commit is contained in:
parent
703308db4a
commit
710af0498d
5 changed files with 59 additions and 16 deletions
|
|
@ -48,6 +48,8 @@ pub use self::util::get_vtable_index_of_object_method;
|
|||
pub use self::util::trait_ref_for_builtin_bound;
|
||||
pub use self::util::supertraits;
|
||||
pub use self::util::Supertraits;
|
||||
pub use self::util::supertrait_def_ids;
|
||||
pub use self::util::SupertraitDefIds;
|
||||
pub use self::util::transitive_bounds;
|
||||
pub use self::util::upcast;
|
||||
|
||||
|
|
@ -640,7 +642,7 @@ impl<'tcx> FulfillmentError<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> TraitObligation<'tcx> {
|
||||
fn self_ty(&self) -> Ty<'tcx> {
|
||||
self.predicate.0.self_ty()
|
||||
fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
|
||||
ty::Binder(self.predicate.skip_binder().self_ty())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,36 +53,36 @@ pub enum MethodViolationCode {
|
|||
}
|
||||
|
||||
pub fn is_object_safe<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>)
|
||||
trait_def_id: ast::DefId)
|
||||
-> bool
|
||||
{
|
||||
// Because we query yes/no results frequently, we keep a cache:
|
||||
let cached_result =
|
||||
tcx.object_safety_cache.borrow().get(&trait_ref.def_id()).cloned();
|
||||
tcx.object_safety_cache.borrow().get(&trait_def_id).cloned();
|
||||
|
||||
let result =
|
||||
cached_result.unwrap_or_else(|| {
|
||||
let result = object_safety_violations(tcx, trait_ref.clone()).is_empty();
|
||||
let result = object_safety_violations(tcx, trait_def_id).is_empty();
|
||||
|
||||
// Record just a yes/no result in the cache; this is what is
|
||||
// queried most frequently. Note that this may overwrite a
|
||||
// previous result, but always with the same thing.
|
||||
tcx.object_safety_cache.borrow_mut().insert(trait_ref.def_id(), result);
|
||||
tcx.object_safety_cache.borrow_mut().insert(trait_def_id, result);
|
||||
|
||||
result
|
||||
});
|
||||
|
||||
debug!("is_object_safe({}) = {}", trait_ref.repr(tcx), result);
|
||||
debug!("is_object_safe({}) = {}", trait_def_id.repr(tcx), result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn object_safety_violations<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
sub_trait_ref: ty::PolyTraitRef<'tcx>)
|
||||
trait_def_id: ast::DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
supertraits(tcx, sub_trait_ref)
|
||||
.flat_map(|tr| object_safety_violations_for_trait(tcx, tr.def_id()).into_iter())
|
||||
traits::supertrait_def_ids(tcx, trait_def_id)
|
||||
.flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id).into_iter())
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1237,7 +1237,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// self-type from one of the other inputs. Without this check,
|
||||
// these cases wind up being considered ambiguous due to a
|
||||
// (spurious) ambiguity introduced here.
|
||||
if !object_safety::is_object_safe(self.tcx(), obligation.predicate.to_poly_trait_ref()) {
|
||||
let predicate_trait_ref = obligation.predicate.to_poly_trait_ref();
|
||||
if !object_safety::is_object_safe(self.tcx(), predicate_trait_ref.def_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -209,6 +209,47 @@ pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
|
|||
elaborate_trait_refs(tcx, bounds).filter_to_traits()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Iterator over def-ids of supertraits
|
||||
|
||||
pub struct SupertraitDefIds<'cx, 'tcx:'cx> {
|
||||
tcx: &'cx ty::ctxt<'tcx>,
|
||||
stack: Vec<ast::DefId>,
|
||||
visited: FnvHashSet<ast::DefId>,
|
||||
}
|
||||
|
||||
pub fn supertrait_def_ids<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
|
||||
trait_def_id: ast::DefId)
|
||||
-> SupertraitDefIds<'cx, 'tcx>
|
||||
{
|
||||
SupertraitDefIds {
|
||||
tcx: tcx,
|
||||
stack: vec![trait_def_id],
|
||||
visited: Some(trait_def_id).into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> Iterator for SupertraitDefIds<'cx, 'tcx> {
|
||||
type Item = ast::DefId;
|
||||
|
||||
fn next(&mut self) -> Option<ast::DefId> {
|
||||
let def_id = match self.stack.pop() {
|
||||
Some(def_id) => def_id,
|
||||
None => { return None; }
|
||||
};
|
||||
|
||||
let predicates = ty::lookup_super_predicates(self.tcx, def_id);
|
||||
let visited = &mut self.visited;
|
||||
self.stack.extend(
|
||||
predicates.predicates
|
||||
.iter()
|
||||
.filter_map(|p| p.to_opt_poly_trait_ref())
|
||||
.map(|t| t.def_id())
|
||||
.filter(|&super_def_id| visited.insert(super_def_id)));
|
||||
Some(def_id)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Other
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -28,18 +28,17 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
|
|||
object_trait: &ty::TyTrait<'tcx>,
|
||||
span: Span)
|
||||
{
|
||||
let object_trait_ref =
|
||||
object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
|
||||
let trait_def_id = object_trait.principal_def_id();
|
||||
|
||||
if traits::is_object_safe(tcx, object_trait_ref.clone()) {
|
||||
if traits::is_object_safe(tcx, trait_def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_err!(tcx.sess, span, E0038,
|
||||
"cannot convert to a trait object because trait `{}` is not object-safe",
|
||||
ty::item_path_str(tcx, object_trait_ref.def_id()));
|
||||
ty::item_path_str(tcx, trait_def_id));
|
||||
|
||||
let violations = traits::object_safety_violations(tcx, object_trait_ref.clone());
|
||||
let violations = traits::object_safety_violations(tcx, trait_def_id);
|
||||
for violation in violations {
|
||||
match violation {
|
||||
ObjectSafetyViolation::SizedSelf => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue