diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 242ab630a20ef..bf2f5bd22c463 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -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; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6767f77de84bb..e09d29b98b09a 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -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); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 902c029fef4bf..3764324734132 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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_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| { - rbml_w.emit_closure_type(ecx, &closure.closure_type); - encode_closure_kind(rbml_w, closure.kind) + 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( diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 188a56135ec33..4a0bed57433ae 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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) } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 0d86dbeadf4a7..1330ad86bfc92 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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( diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 5780b5b70f4a2..106c07baaa7a1 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -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(()) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 8e88e4338cd85..fb1d9fcada1ce 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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>>, + pub closure_kinds: RefCell>, + + /// Records the type of each closure. The def ID is the ID of the + /// expression defining the closure. + pub closure_tys: RefCell>>, pub node_lint_levels: RefCell>, @@ -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; + /// 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 - } - - Some(UpvarCapture::ByRef(borrow)) => { - mk_rptr(tcx, - tcx.mk_region(borrow.region), - ty::mt { - ty: freevar_ty, - mutbl: borrow.kind.to_mutbl_lossy(), - }) - } + 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(), + }) + } + }; - None => { - // FIXME(#16640) we should really return None here; - // but that requires better inference integration, - // for now gin up something. - freevar_ty + ClosureUpvar { + def: freevar.def, + span: freevar.span, + ty: freevar_ref_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 + { + Some(self.tcx.closure_kind(def_id)) } fn closure_type(&self, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6c723a583807e..15cf37dc2f2f0 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -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); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1b212aca33034..9ec3db0f602af 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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>, diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index a15ede095a709..8473ce1b797e3 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -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> { - if !ccx.tcx().closures.borrow().contains_key(&closure_id) { + if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) { // Not a closure. return None } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index a0dbc9c40a68d..5782b3987cb48 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -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 + { 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 + { + self.param_env.closure_kind(def_id) } fn closure_type(&self, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index cb4c880717bc8..c4b7ffb87296e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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], +pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat, + path: &ast::Path, fields: &'tcx [Spanned], 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>>, - 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]>, + 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], + fields: &'tcx [Spanned], struct_fields: &[ty::field<'tcx>], struct_id: ast::DefId, etc: bool) { diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index e37c1ab7f0cb7..93c6445606eb8 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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], + call_expr: &'tcx ast::Expr, + callee_expr: &'tcx ast::Expr, + arg_exprs: &'tcx [P], 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> { + 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> +{ // 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], + arg_exprs: &'tcx [P], 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_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + call_expr: &ast::Expr, + arg_exprs: &'tcx [P], + expected: Expectation<'tcx>, + fn_sig: ty::FnSig<'tcx>) +{ + // `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, - arg_exprs: &[P], - method_callee: ty::MethodCallee<'tcx>, - expected: Expectation<'tcx>) + callee_expr: &'tcx ast::Expr, + arg_exprs: &'tcx [P], + expected: Expectation<'tcx>, + method_callee: ty::MethodCallee<'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 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); - write_call(fcx, call_expr, output_type); } +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"); + } + } + } +} diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index bdae34e787850..808dbd4b31910 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -26,8 +26,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, _capture: ast::CaptureClause, opt_kind: Option, - 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, + decl: &'tcx ast::FnDecl, + body: &'tcx ast::Block, expected_sig: Option>) { 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); - - let closure = ty::Closure { - closure_type: fn_ty, - kind: kind, - }; + opt_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>( diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 56a32186c9eac..c326116cbd544 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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>) @@ -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 } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index d92cc1dfc1e95..88455b3385a1e 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -44,6 +44,9 @@ pub enum MethodError { // Multiple methods might apply. Ambiguity(Vec), + + // 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>, - call_expr: &ast::Expr, - self_expr: &ast::Expr) + call_expr: &'tcx ast::Expr, + self_expr: &'tcx ast::Expr) -> Result, MethodError> { debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})", diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 2e366f4450744..e9ea0921bc9e8 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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>, 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> { @@ -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| { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 3cf9a1a945668..bd5060c940e50 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -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, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9dcde1c2a0a50..c193e1ef48364 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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>>, method_map: MethodMap<'tcx>, upvar_capture_map: RefCell, - closures: RefCell>>, + closure_tys: RefCell>>, + closure_kinds: RefCell>, 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>, + + // 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>>>, +} + +trait DeferredCallResolution<'tcx> { + fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>); } +type DeferredCallResolutionHandler<'tcx> = Box+'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 + { + 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> + { + 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], + callee_expr: &'tcx ast::Expr, + args_no_rcvr: &'tcx [P], 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], + args: &'tcx [P], 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], + args: &'tcx [P], tps: &[P], 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, lhs: &'a ast::Expr, - rhs: Option<&P>, + rhs: Option<&'tcx P>, 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, - 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, + 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) -> Ty<'tcx> { + rhs: &'tcx P) -> 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, - 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) { + fn check_tup_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &'tcx ast::Expr, + lvalue_pref: LvaluePreference, + base: &'tcx ast::Expr, + idx: codemap::Spanned) { 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) { 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>) { + fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + id: ast::NodeId, + fields: &'tcx [ast::Field], + base_expr: &'tcx Option>) { // 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], - id: ast::NodeId) { +pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, + sp: Span, + vs: &'tcx [P], + 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], + vs: &'tcx [P], id: ast::NodeId, hint: attr::ReprAttr) -> Vec>> { diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 449220b1c8528..f7babadd41fd1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -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, } 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, } 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) + -> 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); } } diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 41b63830279a8..5cf71a9be6a02 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -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 { diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index f7e1afed8fc53..0eaecf8ac0574 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -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); } } diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs new file mode 100644 index 0000000000000..7398e6f1089bc --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs @@ -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 or the MIT license +// , 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 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() { } diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs new file mode 100644 index 0000000000000..f40c8fc747494 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs new file mode 100644 index 0000000000000..f993b8fa8c4d1 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs @@ -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 or the MIT license +// , 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 +} + diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs new file mode 100644 index 0000000000000..afbc141b5d246 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs @@ -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 or the MIT license +// , 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 +} + diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs new file mode 100644 index 0000000000000..9e4ed30799686 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs new file mode 100644 index 0000000000000..de17d25b4c360 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs new file mode 100644 index 0000000000000..0050fbdde2639 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs @@ -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 or the MIT license +// , 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 = Vec::new(); + let tick = || mem::drop(counter); + tick(); + tick(); //~ ERROR use of moved value: `tick` +} diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs new file mode 100644 index 0000000000000..f9709b8c59697 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs @@ -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 or the MIT license +// , 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 = Vec::new(); + let tick = move || mem::drop(counter); + tick(); + tick(); //~ ERROR use of moved value: `tick` +} diff --git a/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs new file mode 100644 index 0000000000000..215b2c6798e40 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs @@ -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 or the MIT license +// , 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 { + func: F, + marker: CovariantType<(A,R)>, +} + +impl YCombinator { + fn new(f: F) -> YCombinator { + YCombinator { func: f, marker: CovariantType } + } +} + +impl R, A) -> R> FnMut<(A,)> for YCombinator { + 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); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs new file mode 100644 index 0000000000000..09b8c8f4454b5 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs @@ -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 or the MIT license +// , 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); +} + diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs new file mode 100644 index 0000000000000..794527249bffa --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut.rs new file mode 100644 index 0000000000000..67f36b9a9203c --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs new file mode 100644 index 0000000000000..9f8fc80819bfd --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce.rs b/src/test/run-pass/unboxed-closures-infer-fnonce.rs new file mode 100644 index 0000000000000..f0f10139c5b86 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnonce.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs new file mode 100644 index 0000000000000..1f9b821178c46 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs @@ -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 or the MIT license +// , 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 { + func: F, + marker: CovariantType<(A,R)>, +} + +impl YCombinator { + fn new(f: F) -> YCombinator { + YCombinator { func: f, marker: CovariantType } + } +} + +impl R, A) -> R> Fn<(A,)> for YCombinator { + 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); +}