-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}; | ||
|
||
|
@@ -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; | ||
|
||
|
@@ -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, | ||
} | ||
|
||
|
@@ -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)); | ||
} | ||
} | ||
|
@@ -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()); | ||
|
@@ -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>, | ||
|
@@ -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)) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -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(); | ||
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 () = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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, | ||
|
@@ -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> { | ||
|
@@ -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)), | ||
|
There was a problem hiding this comment.
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
andoutput
out?There was a problem hiding this comment.
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