Skip to content

Commit

Permalink
Make the Fn traits inherit from one another and remove the bridging
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikomatsakis committed Mar 23, 2015
1 parent b0aad7d commit 3760113
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 202 deletions.
24 changes: 24 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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")]
Expand All @@ -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>
{
Expand All @@ -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>
{
Expand Down
29 changes: 28 additions & 1 deletion src/libcore/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;

Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/middle/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 3 additions & 5 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
}
Expand All @@ -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(());
}

Expand Down
19 changes: 18 additions & 1 deletion src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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> {
Expand Down
31 changes: 23 additions & 8 deletions src/librustc_trans/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,24 +264,36 @@ 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 => { }
}

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) =
Expand All @@ -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
Expand Down Expand Up @@ -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<_> =
Expand All @@ -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
}
Expand Down
Loading

0 comments on commit 3760113

Please sign in to comment.