Ensure that, for every trait Foo, the predicate Foo : Foo holds.

This commit is contained in:
Niko Matsakis 2014-12-23 05:26:34 -05:00
parent 19dcecb225
commit dabd7507b6
8 changed files with 334 additions and 38 deletions
src
libcore/fmt
librustc/middle
librustc_trans/trans
librustc_typeck/check

@ -605,9 +605,6 @@ impl<'a, Sized? T: Show> Show for &'a T {
impl<'a, Sized? T: Show> Show for &'a mut T {
fn fmt(&self, f: &mut Formatter) -> Result { (**self).fmt(f) }
}
impl<'a> Show for &'a (Show+'a) {
fn fmt(&self, f: &mut Formatter) -> Result { (*self).fmt(f) }
}
impl Show for bool {
fn fmt(&self, f: &mut Formatter) -> Result {

@ -217,6 +217,9 @@ pub enum Vtable<'tcx, N> {
/// for some type parameter.
VtableParam,
/// Virtual calls through an object
VtableObject(VtableObjectData<'tcx>),
/// Successful resolution for a builtin trait.
VtableBuiltin(VtableBuiltinData<N>),
@ -252,6 +255,13 @@ pub struct VtableBuiltinData<N> {
pub nested: subst::VecPerParamSpace<N>
}
/// A vtable for some object-safe trait `Foo` automatically derived
/// for the object type `Foo`.
#[deriving(PartialEq,Eq,Clone)]
pub struct VtableObjectData<'tcx> {
pub object_ty: Ty<'tcx>,
}
/// True if neither the trait nor self type is local. Note that `impl_def_id` must refer to an impl
/// of a trait, not an inherent impl.
pub fn is_orphan_impl(tcx: &ty::ctxt,
@ -372,6 +382,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableFnPointer(..) => (&[]).iter(),
VtableUnboxedClosure(..) => (&[]).iter(),
VtableParam => (&[]).iter(),
VtableObject(_) => (&[]).iter(),
VtableBuiltin(ref i) => i.iter_nested(),
}
}
@ -382,6 +393,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableFnPointer(ref sig) => VtableFnPointer((*sig).clone()),
VtableUnboxedClosure(d, ref s) => VtableUnboxedClosure(d, s.clone()),
VtableParam => VtableParam,
VtableObject(ref p) => VtableObject(p.clone()),
VtableBuiltin(ref b) => VtableBuiltin(b.map_nested(op)),
}
}
@ -394,6 +406,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableFnPointer(sig) => VtableFnPointer(sig),
VtableUnboxedClosure(d, s) => VtableUnboxedClosure(d, s),
VtableParam => VtableParam,
VtableObject(p) => VtableObject(p),
VtableBuiltin(no) => VtableBuiltin(no.map_move_nested(op)),
}
}

@ -377,20 +377,14 @@ fn project_type<'cx,'tcx>(
ambiguous: false,
};
assemble_candidates_from_object_type(selcx,
obligation,
&mut candidates);
assemble_candidates_from_param_env(selcx,
obligation,
&mut candidates);
if candidates.vec.is_empty() {
assemble_candidates_from_param_env(selcx,
obligation,
&mut candidates);
if let Err(e) = assemble_candidates_from_impls(selcx,
obligation,
&mut candidates) {
return Err(ProjectionTyError::TraitSelectionError(e));
}
if let Err(e) = assemble_candidates_from_impls(selcx,
obligation,
&mut candidates) {
return Err(ProjectionTyError::TraitSelectionError(e));
}
debug!("{} candidates, ambiguous={}",
@ -467,18 +461,22 @@ fn assemble_candidates_from_predicates<'cx,'tcx>(
fn assemble_candidates_from_object_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
object_ty: Ty<'tcx>)
{
let infcx = selcx.infcx();
let trait_ref = infcx.resolve_type_vars_if_possible(&obligation.predicate.trait_ref);
debug!("assemble_candidates_from_object_type(trait_ref={})",
trait_ref.repr(infcx.tcx));
let self_ty = trait_ref.self_ty();
let data = match self_ty.sty {
debug!("assemble_candidates_from_object_type(object_ty={})",
object_ty.repr(infcx.tcx));
let data = match object_ty.sty {
ty::ty_trait(ref data) => data,
_ => { return; }
_ => {
selcx.tcx().sess.span_bug(
obligation.cause.span,
format!("assemble_candidates_from_object_type called with non-object: {}",
object_ty.repr(selcx.tcx()))[]);
}
};
let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), self_ty);
let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty);
let env_predicates = projection_bounds.iter()
.map(|p| p.as_predicate())
.collect();
@ -515,6 +513,10 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
candidate_set.vec.push(
ProjectionTyCandidate::Impl(data));
}
super::VtableObject(data) => {
assemble_candidates_from_object_type(
selcx, obligation, candidate_set, data.object_ty);
}
super::VtableParam(..) => {
// This case tell us nothing about the value of an
// associated type. Consider:

@ -24,8 +24,10 @@ use super::{ObligationCauseCode, BuiltinDerivedObligation};
use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch};
use super::{Selection};
use super::{SelectionResult};
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure, VtableFnPointer};
use super::{VtableImplData, VtableBuiltinData};
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure,
VtableFnPointer, VtableObject};
use super::{VtableImplData, VtableObjectData, VtableBuiltinData};
use super::object_safety;
use super::{util};
use middle::fast_reject;
@ -147,6 +149,8 @@ enum SelectionCandidate<'tcx> {
/// types generated for a fn pointer type (e.g., `fn(int)->int`)
FnPointerCandidate,
ObjectCandidate,
ErrorCandidate,
}
@ -717,6 +721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
try!(self.assemble_unboxed_closure_candidates(obligation, &mut candidates));
try!(self.assemble_fn_pointer_candidates(obligation, &mut candidates));
try!(self.assemble_candidates_from_impls(obligation, &mut candidates.vec));
self.assemble_candidates_from_object_ty(obligation, &mut candidates);
}
}
@ -878,7 +883,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let matching_bounds =
all_bounds.filter(
|bound| self.infcx.probe(
|_| self.match_where_clause(obligation, bound.clone())).is_ok());
|_| self.match_poly_trait_ref(obligation, bound.clone())).is_ok());
let param_candidates =
matching_bounds.map(|bound| ParamCandidate(bound));
@ -945,7 +950,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
match self_ty.sty {
ty::ty_infer(..) => {
ty::ty_infer(ty::TyVar(_)) => {
candidates.ambiguous = true; // could wind up being a fn() type
}
@ -991,6 +996,67 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(())
}
/// Search for impls that might apply to `obligation`.
fn assemble_candidates_from_object_ty(&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>)
{
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
debug!("assemble_candidates_from_object_ty(self_ty={})",
self_ty.repr(self.tcx()));
// Object-safety candidates are only applicable to object-safe
// traits. Including this check is useful because it helps
// inference in cases of traits like `BorrowFrom`, which are
// not object-safe, and which rely on being able to infer the
// 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()) {
return;
}
let poly_trait_ref = match self_ty.sty {
ty::ty_trait(ref data) => {
data.principal_trait_ref_with_self_ty(self.tcx(), self_ty)
}
ty::ty_infer(ty::TyVar(_)) => {
debug!("assemble_candidates_from_object_ty: ambiguous");
candidates.ambiguous = true; // could wind up being an object type
return;
}
_ => {
return;
}
};
debug!("assemble_candidates_from_object_ty: poly_trait_ref={}",
poly_trait_ref.repr(self.tcx()));
// see whether the object trait can be upcast to the trait we are looking for
let obligation_def_id = obligation.predicate.def_id();
let upcast_trait_ref = match util::upcast(self.tcx(), poly_trait_ref, obligation_def_id) {
Some(r) => r,
None => { return; }
};
debug!("assemble_candidates_from_object_ty: upcast_trait_ref={}",
upcast_trait_ref.repr(self.tcx()));
// check whether the upcast version of the trait-ref matches what we are looking for
match
self.infcx.probe(
|_| self.match_poly_trait_ref(obligation, upcast_trait_ref.clone()))
{
Ok(()) => {
debug!("assemble_candidates_from_object_ty: matched, pushing candidate");
candidates.vec.push(ObjectCandidate);
}
Err(()) => { }
}
}
///////////////////////////////////////////////////////////////////////////
// WINNOW
//
@ -1544,6 +1610,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(VtableUnboxedClosure(closure_def_id, substs))
}
ObjectCandidate => {
let data = self.confirm_object_candidate(obligation);
Ok(VtableObject(data))
}
FnPointerCandidate => {
let fn_type =
try!(self.confirm_fn_pointer_candidate(obligation));
@ -1727,6 +1798,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested: impl_predicates }
}
fn confirm_object_candidate(&mut self,
obligation: &TraitObligation<'tcx>)
-> VtableObjectData<'tcx>
{
debug!("confirm_object_candidate({})",
obligation.repr(self.tcx()));
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
let poly_trait_ref = match self_ty.sty {
ty::ty_trait(ref data) => {
data.principal_trait_ref_with_self_ty(self.tcx(), self_ty)
}
_ => {
self.tcx().sess.span_bug(obligation.cause.span,
"object candidate with non-object");
}
};
let obligation_def_id = obligation.predicate.def_id();
let upcast_trait_ref = match util::upcast(self.tcx(),
poly_trait_ref.clone(),
obligation_def_id) {
Some(r) => r,
None => {
self.tcx().sess.span_bug(obligation.cause.span,
format!("unable to upcast from {} to {}",
poly_trait_ref.repr(self.tcx()),
obligation_def_id.repr(self.tcx())).as_slice());
}
};
match self.match_poly_trait_ref(obligation, upcast_trait_ref) {
Ok(()) => { }
Err(()) => {
self.tcx().sess.span_bug(obligation.cause.span,
"failed to match trait refs");
}
}
VtableObjectData { object_ty: self_ty }
}
fn confirm_fn_pointer_candidate(&mut self,
obligation: &TraitObligation<'tcx>)
-> Result<ty::Ty<'tcx>,SelectionError<'tcx>>
@ -1962,12 +2075,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
})
}
fn match_where_clause(&mut self,
obligation: &TraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<(),()>
fn match_poly_trait_ref(&mut self,
obligation: &TraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<(),()>
{
debug!("match_where_clause: obligation={} where_clause_trait_ref={}",
debug!("match_poly_trait_ref: obligation={} where_clause_trait_ref={}",
obligation.repr(self.tcx()),
where_clause_trait_ref.repr(self.tcx()));
@ -2161,6 +2274,9 @@ impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> {
ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)),
ProjectionCandidate => format!("ProjectionCandidate"),
FnPointerCandidate => format!("FnPointerCandidate"),
ObjectCandidate => {
format!("ObjectCandidate")
}
UnboxedClosureCandidate(c, ref s) => {
format!("UnboxedClosureCandidate({},{})", c, s.repr(tcx))
}

@ -238,6 +238,12 @@ impl<'tcx, N> fmt::Show for VtableImplData<'tcx, N> {
}
}
impl<'tcx> fmt::Show for super::VtableObjectData<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableObject(...)")
}
}
/// See `super::obligations_for_generics`
pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
cause: ObligationCause<'tcx>,
@ -366,6 +372,10 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> {
format!("VtableFnPointer({})",
d.repr(tcx)),
super::VtableObject(ref d) =>
format!("VtableObject({})",
d.repr(tcx)),
super::VtableParam =>
format!("VtableParam"),
@ -391,6 +401,13 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableBuiltinData<N> {
}
}
impl<'tcx> Repr<'tcx> for super::VtableObjectData<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
format!("VtableObject(object_ty={})",
self.object_ty.repr(tcx))
}
}
impl<'tcx> Repr<'tcx> for super::SelectionError<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
match *self {

@ -503,6 +503,15 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
}
traits::VtableParam => traits::VtableParam,
traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)),
traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)),
}
}
}
impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> {
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> {
traits::VtableObjectData {
object_ty: self.object_ty.fold_with(folder)
}
}
}

@ -8,12 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use arena::TypedArena;
use back::abi;
use llvm;
use llvm::ValueRef;
use back::link;
use llvm::{mod, ValueRef, get_param};
use metadata::csearch;
use middle::subst::{Substs};
use middle::subst::{Subst, Substs};
use middle::subst::VecPerParamSpace;
use middle::subst;
use middle::traits;
@ -370,6 +370,10 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let llfn = trans_fn_pointer_shim(bcx.ccx(), fn_ty);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableObject(ref data) => {
let llfn = trans_object_shim(bcx.ccx(), data.object_ty, trait_id, n_method);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableBuiltin(..) |
traits::VtableParam(..) => {
bcx.sess().bug(
@ -503,6 +507,137 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
};
}
/// Generate a shim function that allows an object type like `SomeTrait` to
/// implement the type `SomeTrait`. Imagine a trait definition:
///
/// trait SomeTrait { fn get(&self) -> int; ... }
///
/// And a generic bit of code:
///
/// fn foo<T:SomeTrait>(t: &T) {
/// let x = SomeTrait::get;
/// x(t)
/// }
///
/// What is the value of `x` when `foo` is invoked with `T=SomeTrait`?
/// The answer is that it it is a shim function generate by this
/// routine:
///
/// fn shim(t: &SomeTrait) -> int {
/// // ... call t.get() virtually ...
/// }
///
/// In fact, all virtual calls can be thought of as normal trait calls
/// that go through this shim function.
pub fn trans_object_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
object_ty: Ty<'tcx>,
trait_id: ast::DefId,
method_offset_in_trait: uint)
-> ValueRef
{
let _icx = push_ctxt("trans_object_shim");
let tcx = ccx.tcx();
debug!("trans_object_shim(object_ty={}, trait_id={}, n_method={})",
object_ty.repr(tcx),
trait_id.repr(tcx),
method_offset_in_trait);
let object_trait_ref =
match object_ty.sty {
ty::ty_trait(ref data) => {
data.principal_trait_ref_with_self_ty(tcx, object_ty)
}
_ => {
tcx.sess.bug(format!("trans_object_shim() called on non-object: {}",
object_ty.repr(tcx)).as_slice());
}
};
// Upcast to the trait in question and extract out the substitutions.
let upcast_trait_ref = traits::upcast(ccx.tcx(), object_trait_ref.clone(), trait_id).unwrap();
let object_substs = upcast_trait_ref.substs().clone().erase_regions();
debug!("trans_object_shim: object_substs={}", object_substs.repr(tcx));
// Lookup the type of this method as deeclared in the trait and apply substitutions.
let method_ty = match ty::trait_item(tcx, trait_id, method_offset_in_trait) {
ty::MethodTraitItem(method) => method,
ty::TypeTraitItem(_) => {
tcx.sess.bug("can't create a method shim for an associated type")
}
};
let fty = method_ty.fty.subst(tcx, &object_substs);
let fty = tcx.mk_bare_fn(fty);
debug!("trans_object_shim: fty={}", fty.repr(tcx));
//
let method_bare_fn_ty =
ty::mk_bare_fn(tcx, None, fty);
let function_name =
link::mangle_internal_name_by_type_and_seq(ccx, method_bare_fn_ty, "object_shim");
let llfn =
decl_internal_rust_fn(ccx, method_bare_fn_ty, function_name.as_slice());
//
let block_arena = TypedArena::new();
let empty_substs = Substs::trans_empty();
let fcx = new_fn_ctxt(ccx,
llfn,
ast::DUMMY_NODE_ID,
false,
fty.sig.0.output,
&empty_substs,
None,
&block_arena);
let mut bcx = init_function(&fcx, false, fty.sig.0.output);
// the first argument (`self`) will be a trait object
let llobject = get_param(fcx.llfn, fcx.arg_pos(0) as u32);
debug!("trans_object_shim: llobject={}",
bcx.val_to_string(llobject));
// the remaining arguments will be, well, whatever they are
let llargs: Vec<_> =
fty.sig.0.inputs[1..].iter()
.enumerate()
.map(|(i, _)| {
let llarg = get_param(fcx.llfn, fcx.arg_pos(i+1) as u32);
debug!("trans_object_shim: input #{} == {}",
i, bcx.val_to_string(llarg));
llarg
})
.collect();
assert!(!fcx.needs_ret_allocas);
let dest =
fcx.llretslotptr.get().map(
|_| expr::SaveIn(fcx.get_ret_slot(bcx, fty.sig.0.output, "ret_slot")));
let method_offset_in_vtable =
traits::get_vtable_index_of_object_method(bcx.tcx(),
object_trait_ref.clone(),
trait_id,
method_offset_in_trait);
debug!("trans_object_shim: method_offset_in_vtable={}",
method_offset_in_vtable);
bcx = trans_call_inner(bcx,
None,
method_bare_fn_ty,
|bcx, _| trans_trait_callee_from_llval(bcx,
method_bare_fn_ty,
method_offset_in_vtable,
llobject),
ArgVals(llargs.as_slice()),
dest).bcx;
finish_fn(&fcx, bcx, fty.sig.0.output);
llfn
}
/// Creates a returns a dynamic vtable for the given type and vtable origin.
/// This is used only for objects.
///
@ -560,6 +695,14 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let llfn = vec![trans_fn_pointer_shim(bcx.ccx(), bare_fn_ty)];
llfn.into_iter()
}
traits::VtableObject(ref data) => {
// this would imply that the Self type being erased is
// an object type; this cannot happen because we
// cannot cast an unsized type into a trait object
bcx.sess().bug(
format!("cannot get vtable for an object type: {}",
data.repr(bcx.tcx())).as_slice());
}
traits::VtableParam => {
bcx.sess().bug(
format!("resolved vtable for {} to bad vtable {} in trans",

@ -9,7 +9,6 @@
// except according to those terms.
use check::{FnCtxt, structurally_resolved_type};
use middle::subst::{FnSpace, SelfSpace};
use middle::traits::{mod, ObjectSafetyViolation, MethodViolationCode};
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;