Skip to content

Commit d5c2c4a

Browse files
committed
Implement the object-safety checks for arbitrary_self_types: part 1
For a trait method to be considered object-safe, the receiver type must satisfy certain properties: first, we need to be able to get the vtable to so we can look up the method, and second, we need to convert the receiver from the version where `Self=dyn Trait`, to the version where `Self=T`, `T` being some unknown, `Sized` type that implements `Trait`. To check that the receiver satisfies those properties, we use the following query: forall (U) { if (Self: Unsize<U>) { Receiver[Self => U]: CoerceSized<Receiver> } } where `Receiver` is the receiver type of the method (e.g. `Rc<Self>`), and `Receiver[Self => U]` is the receiver type where `Self = U`, e.g. `Rc<U>`. forall queries like this aren’t implemented in the trait system yet, so for now we are using a bit of a hack — see the code for explanation.
1 parent be80a79 commit d5c2c4a

File tree

1 file changed

+144
-20
lines changed

1 file changed

+144
-20
lines changed

src/librustc/traits/object_safety.rs

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,21 @@
1313
//! object if all of their methods meet certain criteria. In particular,
1414
//! they must:
1515
//!
16-
//! - have a suitable receiver from which we can extract a vtable;
16+
//! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version
17+
//! that doesn't contain the vtable;
1718
//! - not reference the erased type `Self` except for in this receiver;
1819
//! - not have generic type parameters
1920
2021
use super::elaborate_predicates;
2122

2223
use hir::def_id::DefId;
2324
use lint;
24-
use traits;
25-
use ty::{self, Ty, TyCtxt, TypeFoldable};
26-
use ty::util::ExplicitSelf;
25+
use traits::{self, Obligation, ObligationCause};
26+
use ty::{self, Ty, TyCtxt, TypeFoldable, Predicate, ToPredicate};
27+
use ty::subst::{Subst, Substs};
2728
use std::borrow::Cow;
28-
use syntax::ast;
29+
use std::iter::{self};
30+
use syntax::ast::{self, Name};
2931
use syntax_pos::Span;
3032

3133
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -62,8 +64,8 @@ impl ObjectSafetyViolation {
6264
format!("method `{}` references the `Self` type in where clauses", name).into(),
6365
ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) =>
6466
format!("method `{}` has generic type parameters", name).into(),
65-
ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) =>
66-
format!("method `{}` has a non-standard `self` type", name).into(),
67+
ObjectSafetyViolation::Method(name, MethodViolationCode::UncoercibleReceiver) =>
68+
format!("method `{}` has an uncoercible receiver type", name).into(),
6769
ObjectSafetyViolation::AssociatedConst(name) =>
6870
format!("the trait cannot contain associated consts like `{}`", name).into(),
6971
}
@@ -85,8 +87,8 @@ pub enum MethodViolationCode {
8587
/// e.g., `fn foo<A>()`
8688
Generic,
8789

88-
/// arbitrary `self` type, e.g. `self: Rc<Self>`
89-
NonStandardSelfType,
90+
/// the self argument can't be coerced from Self=dyn Trait to Self=T where T: Trait
91+
UncoercibleReceiver,
9092
}
9193

9294
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
@@ -113,6 +115,8 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
113115
pub fn object_safety_violations(self, trait_def_id: DefId)
114116
-> Vec<ObjectSafetyViolation>
115117
{
118+
debug!("object_safety_violations: {:?}", trait_def_id);
119+
116120
traits::supertrait_def_ids(self, trait_def_id)
117121
.flat_map(|def_id| self.object_safety_violations_for_trait(def_id))
118122
.collect()
@@ -277,23 +281,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
277281
method: &ty::AssociatedItem)
278282
-> Option<MethodViolationCode>
279283
{
280-
// The method's first parameter must be something that derefs (or
281-
// autorefs) to `&self`. For now, we only accept `self`, `&self`
282-
// and `Box<Self>`.
284+
// The method's first parameter must be named `self`
283285
if !method.method_has_self_argument {
284286
return Some(MethodViolationCode::StaticMethod);
285287
}
286288

287289
let sig = self.fn_sig(method.def_id);
288290

289-
let self_ty = self.mk_self_type();
290-
let self_arg_ty = sig.skip_binder().inputs()[0];
291-
if let ExplicitSelf::Other = ExplicitSelf::determine(self_arg_ty, |ty| ty == self_ty) {
292-
return Some(MethodViolationCode::NonStandardSelfType);
293-
}
294-
295-
// The `Self` type is erased, so it should not appear in list of
296-
// arguments or return type apart from the receiver.
297291
for input_ty in &sig.skip_binder().inputs()[1..] {
298292
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
299293
return Some(MethodViolationCode::ReferencesSelf);
@@ -320,9 +314,139 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
320314
return Some(MethodViolationCode::WhereClauseReferencesSelf(span));
321315
}
322316

317+
let receiver_ty = self.liberate_late_bound_regions(
318+
method.def_id,
319+
&sig.map_bound(|sig| sig.inputs()[0]),
320+
);
321+
322+
// until `unsized_locals` is fully implemented, `self: Self` can't be coerced from
323+
// `Self=dyn Trait` to `Self=T`. However, this is already considered object-safe. We allow
324+
// it as a special case here.
325+
// FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_coercible` allows
326+
// `Receiver: Unsize<Receiver[Self => dyn Trait]>`
327+
if receiver_ty != self.mk_self_type() {
328+
if !self.receiver_is_coercible(method, receiver_ty) {
329+
return Some(MethodViolationCode::UncoercibleReceiver);
330+
}
331+
}
332+
323333
None
324334
}
325335

336+
/// checks the method's receiver (the `self` argument) can be coerced from
337+
/// a fat pointer, including the trait object vtable, to a thin pointer.
338+
/// e.g. from `Rc<dyn Trait>` to `Rc<T>`, where `T` is the erased type of the underlying object.
339+
/// More formally:
340+
/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`
341+
/// - require the following bound:
342+
/// forall(T: Trait) {
343+
/// Receiver[Self => dyn Trait]: CoerceSized<Receiver[Self => T]>
344+
/// }
345+
/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`"
346+
/// (substitution notation).
347+
///
348+
/// some examples of receiver types and their required obligation
349+
/// - `&'a mut self` requires `&'a mut dyn Trait: CoerceSized<&'a mut T>`
350+
/// - `self: Rc<Self>` requires `Rc<dyn Trait>: CoerceSized<Rc<T>>`
351+
///
352+
/// The only case where the receiver is not coercible, but is still a valid receiver
353+
/// type (just not object-safe), is when there is more than one level of pointer indirection.
354+
/// e.g. `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
355+
/// is no way, or at least no inexpensive way, to coerce the receiver, because the object that
356+
/// needs to be coerced is behind a pointer.
357+
///
358+
/// In practice, there are issues with the above bound: `where` clauses that apply to `Self`
359+
/// would have to apply to `T`, trait object types have a lot of parameters that need to
360+
/// be filled in (lifetime and type parameters, and the lifetime of the actual object), and
361+
/// I'm pretty sure using `dyn Trait` in the query causes another object-safety query for
362+
/// `Trait`, resulting in cyclic queries. So in the implementation, we use the following,
363+
/// more general bound:
364+
///
365+
/// forall (U: ?Sized) {
366+
/// if (Self: Unsize<U>) {
367+
/// Receiver[Self => U]: CoerceSized<Receiver>
368+
/// }
369+
/// }
370+
///
371+
/// for `self: &'a mut Self`, this means `&'a mut U: CoerceSized<&'a mut Self>`
372+
/// for `self: Rc<Self>`, this means `Rc<U>: CoerceSized<Rc<Self>>`
373+
//
374+
// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
375+
// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
376+
// `self: Wrapper<Self>`.
377+
#[allow(dead_code)]
378+
fn receiver_is_coercible(
379+
self,
380+
method: &ty::AssociatedItem,
381+
receiver_ty: Ty<'tcx>,
382+
) -> bool {
383+
debug!("receiver_is_coercible: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
384+
385+
let traits = (self.lang_items().unsize_trait(),
386+
self.lang_items().coerce_sized_trait());
387+
let (unsize_did, coerce_sized_did) = if let (Some(u), Some(cu)) = traits {
388+
(u, cu)
389+
} else {
390+
debug!("receiver_is_coercible: Missing Unsize or CoerceSized traits");
391+
return false;
392+
};
393+
394+
// use a bogus type parameter to mimick a forall(U) query using u32::MAX for now.
395+
// FIXME(mikeyhew) this is a total hack, and we should replace it when real forall queries
396+
// are implemented
397+
let target_self_ty: Ty<'tcx> = self.mk_ty_param(
398+
::std::u32::MAX,
399+
Name::intern("RustaceansAreAwesome").as_interned_str(),
400+
);
401+
402+
// create a modified param env, with `Self: Unsize<U>` added to the caller bounds
403+
let param_env = {
404+
let mut param_env = self.param_env(method.def_id);
405+
406+
let predicate = ty::TraitRef {
407+
def_id: unsize_did,
408+
substs: self.mk_substs_trait(self.mk_self_type(), &[target_self_ty.into()]),
409+
}.to_predicate();
410+
411+
let caller_bounds: Vec<Predicate<'tcx>> = param_env.caller_bounds.iter().cloned()
412+
.chain(iter::once(predicate))
413+
.collect();
414+
415+
param_env.caller_bounds = self.intern_predicates(&caller_bounds);
416+
417+
param_env
418+
};
419+
420+
let receiver_substs = Substs::for_item(self, method.def_id, |param, _| {
421+
if param.index == 0 {
422+
target_self_ty.into()
423+
} else {
424+
self.mk_param_from_def(param)
425+
}
426+
});
427+
// the type `Receiver[Self => U]` in the query
428+
let unsized_receiver_ty = receiver_ty.subst(self, receiver_substs);
429+
430+
// Receiver[Self => U]: CoerceSized<Receiver>
431+
let obligation = {
432+
let predicate = ty::TraitRef {
433+
def_id: coerce_sized_did,
434+
substs: self.mk_substs_trait(unsized_receiver_ty, &[receiver_ty.into()]),
435+
}.to_predicate();
436+
437+
Obligation::new(
438+
ObligationCause::dummy(),
439+
param_env,
440+
predicate,
441+
)
442+
};
443+
444+
self.infer_ctxt().enter(|ref infcx| {
445+
// the receiver is coercible iff the obligation holds
446+
infcx.predicate_must_hold(&obligation)
447+
})
448+
}
449+
326450
fn contains_illegal_self_type_reference(self,
327451
trait_def_id: DefId,
328452
ty: Ty<'tcx>)

0 commit comments

Comments
 (0)