Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the Fn trait for bare fn pointers in the compiler #19449

Merged
merged 3 commits into from
Dec 4, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 42 additions & 38 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,48 +833,52 @@ impl<F,A,R> FnOnce<A,R> for F
}
}


impl<Result> Fn<(),Result> for extern "Rust" fn() -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, _args: ()) -> Result {
(*self)()
#[cfg(stage0)]
mod fn_impls {
use super::Fn;

impl<Result> Fn<(),Result> for extern "Rust" fn() -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, _args: ()) -> Result {
(*self)()
}
}
}

impl<Result,A0> Fn<(A0,),Result> for extern "Rust" fn(A0) -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, args: (A0,)) -> Result {
let (a0,) = args;
(*self)(a0)
impl<Result,A0> Fn<(A0,),Result> for extern "Rust" fn(A0) -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, args: (A0,)) -> Result {
let (a0,) = args;
(*self)(a0)
}
}
}

macro_rules! def_fn(
($($args:ident)*) => (
impl<Result$(,$args)*>
Fn<($($args,)*),Result>
for extern "Rust" fn($($args: $args,)*) -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, args: ($($args,)*)) -> Result {
let ($($args,)*) = args;
(*self)($($args,)*)
macro_rules! def_fn(
($($args:ident)*) => (
impl<Result$(,$args)*>
Fn<($($args,)*),Result>
for extern "Rust" fn($($args: $args,)*) -> Result {
#[allow(non_snake_case)]
extern "rust-call" fn call(&self, args: ($($args,)*)) -> Result {
let ($($args,)*) = args;
(*self)($($args,)*)
}
}
}
)
)
)

def_fn!(A0 A1)
def_fn!(A0 A1 A2)
def_fn!(A0 A1 A2 A3)
def_fn!(A0 A1 A2 A3 A4)
def_fn!(A0 A1 A2 A3 A4 A5)
def_fn!(A0 A1 A2 A3 A4 A5 A6)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15)
def_fn!(A0 A1)
def_fn!(A0 A1 A2)
def_fn!(A0 A1 A2 A3)
def_fn!(A0 A1 A2 A3 A4)
def_fn!(A0 A1 A2 A3 A4 A5)
def_fn!(A0 A1 A2 A3 A4 A5 A6)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14)
def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15)
}
18 changes: 12 additions & 6 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,21 @@ pub enum Vtable<'tcx, N> {
/// Vtable identifying a particular impl.
VtableImpl(VtableImplData<'tcx, N>),

/// Vtable automatically generated for an unboxed closure. The def
/// ID is the ID of the closure expression. This is a `VtableImpl`
/// in spirit, but the impl is generated by the compiler and does
/// not appear in the source.
VtableUnboxedClosure(ast::DefId, subst::Substs<'tcx>),

/// Successful resolution to an obligation provided by the caller
/// for some type parameter.
VtableParam(VtableParamData<'tcx>),

/// Successful resolution for a builtin trait.
VtableBuiltin(VtableBuiltinData<N>),

/// Vtable automatically generated for an unboxed closure. The def
/// ID is the ID of the closure expression. This is a `VtableImpl`
/// in spirit, but the impl is generated by the compiler and does
/// not appear in the source.
VtableUnboxedClosure(ast::DefId, subst::Substs<'tcx>),

/// Same as above, but for a fn pointer type with the given signature.
VtableFnPointer(ty::Ty<'tcx>),
}

/// Identifies a particular impl in the source, along with a set of
Expand Down Expand Up @@ -322,6 +325,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
pub fn iter_nested(&self) -> Items<N> {
match *self {
VtableImpl(ref i) => i.iter_nested(),
VtableFnPointer(..) => (&[]).iter(),
VtableUnboxedClosure(..) => (&[]).iter(),
VtableParam(_) => (&[]).iter(),
VtableBuiltin(ref i) => i.iter_nested(),
Expand All @@ -331,6 +335,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
pub fn map_nested<M>(&self, op: |&N| -> M) -> Vtable<'tcx, M> {
match *self {
VtableImpl(ref i) => VtableImpl(i.map_nested(op)),
VtableFnPointer(ref sig) => VtableFnPointer((*sig).clone()),
VtableUnboxedClosure(d, ref s) => VtableUnboxedClosure(d, s.clone()),
VtableParam(ref p) => VtableParam((*p).clone()),
VtableBuiltin(ref i) => VtableBuiltin(i.map_nested(op)),
Expand All @@ -340,6 +345,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
pub fn map_move_nested<M>(self, op: |N| -> M) -> Vtable<'tcx, M> {
match self {
VtableImpl(i) => VtableImpl(i.map_move_nested(op)),
VtableFnPointer(sig) => VtableFnPointer(sig),
VtableUnboxedClosure(d, s) => VtableUnboxedClosure(d, s),
VtableParam(p) => VtableParam(p),
VtableBuiltin(i) => VtableBuiltin(i.map_move_nested(op)),
Expand Down
145 changes: 128 additions & 17 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use super::{SelectionError, Unimplemented, Overflow,
OutputTypeParameterMismatch};
use super::{Selection};
use super::{SelectionResult};
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure};
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure, VtableFnPointer};
use super::{VtableImplData, VtableParamData, VtableBuiltinData};
use super::{util};

Expand All @@ -36,7 +36,7 @@ use middle::ty_fold::TypeFoldable;
use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;
use syntax::ast;
use syntax::{abi, ast};
use util::common::ErrorReported;
use util::ppaux::Repr;

Expand Down Expand Up @@ -131,7 +131,15 @@ enum Candidate<'tcx> {
BuiltinCandidate(ty::BuiltinBound),
ParamCandidate(VtableParamData<'tcx>),
ImplCandidate(ast::DefId),

/// Implementation of a `Fn`-family trait by one of the
/// anonymous types generated for a `||` expression.
UnboxedClosureCandidate(/* closure */ ast::DefId, Substs<'tcx>),

/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int)->int`)
FnPointerCandidate,

ErrorCandidate,
}

Expand Down Expand Up @@ -917,7 +925,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
None => {
// For the time being, we ignore user-defined impls for builtin-bounds.
// (And unboxed candidates only apply to the Fn/FnMut/etc traits.)
try!(self.assemble_unboxed_candidates(obligation, &mut candidates));
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));
}
}
Expand Down Expand Up @@ -968,20 +977,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Note: the type parameters on an unboxed closure candidate are modeled as *output* type
/// parameters and hence do not affect whether this trait is a match or not. They will be
/// unified during the confirmation step.
fn assemble_unboxed_candidates(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
fn assemble_unboxed_closure_candidates(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
let tcx = self.tcx();
let kind = if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_trait() {
ty::FnUnboxedClosureKind
} else if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_mut_trait() {
ty::FnMutUnboxedClosureKind
} else if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_once_trait() {
ty::FnOnceUnboxedClosureKind
} else {
return Ok(()); // not a fn trait, ignore
let kind = match self.fn_family_trait_kind(obligation.trait_ref.def_id) {
Some(k) => k,
None => { return Ok(()); }
};

let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
Expand Down Expand Up @@ -1015,6 +1018,44 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(())
}

/// Implement one of the `Fn()` family for a fn pointer.
fn assemble_fn_pointer_candidates(&mut self,
obligation: &Obligation<'tcx>,
candidates: &mut CandidateSet<'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.trait_ref.def_id) != self.tcx().lang_items.fn_trait() {
return Ok(());
}

let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
match self_ty.sty {
ty::ty_infer(..) => {
candidates.ambiguous = true; // could wind up being a fn() type
}

// provide an impl, but only for suitable `fn` pointers
ty::ty_bare_fn(ty::BareFnTy {
fn_style: ast::NormalFn,
abi: abi::Rust,
sig: ty::FnSig {
inputs: _,
output: ty::FnConverging(_),
variadic: false
}
}) => {
candidates.vec.push(FnPointerCandidate);
}

_ => { }
}

Ok(())
}

/// Search for impls that might apply to `obligation`.
fn assemble_candidates_from_impls(&mut self,
obligation: &Obligation<'tcx>,
Expand Down Expand Up @@ -1551,6 +1592,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
try!(self.confirm_unboxed_closure_candidate(obligation, closure_def_id, &substs));
Ok(VtableUnboxedClosure(closure_def_id, substs))
}

FnPointerCandidate => {
let fn_type =
try!(self.confirm_fn_pointer_candidate(obligation));
Ok(VtableFnPointer(fn_type))
}
}
}

Expand Down Expand Up @@ -1646,6 +1693,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested: impl_obligations }
}

fn confirm_fn_pointer_candidate(&mut self,
obligation: &Obligation<'tcx>)
-> Result<ty::Ty<'tcx>,SelectionError<'tcx>>
{
debug!("confirm_fn_pointer_candidate({})",
obligation.repr(self.tcx()));

let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
let sig = match self_ty.sty {
ty::ty_bare_fn(ty::BareFnTy {
fn_style: ast::NormalFn,
abi: abi::Rust,
ref sig
}) => {
sig
}
_ => {
self.tcx().sess.span_bug(
obligation.cause.span,
format!("Fn pointer candidate for inappropriate self type: {}",
self_ty.repr(self.tcx())).as_slice());
}
};

let arguments_tuple = ty::mk_tup(self.tcx(), sig.inputs.to_vec());
let output_type = sig.output.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you cloned the sig above, why not just move inputs and output out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, holdover from an older version where I needed the clone for something

let substs =
Substs::new_trait(
vec![arguments_tuple, output_type],
vec![],
vec![],
self_ty);
let trait_ref = Rc::new(ty::TraitRef {
def_id: obligation.trait_ref.def_id,
substs: substs,
});

let () =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the let () =?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to assert that I wasn't dropping any return value on the floor, I think (some of the confirm methods product a result you ought to use...)

try!(self.confirm(obligation.cause,
obligation.trait_ref.clone(),
trait_ref));

Ok(self_ty)
}

fn confirm_unboxed_closure_candidate(&mut self,
obligation: &Obligation<'tcx>,
closure_def_id: ast::DefId,
Expand Down Expand Up @@ -1964,6 +2056,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
util::obligations_for_generics(self.tcx(), cause, recursion_depth,
&bounds, &impl_substs.types)
}

fn fn_family_trait_kind(&self,
trait_def_id: ast::DefId)
-> Option<ty::UnboxedClosureKind>
{
let tcx = self.tcx();
if Some(trait_def_id) == tcx.lang_items.fn_trait() {
Some(ty::FnUnboxedClosureKind)
} else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() {
Some(ty::FnMutUnboxedClosureKind)
} else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
Some(ty::FnOnceUnboxedClosureKind)
} else {
None
}
}
}

impl<'tcx> Repr<'tcx> for Candidate<'tcx> {
Expand All @@ -1972,7 +2080,10 @@ impl<'tcx> Repr<'tcx> for Candidate<'tcx> {
ErrorCandidate => format!("ErrorCandidate"),
BuiltinCandidate(b) => format!("BuiltinCandidate({})", b),
UnboxedClosureCandidate(c, ref s) => {
format!("MatchedUnboxedClosureCandidate({},{})", c, s.repr(tcx))
format!("UnboxedClosureCandidate({},{})", c, s.repr(tcx))
}
FnPointerCandidate => {
format!("FnPointerCandidate")
}
ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)),
ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)),
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> {
d.repr(tcx),
s.repr(tcx)),

super::VtableFnPointer(ref d) =>
format!("VtableFnPointer({})",
d.repr(tcx)),

super::VtableParam(ref v) =>
format!("VtableParam({})", v.repr(tcx)),

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/ty_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
traits::VtableUnboxedClosure(d, ref s) => {
traits::VtableUnboxedClosure(d, s.fold_with(folder))
}
traits::VtableFnPointer(ref d) => {
traits::VtableFnPointer(d.fold_with(folder))
}
traits::VtableParam(ref p) => traits::VtableParam(p.fold_with(folder)),
traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)),
}
Expand Down
Loading