Make the Fn traits inherit from one another and remove the bridging

impls.

This requires:

1. modifying trait selection a bit so that when we synthesize impls for
   fn pointers and closures;
2. adding code to trans so that we can synthesize a `FnMut`/`FnOnce`
   impl for a `Fn` closure and so forth.
This commit is contained in:
Niko Matsakis 2015-02-15 15:09:26 -05:00
parent b0aad7dd4f
commit 37601131a0
12 changed files with 495 additions and 202 deletions

View File

@ -1136,6 +1136,7 @@ impl<'a, T: ?Sized> DerefMut for &'a mut T {
#[lang="fn"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_paren_sugar]
#[cfg(stage0)]
pub trait Fn<Args> {
/// The returned type after the call operator is used.
type Output;
@ -1144,10 +1145,21 @@ pub trait Fn<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
/// A version of the call operator that takes an immutable receiver.
#[lang="fn"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_paren_sugar]
#[cfg(not(stage0))]
pub trait Fn<Args> : FnMut<Args> {
/// This is called when the call operator is used.
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
/// A version of the call operator that takes a mutable receiver.
#[lang="fn_mut"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_paren_sugar]
#[cfg(stage0)]
pub trait FnMut<Args> {
/// The returned type after the call operator is used.
type Output;
@ -1156,6 +1168,16 @@ pub trait FnMut<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
/// A version of the call operator that takes a mutable receiver.
#[lang="fn_mut"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_paren_sugar]
#[cfg(not(stage0))]
pub trait FnMut<Args> : FnOnce<Args> {
/// This is called when the call operator is used.
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
/// A version of the call operator that takes a by-value receiver.
#[lang="fn_once"]
#[stable(feature = "rust1", since = "1.0.0")]
@ -1168,6 +1190,7 @@ pub trait FnOnce<Args> {
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
#[cfg(stage0)]
impl<F: ?Sized, A> FnMut<A> for F
where F : Fn<A>
{
@ -1178,6 +1201,7 @@ impl<F: ?Sized, A> FnMut<A> for F
}
}
#[cfg(stage0)]
impl<F,A> FnOnce<A> for F
where F : FnMut<A>
{

View File

@ -29,7 +29,7 @@ use iter::{Map, Iterator, IteratorExt, DoubleEndedIterator};
use marker::Sized;
use mem;
use num::Int;
use ops::{Fn, FnMut};
use ops::{Fn, FnMut, FnOnce};
use option::Option::{self, None, Some};
use raw::{Repr, Slice};
use result::Result::{self, Ok, Err};
@ -524,6 +524,7 @@ delegate_iter!{exact u8 : Bytes<'a>}
#[derive(Copy, Clone)]
struct BytesDeref;
#[cfg(stage0)]
impl<'a> Fn<(&'a u8,)> for BytesDeref {
type Output = u8;
@ -533,6 +534,32 @@ impl<'a> Fn<(&'a u8,)> for BytesDeref {
}
}
#[cfg(not(stage0))]
impl<'a> Fn<(&'a u8,)> for BytesDeref {
#[inline]
extern "rust-call" fn call(&self, (ptr,): (&'a u8,)) -> u8 {
*ptr
}
}
#[cfg(not(stage0))]
impl<'a> FnMut<(&'a u8,)> for BytesDeref {
#[inline]
extern "rust-call" fn call_mut(&mut self, (ptr,): (&'a u8,)) -> u8 {
Fn::call(&*self, (ptr,))
}
}
#[cfg(not(stage0))]
impl<'a> FnOnce<(&'a u8,)> for BytesDeref {
type Output = u8;
#[inline]
extern "rust-call" fn call_once(self, (ptr,): (&'a u8,)) -> u8 {
Fn::call(&self, (ptr,))
}
}
/// An iterator over the substrings of a string, separated by `sep`.
struct CharSplits<'a, P: Pattern<'a>> {
/// The slice remaining to be iterated

View File

@ -789,10 +789,13 @@ fn confirm_callable_candidate<'cx,'tcx>(
obligation.repr(tcx),
fn_sig.repr(tcx));
// the `Output` associated type is declared on `FnOnce`
let fn_once_def_id = tcx.lang_items.fn_once_trait().unwrap();
// Note: we unwrap the binder here but re-create it below (1)
let ty::Binder((trait_ref, ret_type)) =
util::closure_trait_ref_and_return_type(tcx,
obligation.predicate.trait_ref.def_id,
fn_once_def_id,
obligation.predicate.trait_ref.self_ty(),
fn_sig,
flag);

View File

@ -1071,7 +1071,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
match self.closure_typer.closure_kind(closure_def_id) {
Some(closure_kind) => {
debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
if closure_kind == kind {
if closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone()));
}
}
@ -1090,10 +1090,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
// We provide a `Fn` impl for fn pointers. There is no need to provide
// the other traits (e.g. `FnMut`) since those are provided by blanket
// impls.
if Some(obligation.predicate.def_id()) != self.tcx().lang_items.fn_trait() {
// We provide impl of all fn traits for fn pointers.
if self.tcx().lang_items.fn_trait_kind(obligation.predicate.def_id()).is_none() {
return Ok(());
}

View File

@ -2461,8 +2461,11 @@ pub struct ItemSubsts<'tcx> {
pub substs: Substs<'tcx>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub enum ClosureKind {
// Warning: Ordering is significant here! The ordering is chosen
// because the trait Fn is a subtrait of FnMut and so in turn, and
// hence we order it so that Fn < FnMut < FnOnce.
FnClosureKind,
FnMutClosureKind,
FnOnceClosureKind,
@ -2484,6 +2487,20 @@ impl ClosureKind {
Err(err) => cx.sess.fatal(&err[..]),
}
}
/// True if this a type that impls this closure kind
/// must also implement `other`.
pub fn extends(self, other: ty::ClosureKind) -> bool {
match (self, other) {
(FnClosureKind, FnClosureKind) => true,
(FnClosureKind, FnMutClosureKind) => true,
(FnClosureKind, FnOnceClosureKind) => true,
(FnMutClosureKind, FnMutClosureKind) => true,
(FnMutClosureKind, FnOnceClosureKind) => true,
(FnOnceClosureKind, FnOnceClosureKind) => true,
_ => false,
}
}
}
pub trait ClosureTyper<'tcx> {

View File

@ -264,14 +264,29 @@ fn trans_fn_ref_with_substs_to_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
/// but for the bare function type given.
pub fn trans_fn_pointer_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
closure_kind: ty::ClosureKind,
bare_fn_ty: Ty<'tcx>)
-> ValueRef
{
let _icx = push_ctxt("trans_fn_pointer_shim");
let tcx = ccx.tcx();
// Normalize the type for better caching.
let bare_fn_ty = common::erase_regions(tcx, &bare_fn_ty);
match ccx.fn_pointer_shims().borrow().get(&bare_fn_ty) {
// If this is an impl of `Fn` or `FnMut` trait, the receiver is `&self`.
let is_by_ref = match closure_kind {
ty::FnClosureKind | ty::FnMutClosureKind => true,
ty::FnOnceClosureKind => false,
};
let bare_fn_ty_maybe_ref = if is_by_ref {
ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), bare_fn_ty)
} else {
bare_fn_ty
};
// Check if we already trans'd this shim.
match ccx.fn_pointer_shims().borrow().get(&bare_fn_ty_maybe_ref) {
Some(&llval) => { return llval; }
None => { }
}
@ -279,9 +294,6 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
debug!("trans_fn_pointer_shim(bare_fn_ty={})",
bare_fn_ty.repr(tcx));
// This is an impl of `Fn` trait, so receiver is `&self`.
let bare_fn_ty_ref = ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), bare_fn_ty);
// Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`,
// which is the fn pointer, and `args`, which is the arguments tuple.
let (opt_def_id, sig) =
@ -306,7 +318,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
unsafety: ast::Unsafety::Normal,
abi: synabi::RustCall,
sig: ty::Binder(ty::FnSig {
inputs: vec![bare_fn_ty_ref,
inputs: vec![bare_fn_ty_maybe_ref,
tuple_input_ty],
output: sig.output,
variadic: false
@ -337,8 +349,11 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
let mut bcx = init_function(&fcx, false, sig.output);
// the first argument (`self`) will be ptr to the the fn pointer
let llfnpointer =
Load(bcx, get_param(fcx.llfn, fcx.arg_pos(0) as u32));
let llfnpointer = if is_by_ref {
Load(bcx, get_param(fcx.llfn, fcx.arg_pos(0) as u32))
} else {
get_param(fcx.llfn, fcx.arg_pos(0) as u32)
};
// the remaining arguments will be the untupled values
let llargs: Vec<_> =
@ -361,7 +376,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty, llfn);
ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn);
llfn
}

View File

@ -8,24 +8,27 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use back::link::mangle_internal_name_by_path_and_seq;
use llvm::ValueRef;
use arena::TypedArena;
use back::link::{self, mangle_internal_name_by_path_and_seq};
use llvm::{ValueRef, get_param};
use middle::mem_categorization::Typer;
use trans::adt;
use trans::base::*;
use trans::build::*;
use trans::cleanup::{CleanupMethods, ScopeId};
use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
use trans::common::*;
use trans::datum::{Datum, rvalue_scratch_datum};
use trans::datum::{Rvalue, ByValue};
use trans::debuginfo;
use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue, ByValue};
use trans::debuginfo::{self, DebugLoc};
use trans::expr;
use trans::monomorphize::{self, MonoId};
use trans::type_of::*;
use middle::ty::{self, ClosureTyper};
use middle::subst::{Substs};
use session::config::FullDebugInfo;
use util::ppaux::Repr;
use syntax::abi::RustCall;
use syntax::ast;
use syntax::ast_util;
@ -239,11 +242,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
// Create the closure.
for (i, freevar) in freevars.iter().enumerate() {
let datum = expr::trans_local_var(bcx, freevar.def);
let upvar_slot_dest = adt::trans_field_ptr(bcx,
&*repr,
dest_addr,
0,
i);
let upvar_slot_dest = adt::trans_field_ptr(bcx, &*repr, dest_addr, 0, i);
let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
closure_expr_id: id };
match tcx.upvar_capture(upvar_id).unwrap() {
@ -259,3 +258,186 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
Some(bcx)
}
pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
closure_def_id: ast::DefId,
substs: Substs<'tcx>,
node: ExprOrMethodCall,
param_substs: &'tcx Substs<'tcx>,
trait_closure_kind: ty::ClosureKind)
-> ValueRef
{
// The substitutions should have no type parameters remaining
// after passing through fulfill_obligation
let llfn = callee::trans_fn_ref_with_substs(ccx,
closure_def_id,
node,
param_substs,
substs.clone()).val;
// If the closure is a Fn closure, but a FnOnce is needed (etc),
// then adapt the self type
let closure_kind = ccx.tcx().closure_kind(closure_def_id);
trans_closure_adapter_shim(ccx,
closure_def_id,
substs,
closure_kind,
trait_closure_kind,
llfn)
}
fn trans_closure_adapter_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
closure_def_id: ast::DefId,
substs: Substs<'tcx>,
llfn_closure_kind: ty::ClosureKind,
trait_closure_kind: ty::ClosureKind,
llfn: ValueRef)
-> ValueRef
{
let _icx = push_ctxt("trans_closure_adapter_shim");
let tcx = ccx.tcx();
debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
trait_closure_kind={:?}, \
llfn={})",
llfn_closure_kind,
trait_closure_kind,
ccx.tn().val_to_string(llfn));
match (llfn_closure_kind, trait_closure_kind) {
(ty::FnClosureKind, ty::FnClosureKind) |
(ty::FnMutClosureKind, ty::FnMutClosureKind) |
(ty::FnOnceClosureKind, ty::FnOnceClosureKind) => {
// No adapter needed.
llfn
}
(ty::FnClosureKind, ty::FnMutClosureKind) => {
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
// `fn(&mut self, ...)`. In fact, at trans time, these are
// basically the same thing, so we can just return llfn.
llfn
}
(ty::FnClosureKind, ty::FnOnceClosureKind) |
(ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
// self, ...)`. We want a `fn(self, ...)`. We can produce
// this by doing something like:
//
// fn call_once(self, ...) { call_mut(&self, ...) }
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
//
// These are both the same at trans time.
trans_fn_once_adapter_shim(ccx, closure_def_id, substs, llfn)
}
_ => {
tcx.sess.bug(&format!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
llfn_closure_kind,
trait_closure_kind));
}
}
}
fn trans_fn_once_adapter_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
closure_def_id: ast::DefId,
substs: Substs<'tcx>,
llreffn: ValueRef)
-> ValueRef
{
debug!("trans_fn_once_adapter_shim(closure_def_id={}, substs={}, llreffn={})",
closure_def_id.repr(ccx.tcx()),
substs.repr(ccx.tcx()),
ccx.tn().val_to_string(llreffn));
let tcx = ccx.tcx();
let typer = NormalizingClosureTyper::new(tcx);
// Find a version of the closure type. Substitute static for the
// region since it doesn't really matter.
let substs = tcx.mk_substs(substs);
let closure_ty = ty::mk_closure(tcx, closure_def_id, substs);
let ref_closure_ty = ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), closure_ty);
// Make a version with the type of by-ref closure.
let ty::ClosureTy { unsafety, abi, mut sig } = typer.closure_type(closure_def_id, substs);
sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
let llref_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
abi: abi,
sig: sig.clone() });
let llref_fn_ty = ty::mk_bare_fn(tcx, None, llref_bare_fn_ty);
debug!("trans_fn_once_adapter_shim: llref_fn_ty={}",
llref_fn_ty.repr(tcx));
// Make a version of the closure type with the same arguments, but
// with argument #0 being by value.
assert_eq!(abi, RustCall);
sig.0.inputs[0] = closure_ty;
let llonce_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
abi: abi,
sig: sig });
let llonce_fn_ty = ty::mk_bare_fn(tcx, None, llonce_bare_fn_ty);
// Create the by-value helper.
let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
let lloncefn = decl_internal_rust_fn(ccx, llonce_fn_ty, &function_name);
let sig = ty::erase_late_bound_regions(tcx, &llonce_bare_fn_ty.sig);
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
block_arena = TypedArena::new();
fcx = new_fn_ctxt(ccx,
lloncefn,
ast::DUMMY_NODE_ID,
false,
sig.output,
substs,
None,
&block_arena);
let mut bcx = init_function(&fcx, false, sig.output);
// the first argument (`self`) will be the (by value) closure env.
let self_scope = fcx.push_custom_cleanup_scope();
let self_scope_id = CustomScope(self_scope);
let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty);
let llself = get_param(lloncefn, fcx.arg_pos(0) as u32);
let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode));
let env_datum = unpack_datum!(bcx,
env_datum.to_lvalue_datum_in_scope(bcx, "self",
self_scope_id));
debug!("trans_fn_once_adapter_shim: env_datum={}",
bcx.val_to_string(env_datum.val));
// the remaining arguments will be packed up in a tuple.
let input_tys = match sig.inputs[1].sty {
ty::ty_tup(ref tys) => &**tys,
_ => bcx.sess().bug(&format!("trans_fn_once_adapter_shim: not rust-call! \
closure_def_id={}",
closure_def_id.repr(tcx)))
};
let llargs: Vec<_> =
input_tys.iter()
.enumerate()
.map(|(i, _)| get_param(lloncefn, fcx.arg_pos(i+1) as u32))
.collect();
let dest =
fcx.llretslotptr.get().map(
|_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
let callee_data = TraitItem(MethodData { llfn: llreffn,
llself: env_datum.val });
bcx = callee::trans_call_inner(bcx,
DebugLoc::None,
llref_fn_ty,
|bcx, _| Callee { bcx: bcx, data: callee_data },
ArgVals(&llargs),
dest).bcx;
fcx.pop_custom_cleanup_scope(self_scope);
finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
lloncefn
}

View File

@ -17,11 +17,13 @@ use middle::subst::Substs;
use middle::subst::VecPerParamSpace;
use middle::subst;
use middle::traits;
use middle::ty::ClosureTyper;
use trans::base::*;
use trans::build::*;
use trans::callee::*;
use trans::callee;
use trans::cleanup;
use trans::closure;
use trans::common::*;
use trans::consts;
use trans::datum::*;
@ -358,19 +360,21 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
traits::VtableClosure(closure_def_id, substs) => {
// The substitutions should have no type parameters remaining
// after passing through fulfill_obligation
let llfn = trans_fn_ref_with_substs(bcx.ccx(),
closure_def_id,
MethodCallKey(method_call),
bcx.fcx.param_substs,
substs).val;
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = closure::trans_closure_method(bcx.ccx(),
closure_def_id,
substs,
MethodCallKey(method_call),
bcx.fcx.param_substs,
trait_closure_kind);
Callee {
bcx: bcx,
data: Fn(llfn),
}
}
traits::VtableFnPointer(fn_ty) => {
let llfn = trans_fn_pointer_shim(bcx.ccx(), fn_ty);
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = trans_fn_pointer_shim(bcx.ccx(), trait_closure_kind, fn_ty);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableObject(ref data) => {
@ -645,9 +649,6 @@ pub fn trans_object_shim<'a, 'tcx>(
assert!(!fcx.needs_ret_allocas);
let sig =
ty::erase_late_bound_regions(bcx.tcx(), &fty.sig);
let dest =
fcx.llretslotptr.get().map(
|_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
@ -714,17 +715,18 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
emit_vtable_methods(ccx, id, substs, param_substs).into_iter()
}
traits::VtableClosure(closure_def_id, substs) => {
let llfn = trans_fn_ref_with_substs(
ccx,
closure_def_id,
ExprId(0),
param_substs,
substs).val;
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
let llfn = closure::trans_closure_method(ccx,
closure_def_id,
substs,
ExprId(0),
param_substs,
trait_closure_kind);
vec![llfn].into_iter()
}
traits::VtableFnPointer(bare_fn_ty) => {
vec![trans_fn_pointer_shim(ccx, bare_fn_ty)].into_iter()
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter()
}
traits::VtableObject(ref data) => {
// this would imply that the Self type being erased is

View File

@ -55,7 +55,7 @@ use middle::resolve_lifetime as rl;
use middle::privacy::{AllPublic, LastMod};
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
use middle::traits;
use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty};
use middle::ty::{self, RegionEscape, Ty};
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope};
use util::common::{ErrorReported, FN_OUTPUT_NAME};
@ -608,24 +608,16 @@ pub fn instantiate_poly_trait_ref<'tcx>(
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
-> ty::PolyTraitRef<'tcx>
{
let mut projections = Vec::new();
// The trait reference introduces a binding level here, so
// we need to shift the `rscope`. It'd be nice if we could
// do away with this rscope stuff and work this knowledge
// into resolve_lifetimes, as we do with non-omitted
// lifetimes. Oh well, not there yet.
let shifted_rscope = ShiftedRscope::new(rscope);
let trait_ref = instantiate_trait_ref(this, &shifted_rscope,
&ast_trait_ref.trait_ref,
None, self_ty, Some(&mut projections));
for projection in projections {
poly_projections.push(ty::Binder(projection));
}
ty::Binder(trait_ref)
let trait_ref = &ast_trait_ref.trait_ref;
let trait_def_id = trait_def_id(this, trait_ref);
ast_path_to_poly_trait_ref(this,
rscope,
trait_ref.path.span,
PathParamMode::Explicit,
trait_def_id,
self_ty,
trait_ref.path.segments.last().unwrap(),
poly_projections)
}
/// Instantiates the path for the given trait reference, assuming that it's
@ -634,31 +626,27 @@ pub fn instantiate_poly_trait_ref<'tcx>(
///
/// If the `projections` argument is `None`, then assoc type bindings like `Foo<T=X>`
/// are disallowed. Otherwise, they are pushed onto the vector given.
pub fn instantiate_trait_ref<'tcx>(
pub fn instantiate_mono_trait_ref<'tcx>(
this: &AstConv<'tcx>,
rscope: &RegionScope,
trait_ref: &ast::TraitRef,
impl_id: Option<ast::NodeId>,
self_ty: Option<Ty<'tcx>>,
projections: Option<&mut Vec<ty::ProjectionPredicate<'tcx>>>)
self_ty: Option<Ty<'tcx>>)
-> Rc<ty::TraitRef<'tcx>>
{
let trait_def_id = trait_def_id(this, trait_ref);
ast_path_to_mono_trait_ref(this,
rscope,
trait_ref.path.span,
PathParamMode::Explicit,
trait_def_id,
self_ty,
trait_ref.path.segments.last().unwrap())
}
fn trait_def_id<'tcx>(this: &AstConv<'tcx>, trait_ref: &ast::TraitRef) -> ast::DefId {
let path = &trait_ref.path;
match ::lookup_full_def(this.tcx(), path.span, trait_ref.ref_id) {
def::DefTrait(trait_def_id) => {
let trait_ref = ast_path_to_trait_ref(this,
rscope,
path.span,
PathParamMode::Explicit,
trait_def_id,
self_ty,
path.segments.last().unwrap(),
projections);
if let Some(id) = impl_id {
this.tcx().impl_trait_refs.borrow_mut().insert(id, trait_ref.clone());
}
trait_ref
}
def::DefTrait(trait_def_id) => trait_def_id,
_ => {
span_fatal!(this.tcx().sess, path.span, E0245, "`{}` is not a trait",
path.user_string(this.tcx()));
@ -676,24 +664,17 @@ fn object_path_to_poly_trait_ref<'a,'tcx>(
mut projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
-> ty::PolyTraitRef<'tcx>
{
// we are introducing a binder here, so shift the
// anonymous regions depth to account for that
let shifted_rscope = ShiftedRscope::new(rscope);
let mut tmp = Vec::new();
let trait_ref = ty::Binder(ast_path_to_trait_ref(this,
&shifted_rscope,
span,
param_mode,
trait_def_id,
None,
trait_segment,
Some(&mut tmp)));
projections.extend(tmp.into_iter().map(ty::Binder));
trait_ref
ast_path_to_poly_trait_ref(this,
rscope,
span,
param_mode,
trait_def_id,
None,
trait_segment,
projections)
}
fn ast_path_to_trait_ref<'a,'tcx>(
fn ast_path_to_poly_trait_ref<'a,'tcx>(
this: &AstConv<'tcx>,
rscope: &RegionScope,
span: Span,
@ -701,10 +682,78 @@ fn ast_path_to_trait_ref<'a,'tcx>(
trait_def_id: ast::DefId,
self_ty: Option<Ty<'tcx>>,
trait_segment: &ast::PathSegment,
mut projections: Option<&mut Vec<ty::ProjectionPredicate<'tcx>>>)
-> Rc<ty::TraitRef<'tcx>>
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
-> ty::PolyTraitRef<'tcx>
{
debug!("ast_path_to_trait_ref {:?}", trait_segment);
// The trait reference introduces a binding level here, so
// we need to shift the `rscope`. It'd be nice if we could
// do away with this rscope stuff and work this knowledge
// into resolve_lifetimes, as we do with non-omitted
// lifetimes. Oh well, not there yet.
let shifted_rscope = &ShiftedRscope::new(rscope);
let (substs, assoc_bindings) =
create_substs_for_ast_trait_ref(this,
shifted_rscope,
span,
param_mode,
trait_def_id,
self_ty,
trait_segment);
let poly_trait_ref = ty::Binder(Rc::new(ty::TraitRef::new(trait_def_id, substs)));
{
let converted_bindings =
assoc_bindings
.iter()
.filter_map(|binding| {
// specify type to assert that error was already reported in Err case:
let predicate: Result<_, ErrorReported> =
ast_type_binding_to_poly_projection_predicate(this,
poly_trait_ref.clone(),
self_ty,
binding);
predicate.ok() // ok to ignore Err() because ErrorReported (see above)
});
poly_projections.extend(converted_bindings);
}
poly_trait_ref
}
fn ast_path_to_mono_trait_ref<'a,'tcx>(this: &AstConv<'tcx>,
rscope: &RegionScope,
span: Span,
param_mode: PathParamMode,
trait_def_id: ast::DefId,
self_ty: Option<Ty<'tcx>>,
trait_segment: &ast::PathSegment)
-> Rc<ty::TraitRef<'tcx>>
{
let (substs, assoc_bindings) =
create_substs_for_ast_trait_ref(this,
rscope,
span,
param_mode,
trait_def_id,
self_ty,
trait_segment);
prohibit_projections(this.tcx(), &assoc_bindings);
Rc::new(ty::TraitRef::new(trait_def_id, substs))
}
fn create_substs_for_ast_trait_ref<'a,'tcx>(this: &AstConv<'tcx>,
rscope: &RegionScope,
span: Span,
param_mode: PathParamMode,
trait_def_id: ast::DefId,
self_ty: Option<Ty<'tcx>>,
trait_segment: &ast::PathSegment)
-> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>)
{
debug!("create_substs_for_ast_trait_ref(trait_segment={:?})",
trait_segment);
let trait_def = match this.get_trait_def(span, trait_def_id) {
Ok(trait_def) => trait_def,
Err(ErrorReported) => {
@ -752,34 +801,16 @@ fn ast_path_to_trait_ref<'a,'tcx>(
self_ty,
types,
regions);
let substs = this.tcx().mk_substs(substs);
let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs));
match projections {
None => {
prohibit_projections(this.tcx(), &assoc_bindings);
}
Some(ref mut v) => {
for binding in &assoc_bindings {
match ast_type_binding_to_projection_predicate(this, trait_ref.clone(),
self_ty, binding) {
Ok(pp) => { v.push(pp); }
Err(ErrorReported) => { }
}
}
}
}
trait_ref
(this.tcx().mk_substs(substs), assoc_bindings)
}
fn ast_type_binding_to_projection_predicate<'tcx>(
fn ast_type_binding_to_poly_projection_predicate<'tcx>(
this: &AstConv<'tcx>,
mut trait_ref: Rc<ty::TraitRef<'tcx>>,
mut trait_ref: ty::PolyTraitRef<'tcx>,
self_ty: Option<Ty<'tcx>>,
binding: &ConvertedBinding<'tcx>)
-> Result<ty::ProjectionPredicate<'tcx>, ErrorReported>
-> Result<ty::PolyProjectionPredicate<'tcx>, ErrorReported>
{
let tcx = this.tcx();
@ -800,14 +831,14 @@ fn ast_type_binding_to_projection_predicate<'tcx>(
// We want to produce `<B as SuperTrait<int>>::T == foo`.
// Simple case: X is defined in the current trait.
if this.trait_defines_associated_type_named(trait_ref.def_id, binding.item_name) {
return Ok(ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy {
trait_ref: trait_ref,
if this.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
return Ok(ty::Binder(ty::ProjectionPredicate { // <-------------------+
projection_ty: ty::ProjectionTy { // |
trait_ref: trait_ref.skip_binder().clone(), // Binder moved here --+
item_name: binding.item_name,
},
ty: binding.ty,
});
}));
}
// Otherwise, we have to walk through the supertraits to find
@ -820,17 +851,17 @@ fn ast_type_binding_to_projection_predicate<'tcx>(
let dummy_self_ty = ty::mk_infer(tcx, ty::FreshTy(0));
if self_ty.is_none() { // if converting for an object type
let mut dummy_substs = trait_ref.substs.clone();
assert!(dummy_substs.self_ty().is_none());
dummy_substs.types.push(SelfSpace, dummy_self_ty);
trait_ref = Rc::new(ty::TraitRef::new(trait_ref.def_id,
tcx.mk_substs(dummy_substs)));
let mut dummy_substs = trait_ref.skip_binder().substs.clone(); // binder moved here -+
assert!(dummy_substs.self_ty().is_none()); // |
dummy_substs.types.push(SelfSpace, dummy_self_ty); // |
trait_ref = ty::Binder(Rc::new(ty::TraitRef::new(trait_ref.def_id(), // <------------+
tcx.mk_substs(dummy_substs))));
}
try!(this.ensure_super_predicates(binding.span, trait_ref.def_id));
try!(this.ensure_super_predicates(binding.span, trait_ref.def_id()));
let mut candidates: Vec<ty::PolyTraitRef> =
traits::supertraits(tcx, trait_ref.to_poly_trait_ref())
traits::supertraits(tcx, trait_ref.clone())
.filter(|r| this.trait_defines_associated_type_named(r.def_id(), binding.item_name))
.collect();
@ -865,21 +896,13 @@ fn ast_type_binding_to_projection_predicate<'tcx>(
}
};
if ty::binds_late_bound_regions(tcx, &candidate) {
span_err!(tcx.sess, binding.span, E0219,
"associated type `{}` defined in higher-ranked supertrait `{}`",
token::get_name(binding.item_name),
candidate.user_string(tcx));
return Err(ErrorReported);
}
Ok(ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy {
trait_ref: candidate.0,
Ok(ty::Binder(ty::ProjectionPredicate { // <-------------------------+
projection_ty: ty::ProjectionTy { // |
trait_ref: candidate.skip_binder().clone(), // binder is moved up here --+
item_name: binding.item_name,
},
ty: binding.ty,
})
}))
}
fn ast_path_to_ty<'tcx>(
@ -1134,14 +1157,14 @@ fn qpath_to_ty<'tcx>(this: &AstConv<'tcx>,
debug!("qpath_to_ty: self_type={}", self_ty.repr(tcx));
let trait_ref = ast_path_to_trait_ref(this,
rscope,
span,
param_mode,
trait_def_id,
Some(self_ty),
trait_segment,
None);
let trait_ref =
ast_path_to_mono_trait_ref(this,
rscope,
span,
param_mode,
trait_def_id,
Some(self_ty),
trait_segment);
debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(tcx));

View File

@ -16,6 +16,7 @@ use astconv;
use middle::region;
use middle::subst;
use middle::ty::{self, ToPolyTraitRef, Ty};
use std::cmp;
use syntax::abi;
use syntax::ast;
use syntax::ast_util;
@ -109,15 +110,11 @@ fn deduce_expectations_from_expected_type<'a,'tcx>(
ty::ty_trait(ref object_type) => {
let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(),
fcx.tcx().types.err);
let expectations =
proj_bounds.iter()
.filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
.next();
match expectations {
Some((sig, kind)) => (Some(sig), Some(kind)),
None => (None, None)
}
let sig = proj_bounds.iter()
.filter_map(|pb| deduce_sig_from_projection(fcx, pb))
.next();
let kind = fcx.tcx().lang_items.fn_trait_kind(object_type.principal_def_id());
(sig, kind)
}
ty::ty_infer(ty::TyVar(vid)) => {
deduce_expectations_from_obligations(fcx, vid)
@ -136,7 +133,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
let fulfillment_cx = fcx.inh.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
let expected_sig_and_kind =
let expected_sig =
fulfillment_cx
.pending_obligations()
.iter()
@ -150,7 +147,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
ty::Predicate::Projection(ref proj_predicate) => {
let trait_ref = proj_predicate.to_poly_trait_ref();
self_type_matches_expected_vid(fcx, trait_ref, expected_vid)
.and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate))
.and_then(|_| deduce_sig_from_projection(fcx, proj_predicate))
}
_ => {
None
@ -159,14 +156,10 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
})
.next();
match expected_sig_and_kind {
Some((sig, kind)) => { return (Some(sig), Some(kind)); }
None => { }
}
// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur if there is a trait-reference
// like `F : Fn<A>`.
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let expected_kind =
fulfillment_cx
.pending_obligations()
@ -183,54 +176,61 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
.and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))
.and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id()))
})
.next();
.fold(None, pick_most_restrictive_closure_kind);
(None, expected_kind)
(expected_sig, expected_kind)
}
fn pick_most_restrictive_closure_kind(best: Option<ty::ClosureKind>,
cur: ty::ClosureKind)
-> Option<ty::ClosureKind>
{
match best {
None => Some(cur),
Some(best) => Some(cmp::min(best, cur))
}
}
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
/// everything we need to know about a closure.
fn deduce_expectations_from_projection<'a,'tcx>(
fn deduce_sig_from_projection<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
projection: &ty::PolyProjectionPredicate<'tcx>)
-> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
-> Option<ty::FnSig<'tcx>>
{
let tcx = fcx.tcx();
debug!("deduce_expectations_from_projection({})",
debug!("deduce_sig_from_projection({})",
projection.repr(tcx));
let trait_ref = projection.to_poly_trait_ref();
let kind = match tcx.lang_items.fn_trait_kind(trait_ref.def_id()) {
Some(k) => k,
None => { return None; }
};
debug!("found object type {:?}", kind);
if tcx.lang_items.fn_trait_kind(trait_ref.def_id()).is_none() {
return None;
}
let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(&arg_param_ty);
debug!("arg_param_ty {}", arg_param_ty.repr(tcx));
debug!("deduce_sig_from_projection: arg_param_ty {}", arg_param_ty.repr(tcx));
let input_tys = match arg_param_ty.sty {
ty::ty_tup(ref tys) => { (*tys).clone() }
_ => { return None; }
};
debug!("input_tys {}", input_tys.repr(tcx));
debug!("deduce_sig_from_projection: input_tys {}", input_tys.repr(tcx));
let ret_param_ty = projection.0.ty;
let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(&ret_param_ty);
debug!("ret_param_ty {}", ret_param_ty.repr(tcx));
debug!("deduce_sig_from_projection: ret_param_ty {}", ret_param_ty.repr(tcx));
let fn_sig = ty::FnSig {
inputs: input_tys,
output: ty::FnConverging(ret_param_ty),
variadic: false
};
debug!("fn_sig {}", fn_sig.repr(tcx));
debug!("deduce_sig_from_projection: fn_sig {}", fn_sig.repr(tcx));
return Some((fn_sig, kind));
Some(fn_sig)
}
fn self_type_matches_expected_vid<'a,'tcx>(

View File

@ -725,7 +725,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
};
// this closure doesn't implement the right kind of `Fn` trait
if closure_kind != kind {
if !closure_kind.extends(kind) {
continue;
}

View File

@ -784,14 +784,15 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
&enum_definition.variants);
},
ast::ItemDefaultImpl(_, ref ast_trait_ref) => {
let trait_ref = astconv::instantiate_trait_ref(&ccx.icx(&()),
&ExplicitRscope,
ast_trait_ref,
Some(it.id),
None,
None);
let trait_ref =
astconv::instantiate_mono_trait_ref(&ccx.icx(&()),
&ExplicitRscope,
ast_trait_ref,
None);
ty::record_trait_has_default_impl(tcx, trait_ref.def_id);
tcx.impl_trait_refs.borrow_mut().insert(it.id, trait_ref);
}
ast::ItemImpl(_, _,
ref generics,
@ -890,13 +891,14 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
}
}
if let Some(ref trait_ref) = *opt_trait_ref {
astconv::instantiate_trait_ref(&ccx.icx(&ty_predicates),
&ExplicitRscope,
trait_ref,
Some(it.id),
Some(selfty),
None);
if let Some(ref ast_trait_ref) = *opt_trait_ref {
let trait_ref =
astconv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates),
&ExplicitRscope,
ast_trait_ref,
Some(selfty));
tcx.impl_trait_refs.borrow_mut().insert(it.id, trait_ref);
}
enforce_impl_ty_params_are_constrained(tcx,