Auto merge of #21805 - nikomatsakis:closure-inference-refactor-1, r=eddyb

Currently, we only infer the kind of a closure based on the expected type or explicit annotation. If neither applies, we currently report an error. This pull request changes that case to defer the decision until we are able to analyze the actions of the closure: closures which mutate their environment require `FnMut`, closures which move out of their environment require `FnOnce`.

This PR is not the end of the story:

- It does not remove the explicit annotations nor disregard them. The latter is the logical next step to removing them (we'll need a snapshot before we can do anything anyhow). Disregarding explicit annotations might expose more bugs since right now all closures in libstd/rustc use explicit annotations or the expected type, so this inference never kicks in.
- The interaction with instantiating type parameter fallbacks leaves something to be desired. This is mostly just saying that the algorithm from https://github.com/rust-lang/rfcs/pull/213 needs to be implemented, which is a separate bug. There are some semi-subtle interactions though because not knowing whether a closure is `Fn` vs `FnMut` prevents us from resolving obligations like `F : FnMut(...)`, which can in turn prevent unification of some type parameters, which might (in turn) lead to undesired fallback. We can improve this situation however -- even if we don't know whether (or just how) `F : FnMut(..)` holds or not for some closure type `F`, we can still perform unification since we *do* know the argument and return types. Once kind inference is done, we can complete the `F : FnMut(..)` analysis -- which might yield an error if (e.g.) the `F` moves out of its environment. 

r? @nick29581
This commit is contained in:
bors 2015-02-01 13:01:57 +00:00
commit 0ab8d5dadd
37 changed files with 1205 additions and 419 deletions

View file

@ -139,10 +139,11 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
tag_table_adjustments = 0x51,
tag_table_moves_map = 0x52,
tag_table_capture_map = 0x53,
tag_table_closures = 0x54,
tag_table_upvar_capture_map = 0x55,
tag_table_capture_modes = 0x56,
tag_table_object_cast_map = 0x57,
tag_table_closure_tys = 0x54,
tag_table_closure_kinds = 0x55,
tag_table_upvar_capture_map = 0x56,
tag_table_capture_modes = 0x57,
tag_table_object_cast_map = 0x58,
}
static first_astencode_tag: uint = tag_ast as uint;
@ -225,10 +226,7 @@ pub struct LinkMeta {
pub crate_hash: Svh,
}
pub const tag_closures: uint = 0x95;
pub const tag_closure: uint = 0x96;
pub const tag_closure_type: uint = 0x97;
pub const tag_closure_kind: uint = 0x98;
// GAP 0x94...0x98
pub const tag_struct_fields: uint = 0x99;
pub const tag_struct_field: uint = 0x9a;

View file

@ -618,17 +618,6 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: ast::Visibility) {
rbml_w.end_tag();
}
fn encode_closure_kind(rbml_w: &mut Encoder, kind: ty::ClosureKind) {
rbml_w.start_tag(tag_closure_kind);
let ch = match kind {
ty::FnClosureKind => 'f',
ty::FnMutClosureKind => 'm',
ty::FnOnceClosureKind => 'o',
};
rbml_w.wr_str(&ch.to_string()[]);
rbml_w.end_tag();
}
fn encode_explicit_self(rbml_w: &mut Encoder,
explicit_self: &ty::ExplicitSelfCategory) {
rbml_w.start_tag(tag_item_trait_method_explicit_self);
@ -1843,24 +1832,6 @@ fn encode_macro_defs(rbml_w: &mut Encoder,
rbml_w.end_tag();
}
fn encode_closures<'a>(ecx: &'a EncodeContext, rbml_w: &'a mut Encoder) {
rbml_w.start_tag(tag_closures);
for (closure_id, closure) in ecx.tcx.closures.borrow().iter() {
if closure_id.krate != ast::LOCAL_CRATE {
continue
}
rbml_w.start_tag(tag_closure);
encode_def_id(rbml_w, *closure_id);
rbml_w.start_tag(tag_closure_type);
write_closure_type(ecx, rbml_w, &closure.closure_type);
rbml_w.end_tag();
encode_closure_kind(rbml_w, closure.kind);
rbml_w.end_tag();
}
rbml_w.end_tag();
}
fn encode_struct_field_attrs(rbml_w: &mut Encoder, krate: &ast::Crate) {
struct StructFieldVisitor<'a, 'b:'a> {
rbml_w: &'a mut Encoder<'b>,
@ -2069,7 +2040,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
native_lib_bytes: u64,
plugin_registrar_fn_bytes: u64,
macro_defs_bytes: u64,
closure_bytes: u64,
impl_bytes: u64,
misc_bytes: u64,
item_bytes: u64,
@ -2084,7 +2054,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
native_lib_bytes: 0,
plugin_registrar_fn_bytes: 0,
macro_defs_bytes: 0,
closure_bytes: 0,
impl_bytes: 0,
misc_bytes: 0,
item_bytes: 0,
@ -2154,11 +2123,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
encode_macro_defs(&mut rbml_w, krate);
stats.macro_defs_bytes = rbml_w.writer.tell().unwrap() - i;
// Encode the types of all closures in this crate.
i = rbml_w.writer.tell().unwrap();
encode_closures(&ecx, &mut rbml_w);
stats.closure_bytes = rbml_w.writer.tell().unwrap() - i;
// Encode the def IDs of impls, for coherence checking.
i = rbml_w.writer.tell().unwrap();
encode_impls(&ecx, krate, &mut rbml_w);
@ -2199,7 +2163,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
println!(" native bytes: {}", stats.native_lib_bytes);
println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes);
println!(" macro def bytes: {}", stats.macro_defs_bytes);
println!(" closure bytes: {}", stats.closure_bytes);
println!(" impl bytes: {}", stats.impl_bytes);
println!(" misc bytes: {}", stats.misc_bytes);
println!(" item bytes: {}", stats.item_bytes);

View file

@ -647,30 +647,7 @@ impl<'tcx> tr for MethodOrigin<'tcx> {
}
pub fn encode_closure_kind(ebml_w: &mut Encoder, kind: ty::ClosureKind) {
use serialize::Encoder;
ebml_w.emit_enum("ClosureKind", |ebml_w| {
match kind {
ty::FnClosureKind => {
ebml_w.emit_enum_variant("FnClosureKind", 0, 3, |_| {
Ok(())
})
}
ty::FnMutClosureKind => {
ebml_w.emit_enum_variant("FnMutClosureKind", 1, 3, |_| {
Ok(())
})
}
ty::FnOnceClosureKind => {
ebml_w.emit_enum_variant("FnOnceClosureKind",
2,
3,
|_| {
Ok(())
})
}
}
}).unwrap()
kind.encode(ebml_w).unwrap();
}
pub trait vtable_decoder_helpers<'tcx> {
@ -1310,12 +1287,20 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
})
}
for closure in tcx.closures.borrow().get(&ast_util::local_def(id)).iter() {
rbml_w.tag(c::tag_table_closures, |rbml_w| {
for &closure_type in tcx.closure_tys.borrow().get(&ast_util::local_def(id)).iter() {
rbml_w.tag(c::tag_table_closure_tys, |rbml_w| {
rbml_w.id(id);
rbml_w.tag(c::tag_table_val, |rbml_w| {
rbml_w.emit_closure_type(ecx, &closure.closure_type);
encode_closure_kind(rbml_w, closure.kind)
rbml_w.emit_closure_type(ecx, closure_type);
})
})
}
for &&closure_kind in tcx.closure_kinds.borrow().get(&ast_util::local_def(id)).iter() {
rbml_w.tag(c::tag_table_closure_kinds, |rbml_w| {
rbml_w.id(id);
rbml_w.tag(c::tag_table_val, |rbml_w| {
encode_closure_kind(rbml_w, closure_kind)
})
})
}
@ -1354,8 +1339,10 @@ trait rbml_decoder_decoder_helpers<'tcx> {
-> subst::Substs<'tcx>;
fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::AutoAdjustment<'tcx>;
fn read_closure<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::Closure<'tcx>;
fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::ClosureKind;
fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::ClosureTy<'tcx>;
fn read_auto_deref_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::AutoDerefRef<'tcx>;
fn read_autoref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
@ -1782,35 +1769,23 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
}).unwrap()
}
fn read_closure<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
-> ty::Closure<'tcx> {
let closure_type = self.read_opaque(|this, doc| {
fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>)
-> ty::ClosureKind
{
Decodable::decode(self).ok().unwrap()
}
fn read_closure_ty<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
-> ty::ClosureTy<'tcx>
{
self.read_opaque(|this, doc| {
Ok(tydecode::parse_ty_closure_data(
doc.data,
dcx.cdata.cnum,
doc.start,
dcx.tcx,
|s, a| this.convert_def_id(dcx, s, a)))
}).unwrap();
let variants = &[
"FnClosureKind",
"FnMutClosureKind",
"FnOnceClosureKind"
];
let kind = self.read_enum("ClosureKind", |this| {
this.read_enum_variant(variants, |_, i| {
Ok(match i {
0 => ty::FnClosureKind,
1 => ty::FnMutClosureKind,
2 => ty::FnOnceClosureKind,
_ => panic!("bad enum variant for ty::ClosureKind"),
})
})
}).unwrap();
ty::Closure {
closure_type: closure_type,
kind: kind,
}
}).unwrap()
}
/// Converts a def-id that appears in a type. The correct
@ -1937,11 +1912,17 @@ fn decode_side_tables(dcx: &DecodeContext,
let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(dcx);
dcx.tcx.adjustments.borrow_mut().insert(id, adj);
}
c::tag_table_closures => {
let closure =
val_dsr.read_closure(dcx);
dcx.tcx.closures.borrow_mut().insert(ast_util::local_def(id),
closure);
c::tag_table_closure_tys => {
let closure_ty =
val_dsr.read_closure_ty(dcx);
dcx.tcx.closure_tys.borrow_mut().insert(ast_util::local_def(id),
closure_ty);
}
c::tag_table_closure_kinds => {
let closure_kind =
val_dsr.read_closure_kind(dcx);
dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
closure_kind);
}
_ => {
dcx.tcx.sess.bug(

View file

@ -260,12 +260,10 @@ impl OverloadedCallType {
fn from_closure(tcx: &ty::ctxt, closure_did: ast::DefId)
-> OverloadedCallType {
let trait_did =
tcx.closures
tcx.closure_kinds
.borrow()
.get(&closure_did)
.expect("OverloadedCallType::from_closure: didn't \
find closure id")
.kind
.expect("OverloadedCallType::from_closure: didn't find closure id")
.trait_did(tcx);
OverloadedCallType::from_trait_id(tcx, trait_did)
}

View file

@ -594,8 +594,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
let ty = try!(self.node_ty(fn_node_id));
match ty.sty {
ty::ty_closure(closure_id, _, _) => {
let kind = self.typer.closure_kind(closure_id);
self.cat_upvar(id, span, var_id, fn_node_id, kind)
match self.typer.closure_kind(closure_id) {
Some(kind) => {
self.cat_upvar(id, span, var_id, fn_node_id, kind)
}
None => {
self.tcx().sess.span_bug(
span,
&*format!("No closure kind for {:?}", closure_id));
}
}
}
_ => {
self.tcx().sess.span_bug(

View file

@ -1024,12 +1024,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
kind,
obligation.repr(self.tcx()));
let closure_kind = self.closure_typer.closure_kind(closure_def_id);
debug!("closure_kind = {:?}", closure_kind);
if closure_kind == kind {
candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone()));
match self.closure_typer.closure_kind(closure_def_id) {
Some(closure_kind) => {
debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
if closure_kind == kind {
candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone()));
}
}
None => {
debug!("assemble_unboxed_candidates: closure_kind not yet known");
candidates.ambiguous = true;
}
}
Ok(())

View file

@ -790,7 +790,11 @@ pub struct ctxt<'tcx> {
/// Records the type of each closure. The def ID is the ID of the
/// expression defining the closure.
pub closures: RefCell<DefIdMap<Closure<'tcx>>>,
pub closure_kinds: RefCell<DefIdMap<ClosureKind>>,
/// Records the type of each closure. The def ID is the ID of the
/// expression defining the closure.
pub closure_tys: RefCell<DefIdMap<ClosureTy<'tcx>>>,
pub node_lint_levels: RefCell<FnvHashMap<(ast::NodeId, lint::LintId),
lint::LevelSource>>,
@ -2251,16 +2255,7 @@ pub struct ItemSubsts<'tcx> {
pub substs: Substs<'tcx>,
}
/// Records information about each closure.
#[derive(Clone)]
pub struct Closure<'tcx> {
/// The type of the closure.
pub closure_type: ClosureTy<'tcx>,
/// The kind of closure this is.
pub kind: ClosureKind,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub enum ClosureKind {
FnClosureKind,
FnMutClosureKind,
@ -2288,14 +2283,22 @@ impl ClosureKind {
pub trait ClosureTyper<'tcx> {
fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>;
fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind;
/// Is this a `Fn`, `FnMut` or `FnOnce` closure? During typeck,
/// returns `None` if the kind of this closure has not yet been
/// inferred.
fn closure_kind(&self,
def_id: ast::DefId)
-> Option<ty::ClosureKind>;
/// Returns the argument/return types of this closure.
fn closure_type(&self,
def_id: ast::DefId,
substs: &subst::Substs<'tcx>)
-> ty::ClosureTy<'tcx>;
// Returns `None` if the upvar types cannot yet be definitively determined.
/// Returns the set of all upvars and their transformed
/// types. During typeck, maybe return `None` if the upvar types
/// have not yet been inferred.
fn closure_upvars(&self,
def_id: ast::DefId,
substs: &Substs<'tcx>)
@ -2391,7 +2394,8 @@ pub fn mk_ctxt<'tcx>(s: Session,
extern_const_variants: RefCell::new(DefIdMap()),
method_map: RefCell::new(FnvHashMap()),
dependency_formats: RefCell::new(FnvHashMap()),
closures: RefCell::new(DefIdMap()),
closure_kinds: RefCell::new(DefIdMap()),
closure_tys: RefCell::new(DefIdMap()),
node_lint_levels: RefCell::new(FnvHashMap()),
transmute_restrictions: RefCell::new(Vec::new()),
stability: RefCell::new(stability),
@ -2438,7 +2442,7 @@ impl<'tcx> ctxt<'tcx> {
}
pub fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
self.closures.borrow()[def_id].kind
self.closure_kinds.borrow()[def_id]
}
pub fn closure_type(&self,
@ -2446,7 +2450,7 @@ impl<'tcx> ctxt<'tcx> {
substs: &subst::Substs<'tcx>)
-> ty::ClosureTy<'tcx>
{
self.closures.borrow()[def_id].closure_type.subst(self, substs)
self.closure_tys.borrow()[def_id].subst(self, substs)
}
}
@ -5635,32 +5639,26 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
closure_expr_id: closure_id.node
};
let captured_freevar_ty = match typer.upvar_capture(upvar_id) {
Some(UpvarCapture::ByValue) => {
freevar_ty
}
typer.upvar_capture(upvar_id).map(|capture| {
let freevar_ref_ty = match capture {
UpvarCapture::ByValue => {
freevar_ty
}
UpvarCapture::ByRef(borrow) => {
mk_rptr(tcx,
tcx.mk_region(borrow.region),
ty::mt {
ty: freevar_ty,
mutbl: borrow.kind.to_mutbl_lossy(),
})
}
};
Some(UpvarCapture::ByRef(borrow)) => {
mk_rptr(tcx,
tcx.mk_region(borrow.region),
ty::mt {
ty: freevar_ty,
mutbl: borrow.kind.to_mutbl_lossy(),
})
ClosureUpvar {
def: freevar.def,
span: freevar.span,
ty: freevar_ref_ty,
}
None => {
// FIXME(#16640) we should really return None here;
// but that requires better inference integration,
// for now gin up something.
freevar_ty
}
};
Some(ClosureUpvar {
def: freevar.def,
span: freevar.span,
ty: captured_freevar_ty,
})
})
.collect()
@ -6473,8 +6471,11 @@ impl<'a,'tcx> ClosureTyper<'tcx> for ty::ParameterEnvironment<'a,'tcx> {
self
}
fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
self.tcx.closure_kind(def_id)
fn closure_kind(&self,
def_id: ast::DefId)
-> Option<ty::ClosureKind>
{
Some(self.tcx.closure_kind(def_id))
}
fn closure_type(&self,

View file

@ -405,9 +405,9 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
}
ty_str => "str".to_string(),
ty_closure(ref did, _, substs) => {
let closures = cx.closures.borrow();
closures.get(did).map(|cl| {
closure_to_string(cx, &cl.closure_type.subst(cx, substs))
let closure_tys = cx.closure_tys.borrow();
closure_tys.get(did).map(|closure_type| {
closure_to_string(cx, &closure_type.subst(cx, substs))
}).unwrap_or_else(|| {
if did.krate == ast::LOCAL_CRATE {
let span = cx.map.span(did.node);

View file

@ -273,7 +273,7 @@ pub fn self_type_for_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
}
pub fn kind_for_closure(ccx: &CrateContext, closure_id: ast::DefId) -> ty::ClosureKind {
ccx.tcx().closures.borrow()[closure_id].kind
ccx.tcx().closure_kinds.borrow()[closure_id]
}
pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,

View file

@ -125,7 +125,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc
closure_id: ast::DefId,
substs: &Substs<'tcx>)
-> Option<Datum<'tcx, Rvalue>> {
if !ccx.tcx().closures.borrow().contains_key(&closure_id) {
if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) {
// Not a closure.
return None
}

View file

@ -693,7 +693,10 @@ impl<'blk, 'tcx> ty::ClosureTyper<'tcx> for BlockS<'blk, 'tcx> {
&self.fcx.param_env
}
fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
fn closure_kind(&self,
def_id: ast::DefId)
-> Option<ty::ClosureKind>
{
let typer = NormalizingClosureTyper::new(self.tcx());
typer.closure_kind(def_id)
}
@ -1065,8 +1068,11 @@ impl<'a,'tcx> ty::ClosureTyper<'tcx> for NormalizingClosureTyper<'a,'tcx> {
&self.param_env
}
fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
self.param_env.tcx.closure_kind(def_id)
fn closure_kind(&self,
def_id: ast::DefId)
-> Option<ty::ClosureKind>
{
self.param_env.closure_kind(def_id)
}
fn closure_type(&self,

View file

@ -30,7 +30,7 @@ use syntax::print::pprust;
use syntax::ptr::P;
pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
pat: &ast::Pat,
pat: &'tcx ast::Pat,
expected: Ty<'tcx>)
{
let fcx = pcx.fcx;
@ -157,9 +157,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
ast::PatIdent(_, ref path, _) => {
let path = ast_util::ident_to_path(path.span, path.node);
check_pat_enum(pcx, pat, &path, &Some(vec![]), expected);
check_pat_enum(pcx, pat, &path, Some(&[]), expected);
}
ast::PatEnum(ref path, ref subpats) => {
let subpats = subpats.as_ref().map(|v| &v[]);
check_pat_enum(pcx, pat, path, subpats, expected);
}
ast::PatStruct(ref path, ref fields, etc) => {
@ -335,9 +336,9 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
discrim: &ast::Expr,
arms: &[ast::Arm],
expr: &'tcx ast::Expr,
discrim: &'tcx ast::Expr,
arms: &'tcx [ast::Arm],
expected: Expectation<'tcx>,
match_src: ast::MatchSource) {
let tcx = fcx.ccx.tcx;
@ -424,8 +425,8 @@ pub struct pat_ctxt<'a, 'tcx: 'a> {
pub map: PatIdMap,
}
pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
path: &ast::Path, fields: &[Spanned<ast::FieldPat>],
pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
path: &ast::Path, fields: &'tcx [Spanned<ast::FieldPat>],
etc: bool, expected: Ty<'tcx>) {
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;
@ -483,10 +484,12 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
variant_def_id, etc);
}
pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
path: &ast::Path, subpats: &Option<Vec<P<ast::Pat>>>,
expected: Ty<'tcx>) {
pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
pat: &ast::Pat,
path: &ast::Path,
subpats: Option<&'tcx [P<ast::Pat>]>,
expected: Ty<'tcx>)
{
// Typecheck the path.
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;
@ -536,7 +539,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
"`{}` does not name a non-struct variant or a tuple struct", name);
fcx.write_error(pat.id);
if let Some(ref subpats) = *subpats {
if let Some(subpats) = subpats {
for pat in subpats.iter() {
check_pat(pcx, &**pat, tcx.types.err);
}
@ -545,7 +548,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
}
};
if let Some(ref subpats) = *subpats {
if let Some(subpats) = subpats {
if subpats.len() == arg_tys.len() {
for (subpat, arg_ty) in subpats.iter().zip(arg_tys.iter()) {
check_pat(pcx, &**subpat, *arg_ty);
@ -579,7 +582,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
/// `etc` is true if the pattern said '...' and false otherwise.
pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
span: Span,
fields: &[Spanned<ast::FieldPat>],
fields: &'tcx [Spanned<ast::FieldPat>],
struct_fields: &[ty::field<'tcx>],
struct_id: ast::DefId,
etc: bool) {

View file

@ -13,6 +13,8 @@ use super::AutorefArgs;
use super::check_argument_types;
use super::check_expr;
use super::check_method_argument_types;
use super::demand;
use super::DeferredCallResolution;
use super::err_args;
use super::Expectation;
use super::expected_types_for_fn_args;
@ -24,13 +26,14 @@ use super::TupleArgumentsFlag;
use super::UnresolvedTypeAction;
use super::write_call;
use CrateCtxt;
use middle::infer;
use middle::ty::{self, Ty};
use middle::ty::{self, Ty, ClosureTyper};
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ptr::P;
use CrateCtxt;
use util::ppaux::Repr;
/// Check that it is legal to call methods of the trait corresponding
/// to `trait_id` (this only cares about the trait, not the specific
@ -66,9 +69,9 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
}
pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
arg_exprs: &[P<ast::Expr>],
call_expr: &'tcx ast::Expr,
callee_expr: &'tcx ast::Expr,
arg_exprs: &'tcx [P<ast::Expr>],
expected: Expectation<'tcx>)
{
check_expr(fcx, callee_expr);
@ -96,24 +99,35 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected);
}
Some(CallStep::DeferredClosure(fn_sig)) => {
confirm_deferred_closure_call(fcx, call_expr, arg_exprs, expected, fn_sig);
}
Some(CallStep::Overloaded(method_callee)) => {
confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee, expected);
confirm_overloaded_call(fcx, call_expr, callee_expr,
arg_exprs, expected, method_callee);
}
}
}
enum CallStep<'tcx> {
Builtin,
DeferredClosure(ty::FnSig<'tcx>),
Overloaded(ty::MethodCallee<'tcx>)
}
fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
call_expr: &'tcx ast::Expr,
callee_expr: &'tcx ast::Expr,
adjusted_ty: Ty<'tcx>,
autoderefref: ty::AutoDerefRef<'tcx>)
-> Option<CallStep<'tcx>>
{
debug!("try_overloaded_call_step(call_expr={}, adjusted_ty={}, autoderefref={})",
call_expr.repr(fcx.tcx()),
adjusted_ty.repr(fcx.tcx()),
autoderefref.repr(fcx.tcx()));
// If the callee is a bare function or a closure, then we're all set.
match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty {
ty::ty_bare_fn(..) => {
@ -123,9 +137,45 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
return Some(CallStep::Builtin);
}
ty::ty_closure(def_id, _, substs) => {
assert_eq!(def_id.krate, ast::LOCAL_CRATE);
// Check whether this is a call to a closure where we
// haven't yet decided on whether the closure is fn vs
// fnmut vs fnonce. If so, we have to defer further processing.
if fcx.closure_kind(def_id).is_none() {
let closure_ty =
fcx.closure_type(def_id, substs);
let fn_sig =
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
infer::FnCall,
&closure_ty.sig).0;
fcx.record_deferred_call_resolution(
def_id,
box CallResolution {call_expr: call_expr,
callee_expr: callee_expr,
adjusted_ty: adjusted_ty,
autoderefref: autoderefref,
fn_sig: fn_sig.clone(),
closure_def_id: def_id});
return Some(CallStep::DeferredClosure(fn_sig));
}
}
_ => {}
}
try_overloaded_call_traits(fcx, call_expr, callee_expr, adjusted_ty, autoderefref)
.map(|method_callee| CallStep::Overloaded(method_callee))
}
fn try_overloaded_call_traits<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
adjusted_ty: Ty<'tcx>,
autoderefref: ty::AutoDerefRef<'tcx>)
-> Option<ty::MethodCallee<'tcx>>
{
// Try the options that are least restrictive on the caller first.
for &(opt_trait_def_id, method_name) in [
(fcx.tcx().lang_items.fn_trait(), token::intern("call")),
@ -147,7 +197,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
None) {
None => continue,
Some(method_callee) => {
return Some(CallStep::Overloaded(method_callee));
return Some(method_callee);
}
}
}
@ -158,7 +208,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
call_expr: &ast::Expr,
callee_ty: Ty<'tcx>,
arg_exprs: &[P<ast::Expr>],
arg_exprs: &'tcx [P<ast::Expr>],
expected: Expectation<'tcx>)
{
let error_fn_sig;
@ -215,22 +265,132 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
write_call(fcx, call_expr, fn_sig.output);
}
fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
arg_exprs: &[P<ast::Expr>],
method_callee: ty::MethodCallee<'tcx>,
expected: Expectation<'tcx>)
fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
call_expr: &ast::Expr,
arg_exprs: &'tcx [P<ast::Expr>],
expected: Expectation<'tcx>,
fn_sig: ty::FnSig<'tcx>)
{
let output_type = check_method_argument_types(fcx,
call_expr.span,
method_callee.ty,
call_expr,
arg_exprs,
AutorefArgs::No,
TupleArgumentsFlag::TupleArguments,
expected);
let method_call = ty::MethodCall::expr(call_expr.id);
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
write_call(fcx, call_expr, output_type);
// `fn_sig` is the *signature* of the cosure being called. We
// don't know the full details yet (`Fn` vs `FnMut` etc), but we
// do know the types expected for each argument and the return
// type.
let expected_arg_tys =
expected_types_for_fn_args(fcx,
call_expr.span,
expected,
fn_sig.output.clone(),
&*fn_sig.inputs);
check_argument_types(fcx,
call_expr.span,
&*fn_sig.inputs,
&*expected_arg_tys,
arg_exprs,
AutorefArgs::No,
fn_sig.variadic,
TupleArgumentsFlag::TupleArguments);
write_call(fcx, call_expr, fn_sig.output);
}
fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &'tcx ast::Expr,
arg_exprs: &'tcx [P<ast::Expr>],
expected: Expectation<'tcx>,
method_callee: ty::MethodCallee<'tcx>)
{
let output_type =
check_method_argument_types(fcx,
call_expr.span,
method_callee.ty,
callee_expr,
arg_exprs,
AutorefArgs::No,
TupleArgumentsFlag::TupleArguments,
expected);
write_call(fcx, call_expr, output_type);
write_overloaded_call_method_map(fcx, call_expr, method_callee);
}
fn write_overloaded_call_method_map<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
method_callee: ty::MethodCallee<'tcx>) {
let method_call = ty::MethodCall::expr(call_expr.id);
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
}
struct CallResolution<'tcx> {
call_expr: &'tcx ast::Expr,
callee_expr: &'tcx ast::Expr,
adjusted_ty: Ty<'tcx>,
autoderefref: ty::AutoDerefRef<'tcx>,
fn_sig: ty::FnSig<'tcx>,
closure_def_id: ast::DefId,
}
impl<'tcx> Repr<'tcx> for CallResolution<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \
autoderefref={}, fn_sig={}, closure_def_id={})",
self.call_expr.repr(tcx),
self.callee_expr.repr(tcx),
self.adjusted_ty.repr(tcx),
self.autoderefref.repr(tcx),
self.fn_sig.repr(tcx),
self.closure_def_id.repr(tcx))
}
}
impl<'tcx> DeferredCallResolution<'tcx> for CallResolution<'tcx> {
fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>) {
debug!("DeferredCallResolution::resolve() {}",
self.repr(fcx.tcx()));
// we should not be invoked until the closure kind has been
// determined by upvar inference
assert!(fcx.closure_kind(self.closure_def_id).is_some());
// We may now know enough to figure out fn vs fnmut etc.
match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr,
self.adjusted_ty, self.autoderefref.clone()) {
Some(method_callee) => {
// One problem is that when we get here, we are going
// to have a newly instantiated function signature
// from the call trait. This has to be reconciled with
// the older function signature we had before. In
// principle we *should* be able to fn_sigs(), but we
// can't because of the annoying need for a TypeTrace.
// (This always bites me, should find a way to
// refactor it.)
let method_sig =
ty::assert_no_late_bound_regions(fcx.tcx(),
ty::ty_fn_sig(method_callee.ty));
debug!("attempt_resolution: method_callee={}",
method_callee.repr(fcx.tcx()));
for (&method_arg_ty, &self_arg_ty) in
method_sig.inputs[1..].iter().zip(self.fn_sig.inputs.iter())
{
demand::eqtype(fcx, self.call_expr.span, self_arg_ty, method_arg_ty);
}
demand::eqtype(fcx,
self.call_expr.span,
method_sig.output.unwrap(),
self.fn_sig.output.unwrap());
write_overloaded_call_method_map(fcx, self.call_expr, method_callee);
}
None => {
fcx.tcx().sess.span_bug(
self.call_expr.span,
"failed to find an overloaded call trait for closure call");
}
}
}
}

View file

@ -26,8 +26,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
_capture: ast::CaptureClause,
opt_kind: Option<ast::ClosureKind>,
decl: &ast::FnDecl,
body: &ast::Block,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
expected: Expectation<'tcx>) {
debug!("check_expr_closure(expr={},expected={})",
expr.repr(fcx.tcx()),
@ -42,20 +42,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
// If users didn't specify what sort of closure they want,
// examine the expected type. For now, if we see explicit
// evidence than an unboxed closure is desired, we'll use
// that, otherwise we'll error, requesting an annotation.
// that. Otherwise, we leave it unspecified, to be filled
// in by upvar inference.
match expected_sig_and_kind {
None => { // don't have information about the kind, request explicit annotation
// NB We still need to typeck the body, so assume `FnMut` kind just for that
let kind = ty::FnMutClosureKind;
check_closure(fcx, expr, kind, decl, body, None);
span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
"can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
`|&:| {{}}`");
check_closure(fcx, expr, None, decl, body, None);
},
Some((sig, kind)) => {
check_closure(fcx, expr, kind, decl, body, Some(sig));
check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
}
}
}
@ -68,21 +62,21 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
};
let expected_sig = expected_sig_and_kind.map(|t| t.0);
check_closure(fcx, expr, kind, decl, body, expected_sig);
check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
}
}
}
fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
kind: ty::ClosureKind,
decl: &ast::FnDecl,
body: &ast::Block,
opt_kind: Option<ty::ClosureKind>,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
expected_sig: Option<ty::FnSig<'tcx>>) {
let expr_def_id = ast_util::local_def(expr.id);
debug!("check_closure kind={:?} expected_sig={}",
kind,
debug!("check_closure opt_kind={:?} expected_sig={}",
opt_kind,
expected_sig.repr(fcx.tcx()));
let mut fn_ty = astconv::ty_of_closure(
@ -124,17 +118,16 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
// the `closures` table.
fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];
debug!("closure for {} --> sig={} kind={:?}",
debug!("closure for {} --> sig={} opt_kind={:?}",
expr_def_id.repr(fcx.tcx()),
fn_ty.sig.repr(fcx.tcx()),
kind);
opt_kind);
let closure = ty::Closure {
closure_type: fn_ty,
kind: kind,
};
fcx.inh.closures.borrow_mut().insert(expr_def_id, closure);
fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty);
match opt_kind {
Some(kind) => { fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); }
None => { }
}
}
fn deduce_expectations_from_expected_type<'a,'tcx>(

View file

@ -31,8 +31,8 @@ use util::ppaux::Repr;
struct ConfirmContext<'a, 'tcx:'a> {
fcx: &'a FnCtxt<'a, 'tcx>,
span: Span,
self_expr: &'a ast::Expr,
call_expr: &'a ast::Expr,
self_expr: &'tcx ast::Expr,
call_expr: &'tcx ast::Expr,
}
struct InstantiatedMethodSig<'tcx> {
@ -51,8 +51,8 @@ struct InstantiatedMethodSig<'tcx> {
pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
self_expr: &ast::Expr,
call_expr: &ast::Expr,
self_expr: &'tcx ast::Expr,
call_expr: &'tcx ast::Expr,
unadjusted_self_ty: Ty<'tcx>,
pick: probe::Pick<'tcx>,
supplied_method_types: Vec<Ty<'tcx>>)
@ -70,8 +70,8 @@ pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
impl<'a,'tcx> ConfirmContext<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a, 'tcx>,
span: Span,
self_expr: &'a ast::Expr,
call_expr: &'a ast::Expr)
self_expr: &'tcx ast::Expr,
call_expr: &'tcx ast::Expr)
-> ConfirmContext<'a, 'tcx>
{
ConfirmContext { fcx: fcx, span: span, self_expr: self_expr, call_expr: call_expr }

View file

@ -44,6 +44,9 @@ pub enum MethodError {
// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
// Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind.
ClosureAmbiguity(/* DefId of fn trait */ ast::DefId),
}
// A pared down enum describing just the places from which a method
@ -65,9 +68,10 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-> bool
{
match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
Ok(_) => true,
Err(NoMatch(_, _)) => false,
Err(Ambiguity(_)) => true,
Ok(..) => true,
Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true,
Err(ClosureAmbiguity(..)) => true,
}
}
@ -90,8 +94,8 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_name: ast::Name,
self_ty: Ty<'tcx>,
supplied_method_types: Vec<Ty<'tcx>>,
call_expr: &ast::Expr,
self_expr: &ast::Expr)
call_expr: &'tcx ast::Expr,
self_expr: &'tcx ast::Expr)
-> Result<MethodCallee<'tcx>, MethodError>
{
debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})",

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::{MethodError,Ambiguity,NoMatch};
use super::{MethodError};
use super::MethodIndex;
use super::{CandidateSource,ImplSource,TraitSource};
use super::suggest;
@ -129,7 +129,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// take place in the `fcx.infcx().probe` below.
let steps = match create_steps(fcx, span, self_ty) {
Some(steps) => steps,
None => return Err(NoMatch(Vec::new(), Vec::new())),
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new())),
};
// Create a list of simplified self types, if we can.
@ -158,7 +158,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let (steps, opt_simplified_steps) = dummy.take().unwrap();
let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps);
probe_cx.assemble_inherent_candidates();
probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id);
try!(probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id));
probe_cx.pick()
})
}
@ -444,29 +444,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_extension_candidates_for_traits_in_scope(&mut self,
expr_id: ast::NodeId)
-> Result<(),MethodError>
{
let mut duplicates = HashSet::new();
let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id);
for applicable_traits in opt_applicable_traits.into_iter() {
for &trait_did in applicable_traits.iter() {
if duplicates.insert(trait_did) {
self.assemble_extension_candidates_for_trait(trait_did);
try!(self.assemble_extension_candidates_for_trait(trait_did));
}
}
}
Ok(())
}
fn assemble_extension_candidates_for_all_traits(&mut self) {
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> {
let mut duplicates = HashSet::new();
for trait_info in suggest::all_traits(self.fcx.ccx) {
if duplicates.insert(trait_info.def_id) {
self.assemble_extension_candidates_for_trait(trait_info.def_id)
try!(self.assemble_extension_candidates_for_trait(trait_info.def_id));
}
}
Ok(())
}
fn assemble_extension_candidates_for_trait(&mut self,
trait_def_id: ast::DefId) {
trait_def_id: ast::DefId)
-> Result<(),MethodError>
{
debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
trait_def_id.repr(self.tcx()));
@ -478,26 +483,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
.position(|item| item.name() == self.method_name);
let matching_index = match matching_index {
Some(i) => i,
None => { return; }
None => { return Ok(()); }
};
let method = match (&*trait_items)[matching_index].as_opt_method() {
Some(m) => m,
None => { return; }
None => { return Ok(()); }
};
// Check whether `trait_def_id` defines a method with suitable name:
if !self.has_applicable_self(&*method) {
debug!("method has inapplicable self");
return self.record_static_candidate(TraitSource(trait_def_id));
self.record_static_candidate(TraitSource(trait_def_id));
return Ok(());
}
self.assemble_extension_candidates_for_trait_impls(trait_def_id,
method.clone(),
matching_index);
self.assemble_closure_candidates(trait_def_id,
method.clone(),
matching_index);
try!(self.assemble_closure_candidates(trait_def_id,
method.clone(),
matching_index));
self.assemble_projection_candidates(trait_def_id,
method.clone(),
@ -506,6 +512,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.assemble_where_clause_candidates(trait_def_id,
method,
matching_index);
Ok(())
}
fn assemble_extension_candidates_for_trait_impls(&mut self,
@ -576,6 +584,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
trait_def_id: ast::DefId,
method_ty: Rc<ty::Method<'tcx>>,
method_index: uint)
-> Result<(),MethodError>
{
// Check if this is one of the Fn,FnMut,FnOnce traits.
let tcx = self.tcx();
@ -586,7 +595,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
} else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
ty::FnOnceClosureKind
} else {
return;
return Ok(());
};
// Check if there is an unboxed-closure self-type in the list of receivers.
@ -598,19 +607,16 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
_ => continue,
};
let closures = self.fcx.inh.closures.borrow();
let closure_data = match closures.get(&closure_def_id) {
Some(data) => data,
let closure_kinds = self.fcx.inh.closure_kinds.borrow();
let closure_kind = match closure_kinds.get(&closure_def_id) {
Some(&k) => k,
None => {
self.tcx().sess.span_bug(
self.span,
&format!("No entry for closure: {}",
closure_def_id.repr(self.tcx()))[]);
return Err(MethodError::ClosureAmbiguity(trait_def_id));
}
};
// this closure doesn't implement the right kind of `Fn` trait
if closure_data.kind != kind {
if closure_kind != kind {
continue;
}
@ -630,6 +636,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
kind: ClosureCandidate(trait_def_id, method_index)
});
}
Ok(())
}
fn assemble_projection_candidates(&mut self,
@ -735,11 +743,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
let span = self.span;
let tcx = self.tcx();
self.assemble_extension_candidates_for_all_traits();
try!(self.assemble_extension_candidates_for_all_traits());
let out_of_scope_traits = match self.pick_core() {
Some(Ok(p)) => vec![p.method_ty.container.id()],
Some(Err(Ambiguity(v))) => v.into_iter().map(|source| {
Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| {
match source {
TraitSource(id) => id,
ImplSource(impl_id) => {
@ -752,14 +760,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
}).collect(),
Some(Err(NoMatch(_, others))) => {
Some(Err(MethodError::NoMatch(_, others))) => {
assert!(others.is_empty());
vec![]
}
Some(Err(MethodError::ClosureAmbiguity(..))) => {
// this error only occurs when assembling candidates
tcx.sess.span_bug(span, "encountered ClosureAmbiguity from pick_core");
}
None => vec![],
};
;
Err(NoMatch(static_candidates, out_of_scope_traits))
Err(MethodError::NoMatch(static_candidates, out_of_scope_traits))
}
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
@ -895,7 +907,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
if applicable_candidates.len() > 1 {
let sources = probes.iter().map(|p| p.to_source()).collect();
return Some(Err(Ambiguity(sources)));
return Some(Err(MethodError::Ambiguity(sources)));
}
applicable_candidates.pop().map(|probe| {

View file

@ -22,6 +22,7 @@ use util::ppaux::UserString;
use syntax::{ast, ast_util};
use syntax::codemap::Span;
use syntax::print::pprust;
use std::cell;
use std::cmp::Ordering;
@ -32,6 +33,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
callee_expr: &ast::Expr,
error: MethodError)
{
match error {
@ -84,6 +86,18 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
report_candidates(fcx, span, method_name, sources);
}
MethodError::ClosureAmbiguity(trait_def_id) => {
fcx.sess().span_err(
span,
&*format!("the `{}` method from the `{}` trait cannot be explicitly \
invoked on this closure as we have not yet inferred what \
kind of closure it is; use overloaded call notation instead \
(e.g., `{}()`)",
method_name.user_string(fcx.tcx()),
ty::item_path_str(fcx.tcx(), trait_def_id),
pprust::expr_to_string(callee_expr)));
}
}
fn report_candidates(fcx: &FnCtxt,

View file

@ -110,6 +110,7 @@ use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
use util::lev_distance::lev_distance;
use std::cell::{Cell, Ref, RefCell};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::mem::replace;
use std::rc::Rc;
use std::iter::repeat;
@ -160,7 +161,8 @@ pub struct Inherited<'a, 'tcx: 'a> {
adjustments: RefCell<NodeMap<ty::AutoAdjustment<'tcx>>>,
method_map: MethodMap<'tcx>,
upvar_capture_map: RefCell<ty::UpvarCaptureMap>,
closures: RefCell<DefIdMap<ty::Closure<'tcx>>>,
closure_tys: RefCell<DefIdMap<ty::ClosureTy<'tcx>>>,
closure_kinds: RefCell<DefIdMap<ty::ClosureKind>>,
object_cast_map: ObjectCastMap<'tcx>,
// A mapping from each fn's id to its signature, with all bound
@ -170,8 +172,23 @@ pub struct Inherited<'a, 'tcx: 'a> {
// Tracks trait obligations incurred during this function body.
fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,
// When we process a call like `c()` where `c` is a closure type,
// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
// `FnOnce` closure. In that case, we defer full resolution of the
// call until upvar inference can kick in and make the
// decision. We keep these deferred resolutions grouped by the
// def-id of the closure, so that once we decide, we can easily go
// back and process them.
deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'tcx>>>>,
}
trait DeferredCallResolution<'tcx> {
fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>);
}
type DeferredCallResolutionHandler<'tcx> = Box<DeferredCallResolution<'tcx>+'tcx>;
/// When type-checking an expression, we propagate downward
/// whatever type hint we are able in the form of an `Expectation`.
#[derive(Copy)]
@ -339,8 +356,11 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
&self.inh.param_env
}
fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
self.inh.closures.borrow()[def_id].kind
fn closure_kind(&self,
def_id: ast::DefId)
-> Option<ty::ClosureKind>
{
self.inh.closure_kinds.borrow().get(&def_id).cloned()
}
fn closure_type(&self,
@ -348,7 +368,7 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
substs: &subst::Substs<'tcx>)
-> ty::ClosureTy<'tcx>
{
self.inh.closures.borrow()[def_id].closure_type.subst(self.tcx(), substs)
self.inh.closure_tys.borrow()[def_id].subst(self.tcx(), substs)
}
fn closure_upvars(&self,
@ -374,9 +394,11 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
method_map: RefCell::new(FnvHashMap()),
object_cast_map: RefCell::new(NodeMap()),
upvar_capture_map: RefCell::new(FnvHashMap()),
closures: RefCell::new(DefIdMap()),
closure_tys: RefCell::new(DefIdMap()),
closure_kinds: RefCell::new(DefIdMap()),
fn_sig_map: RefCell::new(NodeMap()),
fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
deferred_call_resolutions: RefCell::new(DefIdMap()),
}
}
@ -425,13 +447,13 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>)
struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &ast::Item) {
impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &'tcx ast::Item) {
check_item(self.ccx, i);
visit::walk_item(self, i);
}
fn visit_ty(&mut self, t: &ast::Ty) {
fn visit_ty(&mut self, t: &'tcx ast::Ty) {
match t.node {
ast::TyFixedLengthVec(_, ref expr) => {
check_const_in_type(self.ccx, &**expr, self.ccx.tcx.types.uint);
@ -459,8 +481,8 @@ pub fn check_item_types(ccx: &CrateCtxt) {
}
fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
decl: &ast::FnDecl,
body: &ast::Block,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
id: ast::NodeId,
raw_fty: Ty<'tcx>,
param_env: ty::ParameterEnvironment<'a, 'tcx>)
@ -480,8 +502,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
decl, id, body, &inh);
vtable::select_all_fcx_obligations_or_error(&fcx);
vtable::select_all_fcx_obligations_and_apply_defaults(&fcx);
upvar::closure_analyze_fn(&fcx, id, decl, body);
vtable::select_all_fcx_obligations_or_error(&fcx);
regionck::regionck_fn(&fcx, id, decl, body);
writeback::resolve_type_vars_in_fn(&fcx, decl, body);
}
@ -512,9 +535,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
// Add explicitly-declared locals.
fn visit_local(&mut self, local: &ast::Local) {
fn visit_local(&mut self, local: &'tcx ast::Local) {
let o_ty = match local.ty {
Some(ref ty) => Some(self.fcx.to_ty(&**ty)),
None => None
@ -528,7 +551,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
}
// Add pattern bindings.
fn visit_pat(&mut self, p: &ast::Pat) {
fn visit_pat(&mut self, p: &'tcx ast::Pat) {
if let ast::PatIdent(_, ref path1, _) = p.node {
if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) {
let var_ty = self.assign(p.span, p.id, None);
@ -546,7 +569,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
visit::walk_pat(self, p);
}
fn visit_block(&mut self, b: &ast::Block) {
fn visit_block(&mut self, b: &'tcx ast::Block) {
// non-obvious: the `blk` variable maps to region lb, so
// we have to keep this up-to-date. This
// is... unfortunate. It'd be nice to not need this.
@ -555,7 +578,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
// Since an expr occurs as part of the type fixed size arrays we
// need to record the type for that node
fn visit_ty(&mut self, t: &ast::Ty) {
fn visit_ty(&mut self, t: &'tcx ast::Ty) {
match t.node {
ast::TyFixedLengthVec(ref ty, ref count_expr) => {
self.visit_ty(&**ty);
@ -566,8 +589,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
}
// Don't descend into fns and items
fn visit_fn(&mut self, _: visit::FnKind<'v>, _: &'v ast::FnDecl,
_: &'v ast::Block, _: Span, _: ast::NodeId) { }
fn visit_fn(&mut self, _: visit::FnKind<'tcx>, _: &'tcx ast::FnDecl,
_: &'tcx ast::Block, _: Span, _: ast::NodeId) { }
fn visit_item(&mut self, _: &ast::Item) { }
}
@ -582,9 +605,9 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
unsafety: ast::Unsafety,
unsafety_id: ast::NodeId,
fn_sig: &ty::FnSig<'tcx>,
decl: &ast::FnDecl,
decl: &'tcx ast::FnDecl,
fn_id: ast::NodeId,
body: &ast::Block,
body: &'tcx ast::Block,
inherited: &'a Inherited<'a, 'tcx>)
-> FnCtxt<'a, 'tcx>
{
@ -677,7 +700,7 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
}
}
pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
debug!("check_item(it.id={}, it.ident={})",
it.id,
ty::item_path_str(ccx.tcx, local_def(it.id)));
@ -829,7 +852,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
/// * `method`: the method definition
fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
item_generics: &ty::Generics<'tcx>,
method: &ast::Method) {
method: &'tcx ast::Method) {
debug!("check_method_body(item_generics={}, method.id={})",
item_generics.repr(ccx.tcx),
method.id);
@ -1133,10 +1156,10 @@ fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
fn check_cast(fcx: &FnCtxt,
cast_expr: &ast::Expr,
e: &ast::Expr,
t: &ast::Ty) {
fn check_cast<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
cast_expr: &ast::Expr,
e: &'tcx ast::Expr,
t: &ast::Ty) {
let id = cast_expr.id;
let span = cast_expr.span;
@ -1279,6 +1302,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) }
}
fn record_deferred_call_resolution(&self,
closure_def_id: ast::DefId,
r: DeferredCallResolutionHandler<'tcx>) {
let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut();
let mut vec = match deferred_call_resolutions.entry(closure_def_id) {
Occupied(entry) => entry.into_mut(),
Vacant(entry) => entry.insert(Vec::new()),
};
vec.push(r);
}
fn remove_deferred_call_resolutions(&self,
closure_def_id: ast::DefId)
-> Vec<DeferredCallResolutionHandler<'tcx>>
{
let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut();
deferred_call_resolutions.remove(&closure_def_id).unwrap_or(Vec::new())
}
pub fn tag(&self) -> String {
format!("{:?}", self as *const FnCtxt)
}
@ -2068,7 +2110,7 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_call: MethodCall,
expr: &ast::Expr,
base_expr: &ast::Expr,
base_expr: &'tcx ast::Expr,
adjusted_ty: Ty<'tcx>,
adjustment: ty::AutoDerefRef<'tcx>,
lvalue_pref: LvaluePreference,
@ -2138,8 +2180,8 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
method_fn_ty: Ty<'tcx>,
callee_expr: &ast::Expr,
args_no_rcvr: &[P<ast::Expr>],
callee_expr: &'tcx ast::Expr,
args_no_rcvr: &'tcx [P<ast::Expr>],
autoref_args: AutorefArgs,
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>)
@ -2194,7 +2236,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
fn_inputs: &[Ty<'tcx>],
expected_arg_tys: &[Ty<'tcx>],
args: &[P<ast::Expr>],
args: &'tcx [P<ast::Expr>],
autoref_args: AutorefArgs,
variadic: bool,
tuple_arguments: TupleArgumentsFlag) {
@ -2462,7 +2504,7 @@ pub fn valid_range_bounds(ccx: &CrateCtxt,
}
pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected: Ty<'tcx>) {
check_expr_with_unifier(
fcx, expr, ExpectHasType(expected), NoPreference,
@ -2470,14 +2512,14 @@ pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
fn check_expr_coercable_to_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected: Ty<'tcx>) {
check_expr_with_unifier(
fcx, expr, ExpectHasType(expected), NoPreference,
|| demand::coerce(fcx, expr.span, expected, expr));
}
fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr,
fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx ast::Expr,
expected: Ty<'tcx>) {
check_expr_with_unifier(
fcx, expr, ExpectHasType(expected), NoPreference,
@ -2485,7 +2527,7 @@ fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr,
}
fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected: Expectation<'tcx>) {
check_expr_with_unifier(
fcx, expr, expected, NoPreference,
@ -2493,19 +2535,19 @@ fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference)
{
check_expr_with_unifier(fcx, expr, expected, lvalue_pref, || ())
}
fn check_expr(fcx: &FnCtxt, expr: &ast::Expr) {
fn check_expr<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr) {
check_expr_with_unifier(fcx, expr, NoExpectation, NoPreference, || ())
}
fn check_expr_with_lvalue_pref(fcx: &FnCtxt, expr: &ast::Expr,
lvalue_pref: LvaluePreference) {
fn check_expr_with_lvalue_pref<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr,
lvalue_pref: LvaluePreference) {
check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ())
}
@ -2613,7 +2655,7 @@ fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
/// that there are actually multiple representations for `ty_err`, so avoid
/// that when err needs to be handled differently.
fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference,
unifier: F) where
@ -2624,9 +2666,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
// Checks a method call.
fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
method_name: ast::SpannedIdent,
args: &[P<ast::Expr>],
args: &'tcx [P<ast::Expr>],
tps: &[P<ast::Ty>],
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) {
@ -2653,7 +2695,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
method_ty
}
Err(error) => {
method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error);
method::report_error(fcx, method_name.span, expr_t,
method_name.node.name, rcvr, error);
fcx.write_error(expr.id);
fcx.tcx().types.err
}
@ -2675,9 +2718,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
// A generic function for checking the then and else in an if
// or if-else.
fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
cond_expr: &ast::Expr,
then_blk: &ast::Block,
opt_else_expr: Option<&ast::Expr>,
cond_expr: &'tcx ast::Expr,
then_blk: &'tcx ast::Block,
opt_else_expr: Option<&'tcx ast::Expr>,
id: ast::NodeId,
sp: Span,
expected: Expectation<'tcx>) {
@ -2717,12 +2760,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
op_ex: &ast::Expr,
op_ex: &'tcx ast::Expr,
lhs_ty: Ty<'tcx>,
opname: ast::Name,
trait_did: Option<ast::DefId>,
lhs: &'a ast::Expr,
rhs: Option<&P<ast::Expr>>,
rhs: Option<&'tcx P<ast::Expr>>,
unbound_method: F,
autoref_args: AutorefArgs) -> Ty<'tcx> where
F: FnOnce(),
@ -2803,12 +2846,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
// could be either an expr_binop or an expr_assign_binop
fn check_binop(fcx: &FnCtxt,
expr: &ast::Expr,
op: ast::BinOp,
lhs: &ast::Expr,
rhs: &P<ast::Expr>,
is_binop_assignment: IsBinopAssignment) {
fn check_binop<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &'tcx ast::Expr,
op: ast::BinOp,
lhs: &'tcx ast::Expr,
rhs: &'tcx P<ast::Expr>,
is_binop_assignment: IsBinopAssignment) {
let tcx = fcx.ccx.tcx;
let lvalue_pref = match is_binop_assignment {
@ -2923,11 +2966,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
ex: &ast::Expr,
lhs_expr: &ast::Expr,
ex: &'tcx ast::Expr,
lhs_expr: &'tcx ast::Expr,
lhs_resolved_t: Ty<'tcx>,
op: ast::BinOp,
rhs: &P<ast::Expr>) -> Ty<'tcx> {
rhs: &'tcx P<ast::Expr>) -> Ty<'tcx> {
let tcx = fcx.ccx.tcx;
let lang = &tcx.lang_items;
let (name, trait_did) = match op.node {
@ -2966,8 +3009,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
op_str: &str,
mname: &str,
trait_did: Option<ast::DefId>,
ex: &ast::Expr,
rhs_expr: &ast::Expr,
ex: &'tcx ast::Expr,
rhs_expr: &'tcx ast::Expr,
rhs_t: Ty<'tcx>,
op: ast::UnOp) -> Ty<'tcx> {
lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
@ -2980,11 +3023,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
// Check field access expressions
fn check_field(fcx: &FnCtxt,
expr: &ast::Expr,
lvalue_pref: LvaluePreference,
base: &ast::Expr,
field: &ast::SpannedIdent) {
fn check_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &'tcx ast::Expr,
lvalue_pref: LvaluePreference,
base: &'tcx ast::Expr,
field: &ast::SpannedIdent) {
let tcx = fcx.ccx.tcx;
check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
let expr_t = structurally_resolved_type(fcx, expr.span,
@ -3077,11 +3120,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
// Check tuple index expressions
fn check_tup_field(fcx: &FnCtxt,
expr: &ast::Expr,
lvalue_pref: LvaluePreference,
base: &ast::Expr,
idx: codemap::Spanned<uint>) {
fn check_tup_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &'tcx ast::Expr,
lvalue_pref: LvaluePreference,
base: &'tcx ast::Expr,
idx: codemap::Spanned<uint>) {
let tcx = fcx.ccx.tcx;
check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
let expr_t = structurally_resolved_type(fcx, expr.span,
@ -3149,7 +3192,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
node_id: ast::NodeId,
substitutions: &'tcx subst::Substs<'tcx>,
field_types: &[ty::field_ty],
ast_fields: &[ast::Field],
ast_fields: &'tcx [ast::Field],
check_completeness: bool,
enum_id_opt: Option<ast::DefId>) {
let tcx = fcx.ccx.tcx;
@ -3252,12 +3295,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
}
fn check_struct_constructor(fcx: &FnCtxt,
id: ast::NodeId,
span: codemap::Span,
class_id: ast::DefId,
fields: &[ast::Field],
base_expr: Option<&ast::Expr>) {
fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
span: codemap::Span,
class_id: ast::DefId,
fields: &'tcx [ast::Field],
base_expr: Option<&'tcx ast::Expr>) {
let tcx = fcx.ccx.tcx;
// Generate the struct type.
@ -3294,12 +3337,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
fcx.write_ty(id, struct_type);
}
fn check_struct_enum_variant(fcx: &FnCtxt,
id: ast::NodeId,
span: codemap::Span,
enum_id: ast::DefId,
variant_id: ast::DefId,
fields: &[ast::Field]) {
fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
span: codemap::Span,
enum_id: ast::DefId,
variant_id: ast::DefId,
fields: &'tcx [ast::Field]) {
let tcx = fcx.ccx.tcx;
// Look up the number of type parameters and the raw type, and
@ -3324,10 +3367,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
fcx.write_ty(id, enum_type);
}
fn check_struct_fields_on_error(fcx: &FnCtxt,
id: ast::NodeId,
fields: &[ast::Field],
base_expr: &Option<P<ast::Expr>>) {
fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
fields: &'tcx [ast::Field],
base_expr: &'tcx Option<P<ast::Expr>>) {
// Make sure to still write the types
// otherwise we might ICE
fcx.write_error(id);
@ -4126,15 +4169,15 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
}
}
pub fn check_decl_initializer(fcx: &FnCtxt,
nid: ast::NodeId,
init: &ast::Expr)
pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
nid: ast::NodeId,
init: &'tcx ast::Expr)
{
let local_ty = fcx.local_ty(init.span, nid);
check_expr_coercable_to_type(fcx, init, local_ty)
}
pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) {
pub fn check_decl_local<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, local: &'tcx ast::Local) {
let tcx = fcx.ccx.tcx;
let t = fcx.local_ty(local.span, local.id);
@ -4159,7 +4202,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) {
}
}
pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) {
pub fn check_stmt<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, stmt: &'tcx ast::Stmt) {
let node_id;
let mut saw_bot = false;
let mut saw_err = false;
@ -4204,7 +4247,7 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) {
}
}
pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block) {
pub fn check_block_no_value<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, blk: &'tcx ast::Block) {
check_block_with_expected(fcx, blk, ExpectHasType(ty::mk_nil(fcx.tcx())));
let blkty = fcx.node_ty(blk.id);
if ty::type_is_error(blkty) {
@ -4216,7 +4259,7 @@ pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block) {
}
fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
blk: &ast::Block,
blk: &'tcx ast::Block,
expected: Expectation<'tcx>) {
let prev = {
let mut fcx_ps = fcx.ps.borrow_mut();
@ -4299,17 +4342,17 @@ fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
/// length expression in a fixed-length vector, but someday it might be
/// extended to type-level numeric literals.
fn check_const_in_type<'a,'tcx>(ccx: &'a CrateCtxt<'a,'tcx>,
expr: &ast::Expr,
expr: &'tcx ast::Expr,
expected_type: Ty<'tcx>) {
let inh = static_inherited_fields(ccx);
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(expected_type), expr.id);
check_const_with_ty(&fcx, expr.span, expr, expected_type);
}
fn check_const(ccx: &CrateCtxt,
sp: Span,
e: &ast::Expr,
id: ast::NodeId) {
fn check_const<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
sp: Span,
e: &'tcx ast::Expr,
id: ast::NodeId) {
let inh = static_inherited_fields(ccx);
let rty = ty::node_id_to_type(ccx.tcx, id);
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
@ -4319,7 +4362,7 @@ fn check_const(ccx: &CrateCtxt,
fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
_: Span,
e: &ast::Expr,
e: &'tcx ast::Expr,
declty: Ty<'tcx>) {
// Gather locals in statics (because of block expressions).
// This is technically unnecessary because locals in static items are forbidden,
@ -4420,10 +4463,10 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
}
}
pub fn check_enum_variants(ccx: &CrateCtxt,
sp: Span,
vs: &[P<ast::Variant>],
id: ast::NodeId) {
pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
sp: Span,
vs: &'tcx [P<ast::Variant>],
id: ast::NodeId) {
fn disr_in_range(ccx: &CrateCtxt,
ty: attr::IntType,
@ -4453,7 +4496,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
}
fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
vs: &[P<ast::Variant>],
vs: &'tcx [P<ast::Variant>],
id: ast::NodeId,
hint: attr::ReprAttr)
-> Vec<Rc<ty::VariantInfo<'tcx>>> {

View file

@ -46,7 +46,9 @@ use middle::expr_use_visitor as euv;
use middle::mem_categorization as mc;
use middle::ty::{self};
use middle::infer::{InferCtxt, UpvarRegion};
use std::collections::HashSet;
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::visit::{self, Visitor};
use util::ppaux::Repr;
@ -56,13 +58,18 @@ use util::ppaux::Repr;
pub fn closure_analyze_fn(fcx: &FnCtxt,
_id: ast::NodeId,
decl: &ast::FnDecl,
body: &ast::Block) {
_decl: &ast::FnDecl,
body: &ast::Block)
{
let mut seed = SeedBorrowKind::new(fcx);
seed.visit_block(body);
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
let mut adjust = AdjustBorrowKind::new(fcx);
adjust.analyze_fn(decl, body);
let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds);
adjust.visit_block(body);
// it's our job to process these.
assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
}
///////////////////////////////////////////////////////////////////////////
@ -70,6 +77,7 @@ pub fn closure_analyze_fn(fcx: &FnCtxt,
struct SeedBorrowKind<'a,'tcx:'a> {
fcx: &'a FnCtxt<'a,'tcx>,
closures_with_inferred_kinds: HashSet<ast::NodeId>,
}
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
@ -105,7 +113,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
SeedBorrowKind { fcx: fcx }
SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@ -121,6 +129,14 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
capture_clause: ast::CaptureClause,
_body: &ast::Block)
{
let closure_def_id = ast_util::local_def(expr.id);
if !self.fcx.inh.closure_kinds.borrow().contains_key(&closure_def_id) {
self.closures_with_inferred_kinds.insert(expr.id);
self.fcx.inh.closure_kinds.borrow_mut().insert(closure_def_id, ty::FnClosureKind);
debug!("check_closure: adding closure_id={} to closures_with_inferred_kinds",
closure_def_id.repr(self.tcx()));
}
ty::with_freevars(self.tcx(), expr.id, |freevars| {
for freevar in freevars.iter() {
let var_node_id = freevar.def.local_node_id();
@ -151,29 +167,78 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
// ADJUST BORROW KIND
struct AdjustBorrowKind<'a,'tcx:'a> {
fcx: &'a FnCtxt<'a,'tcx>
fcx: &'a FnCtxt<'a,'tcx>,
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
}
impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
AdjustBorrowKind { fcx: fcx }
fn new(fcx: &'a FnCtxt<'a,'tcx>,
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
-> AdjustBorrowKind<'a,'tcx> {
AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.fcx.tcx()
}
fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) {
/*!
* Analysis starting point.
*/
self.visit_block(body);
debug!("analyzing fn body with id {}", body.id);
debug!("analyzing closure `{}` with fn body id `{}`", id, body.id);
let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
euv.walk_fn(decl, body);
// If we had not yet settled on a closure kind for this closure,
// then we should have by now. Process and remove any deferred resolutions.
//
// Interesting fact: all calls to this closure must come
// *after* its definition. Initially, I thought that some
// kind of fixed-point iteration would be required, due to the
// possibility of twisted examples like this one:
//
// ```rust
// let mut closure0 = None;
// let vec = vec!(1, 2, 3);
//
// loop {
// {
// let closure1 = || {
// match closure0.take() {
// Some(c) => {
// return c(); // (*) call to `closure0` before it is defined
// }
// None => { }
// }
// };
// closure1();
// }
//
// closure0 = || vec;
// }
// ```
//
// However, this turns out to be wrong. Examples like this
// fail to compile because the type of the variable `c` above
// is an inference variable. And in fact since closure types
// cannot be written, there is no way to make this example
// work without a boxed closure. This implies that we can't
// have two closures that recursively call one another without
// some form of boxing (and hence explicit writing of a
// closure kind) involved. Huzzah. -nmatsakis
let closure_def_id = ast_util::local_def(id);
if self.closures_with_inferred_kinds.contains(&id) {
let mut deferred_call_resolutions =
self.fcx.remove_deferred_call_resolutions(closure_def_id);
for deferred_call_resolution in deferred_call_resolutions.iter_mut() {
deferred_call_resolution.resolve(self.fcx);
}
}
}
fn adjust_upvar_borrow_kind_for_consume(&self,
@ -198,13 +263,29 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
match guarantor.cat {
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
mc::cat_deref(_, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
debug!("adjust_upvar_borrow_kind_for_consume: \
setting upvar_id={:?} to by value",
upvar_id);
match cmt.note {
mc::NoteUpvarRef(upvar_id) => {
debug!("adjust_upvar_borrow_kind_for_consume: \
setting upvar_id={:?} to by value",
upvar_id);
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
// to move out of an upvar, this must be a FnOnce closure
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
}
mc::NoteClosureEnv(upvar_id) => {
// we get just a closureenv ref if this is a
// `move` closure, or if the upvar has already
// been inferred to by-value. In any case, we
// must still adjust the kind of the closure
// to be a FnOnce closure to permit moves out
// of the environment.
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
}
mc::NoteNone => {
}
}
}
_ => { }
@ -229,15 +310,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
let ub = &mut upvar_capture_map[upvar_id];
self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
} else {
if !self.try_adjust_upvar_deref(&cmt.note, ty::MutBorrow) {
// assignment to deref of an `&mut`
// borrowed pointer implies that the
// pointer itself must be unique, but not
@ -271,15 +344,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to unique if necessary
let mut ub = self.fcx.inh.upvar_capture_map.borrow_mut();
let ub = &mut ub[upvar_id];
self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
} else {
if !self.try_adjust_upvar_deref(&cmt.note, ty::UniqueImmBorrow) {
// for a borrowed pointer to be unique, its
// base must be unique
self.adjust_upvar_borrow_kind_for_unique(base);
@ -295,6 +360,48 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
}
}
fn try_adjust_upvar_deref(&self,
note: &mc::Note,
borrow_kind: ty::BorrowKind)
-> bool
{
assert!(match borrow_kind {
ty::MutBorrow => true,
ty::UniqueImmBorrow => true,
// imm borrows never require adjusting any kinds, so we don't wind up here
ty::ImmBorrow => false,
});
match *note {
mc::NoteUpvarRef(upvar_id) => {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
let ub = &mut upvar_capture_map[upvar_id];
self.adjust_upvar_borrow_kind(upvar_id, ub, borrow_kind);
// also need to be in an FnMut closure since this is not an ImmBorrow
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
true
}
mc::NoteClosureEnv(upvar_id) => {
// this kind of deref occurs in a `move` closure, or
// for a by-value upvar; in either case, to mutate an
// upvar, we need to be an FnMut closure
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
true
}
mc::NoteNone => {
false
}
}
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
@ -328,6 +435,40 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
}
}
}
fn adjust_closure_kind(&self,
closure_id: ast::NodeId,
new_kind: ty::ClosureKind) {
debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
closure_id, new_kind);
if !self.closures_with_inferred_kinds.contains(&closure_id) {
return;
}
let closure_def_id = ast_util::local_def(closure_id);
let mut closure_kinds = self.fcx.inh.closure_kinds.borrow_mut();
let existing_kind = closure_kinds[closure_def_id];
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
closure_id, existing_kind, new_kind);
match (existing_kind, new_kind) {
(ty::FnClosureKind, ty::FnClosureKind) |
(ty::FnMutClosureKind, ty::FnClosureKind) |
(ty::FnMutClosureKind, ty::FnMutClosureKind) |
(ty::FnOnceClosureKind, _) => {
// no change needed
}
(ty::FnClosureKind, ty::FnMutClosureKind) |
(ty::FnClosureKind, ty::FnOnceClosureKind) |
(ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
// new kind is stronger than the old kind
closure_kinds.insert(closure_def_id, new_kind);
}
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
@ -336,14 +477,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
decl: &'v ast::FnDecl,
body: &'v ast::Block,
span: Span,
_id: ast::NodeId)
id: ast::NodeId)
{
match fn_kind {
visit::FkItemFn(..) | visit::FkMethod(..) => {
// ignore nested fn items
}
visit::FkFnBlock => {
self.analyze_fn(decl, body);
self.analyze_closure(id, decl, body);
visit::walk_fn(self, fn_kind, decl, body, span);
}
}

View file

@ -277,12 +277,22 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
}
}
pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
debug!("select_all_fcx_obligations_or_error");
pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
debug!("select_all_fcx_obligations_and_apply_defaults");
select_fcx_obligations_where_possible(fcx);
fcx.default_type_parameters();
select_fcx_obligations_where_possible(fcx);
}
pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
debug!("select_all_fcx_obligations_or_error");
// upvar inference should have ensured that all deferrred call
// resolutions are handled by now.
assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
select_all_fcx_obligations_and_apply_defaults(fcx);
let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx);
match r {

View file

@ -204,14 +204,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
return
}
for (def_id, closure) in self.fcx.inh.closures.borrow().iter() {
let closure_ty = self.resolve(&closure.closure_type,
ResolvingClosure(*def_id));
let closure = ty::Closure {
closure_type: closure_ty,
kind: closure.kind,
};
self.fcx.tcx().closures.borrow_mut().insert(*def_id, closure);
for (def_id, closure_ty) in self.fcx.inh.closure_tys.borrow().iter() {
let closure_ty = self.resolve(closure_ty, ResolvingClosure(*def_id));
self.fcx.tcx().closure_tys.borrow_mut().insert(*def_id, closure_ty);
}
for (def_id, &closure_kind) in self.fcx.inh.closure_kinds.borrow().iter() {
self.fcx.tcx().closure_kinds.borrow_mut().insert(*def_id, closure_kind);
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Various unsuccessful attempts to put the unboxed closure kind
// inference into an awkward position that might require fixed point
// iteration (basically where inferring the kind of a closure `c`
// would require knowing the kind of `c`). I currently believe this is
// impossible.
fn a() {
// This case of recursion wouldn't even require fixed-point
// iteration, but it still doesn't work. The weird structure with
// the `Option` is to avoid giving any useful hints about the `Fn`
// kind via the expected type.
let mut factorial: Option<Box<Fn(u32) -> u32>> = None;
let f = |x: u32| -> u32 {
let g = factorial.as_ref().unwrap();
if x == 0 {1} else {x * g(x-1)}
};
factorial = Some(Box::new(f));
//~^ ERROR cannot assign to `factorial` because it is borrowed
}
fn main() { }

View file

@ -0,0 +1,39 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Various unsuccessful attempts to put the unboxed closure kind
// inference into an awkward position that might require fixed point
// iteration (basically where inferring the kind of a closure `c`
// would require knowing the kind of `c`). I currently believe this is
// impossible.
fn a() {
let mut closure0 = None;
let vec = vec!(1, 2, 3);
loop {
{
let closure1 = || {
match closure0.take() {
Some(c) => {
return c();
//~^ ERROR the type of this value must be known in this context
}
None => { }
}
};
closure1();
}
closure0 = || vec;
}
}
fn main() { }

View file

@ -0,0 +1,18 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closures)]
fn main() {
let mut zero = || {};
let () = zero.call_mut(());
//~^ ERROR we have not yet inferred what kind of closure it is
}

View file

@ -0,0 +1,32 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
fn main() {
let mut counter = 0;
// Here this must be inferred to FnMut so that it can mutate counter,
// but we forgot the mut.
let tick1 = || {
counter += 1;
};
// In turn, tick2 must be inferred to FnMut so that it can call
// tick1, but we forgot the mut. The error message we currently
// get seems... suboptimal.
let tick2 = || { //~ ERROR closure cannot assign to immutable local variable `tick1`
tick1();
};
tick2(); //~ ERROR cannot borrow
}

View file

@ -0,0 +1,18 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
fn main() {
let mut counter = 0;
let tick = || counter += 1;
tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
}

View file

@ -0,0 +1,18 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
fn main() {
let mut counter = 0;
let tick = move || counter += 1;
tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
}

View file

@ -0,0 +1,21 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
use std::mem;
fn main() {
let mut counter: Vec<i32> = Vec::new();
let tick = || mem::drop(counter);
tick();
tick(); //~ ERROR use of moved value: `tick`
}

View file

@ -0,0 +1,21 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
use std::mem;
fn main() {
let mut counter: Vec<i32> = Vec::new();
let tick = move || mem::drop(counter);
tick();
tick(); //~ ERROR use of moved value: `tick`
}

View file

@ -0,0 +1,48 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(core,unboxed_closures)]
use std::marker::CovariantType;
// A erroneous variant of `run-pass/unboxed_closures-infer-recursive-fn.rs`
// where we attempt to perform mutation in the recursive function. This fails to compile
// because it winds up requiring `FnMut` which enforces linearity.
struct YCombinator<F,A,R> {
func: F,
marker: CovariantType<(A,R)>,
}
impl<F,A,R> YCombinator<F,A,R> {
fn new(f: F) -> YCombinator<F,A,R> {
YCombinator { func: f, marker: CovariantType }
}
}
impl<A,R,F : FnMut(&mut FnMut(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call_mut(&mut self, (arg,): (A,)) -> R {
(self.func)(self, arg)
//~^ ERROR cannot borrow `*self` as mutable more than once at a time
}
}
fn main() {
let mut counter = 0;
let factorial = |recur: &mut FnMut(u32) -> u32, arg: u32| -> u32 {
counter += 1;
if arg == 0 {1} else {arg * recur(arg-1)}
};
let mut factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
let mut r = factorial(10);
assert_eq!(3628800, r);
}

View file

@ -0,0 +1,29 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
fn main() {
let mut counter = 0;
{
// Here this must be inferred to FnMut so that it can mutate counter:
let mut tick1 = || counter += 1;
// In turn, tick2 must be inferred to FnMut so that it can call tick1:
let mut tick2 = || { tick1(); tick1(); };
tick2();
}
assert_eq!(counter, 2);
}

View file

@ -0,0 +1,25 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this `move`
// closure that is just called (`FnMut`).
fn main() {
let mut counter = 0;
let v = {
let mut tick = move || { counter += 1; counter };
tick();
tick()
};
assert_eq!(counter, 0);
assert_eq!(v, 2);
}

View file

@ -0,0 +1,24 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnMut`).
fn main() {
let mut counter = 0;
{
let mut tick = || counter += 1;
tick();
tick();
}
assert_eq!(counter, 2);
}

View file

@ -0,0 +1,37 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unsafe_destructor)]
// Test that we are able to infer a suitable kind for this `move`
// closure that is just called (`FnOnce`).
use std::mem;
struct DropMe<'a>(&'a mut i32);
#[unsafe_destructor]
impl<'a> Drop for DropMe<'a> {
fn drop(&mut self) {
*self.0 += 1;
}
}
fn main() {
let mut counter = 0;
{
let drop_me = DropMe(&mut counter);
let tick = move || mem::drop(drop_me);
tick();
}
assert_eq!(counter, 1);
}

View file

@ -0,0 +1,37 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unsafe_destructor)]
// Test that we are able to infer a suitable kind for this closure
// that is just called (`FnOnce`).
use std::mem;
struct DropMe<'a>(&'a mut i32);
#[unsafe_destructor]
impl<'a> Drop for DropMe<'a> {
fn drop(&mut self) {
*self.0 += 1;
}
}
fn main() {
let mut counter = 0;
{
let drop_me = DropMe(&mut counter);
let tick = || mem::drop(drop_me);
tick();
}
assert_eq!(counter, 1);
}

View file

@ -0,0 +1,47 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(core,unboxed_closures)]
use std::marker::CovariantType;
// Test that we are able to infer a suitable kind for a "recursive"
// closure. As far as I can tell, coding up a recursive closure
// requires the good ol' [Y Combinator].
//
// [Y Combinator]: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator
struct YCombinator<F,A,R> {
func: F,
marker: CovariantType<(A,R)>,
}
impl<F,A,R> YCombinator<F,A,R> {
fn new(f: F) -> YCombinator<F,A,R> {
YCombinator { func: f, marker: CovariantType }
}
}
impl<A,R,F : Fn(&Fn(A) -> R, A) -> R> Fn<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call(&self, (arg,): (A,)) -> R {
(self.func)(self, arg)
}
}
fn main() {
let factorial = |recur: &Fn(u32) -> u32, arg: u32| -> u32 {
if arg == 0 {1} else {arg * recur(arg-1)}
};
let factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
let r = factorial(10);
assert_eq!(3628800, r);
}