diff --git a/Makefile.in b/Makefile.in index b927c805220f4..f5bb3cb2ed00f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -125,6 +125,7 @@ ifdef TRACE CFG_RUSTC_FLAGS += -Z trace endif ifndef DEBUG_BORROWS + RUSTFLAGS_STAGE0 += -Z no-debug-borrows RUSTFLAGS_STAGE1 += -Z no-debug-borrows RUSTFLAGS_STAGE2 += -Z no-debug-borrows endif diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs index df67b1c9cc1df..b3da9b4f16b88 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -233,10 +233,10 @@ impl MutexArc { /// As unsafe_access(), but with a condvar, as sync::mutex.lock_cond(). #[inline] - pub unsafe fn unsafe_access_cond<'x, 'c, U>(&self, - blk: &fn(x: &'x mut T, - c: &'c Condvar) -> U) - -> U { + pub unsafe fn unsafe_access_cond(&self, + blk: &fn(x: &mut T, + c: &Condvar) -> U) + -> U { let state = self.x.get(); do (&(*state).lock).lock_cond |cond| { check_poison(true, (*state).failed); @@ -290,10 +290,10 @@ impl MutexArc { /// As unsafe_access_cond but safe and Freeze. #[inline] - pub fn access_cond<'x, 'c, U>(&self, - blk: &fn(x: &'x mut T, - c: &'c Condvar) -> U) - -> U { + pub fn access_cond(&self, + blk: &fn(x: &mut T, + c: &Condvar) -> U) + -> U { unsafe { self.unsafe_access_cond(blk) } } } @@ -402,9 +402,9 @@ impl RWArc { /// As write(), but with a condvar, as sync::rwlock.write_cond(). #[inline] - pub fn write_cond<'x, 'c, U>(&self, - blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) - -> U { + pub fn write_cond(&self, + blk: &fn(x: &mut T, c: &Condvar) -> U) + -> U { unsafe { let state = self.x.get(); do (*borrow_rwlock(state)).write_cond |cond| { @@ -554,9 +554,9 @@ impl<'self, T:Freeze + Send> RWWriteMode<'self, T> { } /// Access the pre-downgrade RWArc in write mode with a condvar. - pub fn write_cond<'x, 'c, U>(&mut self, - blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) - -> U { + pub fn write_cond(&mut self, + blk: &fn(x: &mut T, c: &Condvar) -> U) + -> U { match *self { RWWriteMode { data: &ref mut data, diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index c57cd134e01b6..6346e4856f2d1 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -239,6 +239,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "resolution", (), |_| middle::resolve::resolve_crate(sess, lang_items, crate)); + let named_region_map = time(time_passes, "lifetime resolution", (), + |_| middle::resolve_lifetime::crate(sess, crate)); + time(time_passes, "looking for entry point", (), |_| middle::entry::find_entry_point(sess, crate, ast_map)); @@ -246,13 +249,10 @@ pub fn phase_3_run_analysis_passes(sess: Session, freevars::annotate_freevars(def_map, crate)); let region_map = time(time_passes, "region resolution", (), |_| - middle::region::resolve_crate(sess, def_map, crate)); - - let rp_set = time(time_passes, "region parameterization inference", (), |_| - middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate)); + middle::region::resolve_crate(sess, crate)); - let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars, - region_map, rp_set, lang_items); + let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, freevars, + region_map, lang_items); // passes are timed inside typeck let (method_map, vtable_map) = typeck::check_crate( diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index b34829bf47fc1..c2d0251b23cc2 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -116,12 +116,12 @@ impl fold::ast_fold for StandardLibraryInjector { segments: ~[ ast::PathSegment { identifier: self.sess.ident_of("std"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }, ast::PathSegment { identifier: self.sess.ident_of("prelude"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }, ], diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 9541a03aff25e..b6ae41833777a 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -343,7 +343,7 @@ fn path_node(ids: ~[ast::Ident]) -> ast::Path { global: false, segments: ids.move_iter().map(|identifier| ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }).collect() } @@ -355,7 +355,7 @@ fn path_node_global(ids: ~[ast::Ident]) -> ast::Path { global: true, segments: ids.move_iter().map(|identifier| ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }).collect() } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index bc436abbf5660..8d273e638ee73 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -54,8 +54,10 @@ use syntax::diagnostic; pub mod middle { pub mod trans; pub mod ty; + pub mod ty_fold; pub mod subst; pub mod resolve; + pub mod resolve_lifetime; pub mod typeck; pub mod check_loop; pub mod check_match; diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 6294b6cb6e312..5e6d0f2761523 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -89,7 +89,7 @@ pub static tag_path_elt_name: uint = 0x43u; pub static tag_item_field: uint = 0x44u; pub static tag_struct_mut: uint = 0x45u; -pub static tag_region_param: uint = 0x46u; +pub static tag_item_variances: uint = 0x46; pub static tag_mod_impl_trait: uint = 0x47u; /* trait items contain tag_item_trait_method elements, @@ -193,6 +193,11 @@ pub static tag_path_elt_pretty_name: uint = 0x87; pub static tag_path_elt_pretty_name_ident: uint = 0x88; pub static tag_path_elt_pretty_name_extra: uint = 0x89; +pub static tag_region_param_def: uint = 0x100; +pub static tag_region_param_def_ident: uint = 0x101; +pub static tag_region_param_def_def_id: uint = 0x102; + + pub struct LinkMeta { name: @str, vers: @str, diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 48fe21b334395..fdda6b38462e3 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -14,7 +14,6 @@ use metadata::common::*; use metadata::cstore; use metadata::decoder; -use metadata; use middle::ty; use middle::typeck; @@ -144,6 +143,12 @@ pub fn get_trait_method_def_ids(cstore: @mut cstore::CStore, decoder::get_trait_method_def_ids(cdata, def.node) } +pub fn get_item_variances(cstore: @mut cstore::CStore, + def: ast::DefId) -> ty::ItemVariances { + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::get_item_variances(cdata, def.node) +} + pub fn get_provided_trait_methods(tcx: ty::ctxt, def: ast::DefId) -> ~[@ty::Method] { @@ -199,12 +204,6 @@ pub fn get_trait_def(tcx: ty::ctxt, def: ast::DefId) -> ty::TraitDef { decoder::get_trait_def(cdata, def.node, tcx) } -pub fn get_region_param(cstore: @mut metadata::cstore::CStore, - def: ast::DefId) -> Option { - let cdata = cstore::get_crate_data(cstore, def.crate); - return decoder::get_region_param(cdata, def.node); -} - pub fn get_field_type(tcx: ty::ctxt, class_id: ast::DefId, def: ast::DefId) -> ty::ty_param_bounds_and_ty { let cstore = tcx.cstore; @@ -224,7 +223,7 @@ pub fn get_field_type(tcx: ty::ctxt, class_id: ast::DefId, let ty = decoder::item_type(def, the_field, tcx, cdata); ty::ty_param_bounds_and_ty { generics: ty::Generics {type_param_defs: @~[], - region_param: None}, + region_param_defs: @[]}, ty: ty } } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 56abaa27cce2c..b63d9320bd6e0 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -25,7 +25,7 @@ use middle::ty; use middle::typeck; use middle::astencode::vtable_decoder_helpers; - +use std::at_vec; use std::u64; use std::rt::io; use std::rt::io::extensions::u64_from_be_bytes; @@ -252,9 +252,11 @@ fn item_trait_ref(doc: ebml::Doc, tcx: ty::ctxt, cdata: Cmd) -> ty::TraitRef { doc_trait_ref(tp, tcx, cdata) } -fn item_ty_param_defs(item: ebml::Doc, tcx: ty::ctxt, cdata: Cmd, +fn item_ty_param_defs(item: ebml::Doc, + tcx: ty::ctxt, + cdata: Cmd, tag: uint) - -> @~[ty::TypeParameterDef] { + -> @~[ty::TypeParameterDef] { let mut bounds = ~[]; do reader::tagged_docs(item, tag) |p| { let bd = parse_type_param_def_data( @@ -266,10 +268,23 @@ fn item_ty_param_defs(item: ebml::Doc, tcx: ty::ctxt, cdata: Cmd, @bounds } -fn item_ty_region_param(item: ebml::Doc) -> Option { - do reader::maybe_get_doc(item, tag_region_param).map |doc| { - let mut decoder = reader::Decoder(doc); - Decodable::decode(&mut decoder) +fn item_region_param_defs(item_doc: ebml::Doc, + tcx: ty::ctxt, + cdata: Cmd) + -> @[ty::RegionParameterDef] { + do at_vec::build(None) |push| { + do reader::tagged_docs(item_doc, tag_region_param_def) |rp_doc| { + let ident_str_doc = reader::get_doc(rp_doc, + tag_region_param_def_ident); + let ident = item_name(tcx.sess.intr(), ident_str_doc); + let def_id_doc = reader::get_doc(rp_doc, + tag_region_param_def_def_id); + let def_id = reader::with_doc_data(def_id_doc, parse_def_id); + let def_id = translate_def_id(cdata, def_id); + push(ty::RegionParameterDef { ident: ident, + def_id: def_id }); + true + }; } } @@ -393,7 +408,7 @@ pub fn get_trait_def(cdata: Cmd, let item_doc = lookup_item(item_id, cdata.data); let tp_defs = item_ty_param_defs(item_doc, tcx, cdata, tag_items_data_item_ty_param_bounds); - let rp = item_ty_region_param(item_doc); + let rp_defs = item_region_param_defs(item_doc, tcx, cdata); let mut bounds = ty::EmptyBuiltinBounds(); // Collect the builtin bounds from the encoded supertraits. // FIXME(#8559): They should be encoded directly. @@ -407,7 +422,7 @@ pub fn get_trait_def(cdata: Cmd, }; ty::TraitDef { generics: ty::Generics {type_param_defs: tp_defs, - region_param: rp}, + region_param_defs: rp_defs}, bounds: bounds, trait_ref: @item_trait_ref(item_doc, tcx, cdata) } @@ -417,33 +432,27 @@ pub fn get_type(cdata: Cmd, id: ast::NodeId, tcx: ty::ctxt) -> ty::ty_param_bounds_and_ty { let item = lookup_item(id, cdata.data); + let t = item_type(ast::DefId { crate: cdata.cnum, node: id }, item, tcx, cdata); - let tp_defs = if family_has_type_params(item_family(item)) { - item_ty_param_defs(item, tcx, cdata, tag_items_data_item_ty_param_bounds) - } else { @~[] }; - let rp = item_ty_region_param(item); + + let tp_defs = item_ty_param_defs(item, tcx, cdata, tag_items_data_item_ty_param_bounds); + let rp_defs = item_region_param_defs(item, tcx, cdata); + ty::ty_param_bounds_and_ty { generics: ty::Generics {type_param_defs: tp_defs, - region_param: rp}, + region_param_defs: rp_defs}, ty: t } } -pub fn get_region_param(cdata: Cmd, id: ast::NodeId) - -> Option { - - let item = lookup_item(id, cdata.data); - return item_ty_region_param(item); -} - pub fn get_type_param_count(data: @~[u8], id: ast::NodeId) -> uint { item_ty_param_count(lookup_item(id, data)) } pub fn get_impl_trait(cdata: Cmd, - id: ast::NodeId, - tcx: ty::ctxt) -> Option<@ty::TraitRef> + id: ast::NodeId, + tcx: ty::ctxt) -> Option<@ty::TraitRef> { let item_doc = lookup_item(id, cdata.data); do reader::maybe_get_doc(item_doc, tag_item_trait_ref).map |tp| { @@ -1044,6 +1053,7 @@ pub fn get_method(intr: @ident_interner, cdata: Cmd, id: ast::NodeId, let name = item_name(intr, method_doc); let type_param_defs = item_ty_param_defs(method_doc, tcx, cdata, tag_item_method_tps); + let rp_defs = item_region_param_defs(method_doc, tcx, cdata); let transformed_self_ty = doc_transformed_self_ty(method_doc, tcx, cdata); let fty = doc_method_fty(method_doc, tcx, cdata); let vis = item_visibility(method_doc); @@ -1054,7 +1064,7 @@ pub fn get_method(intr: @ident_interner, cdata: Cmd, id: ast::NodeId, name, ty::Generics { type_param_defs: type_param_defs, - region_param: None + region_param_defs: rp_defs, }, transformed_self_ty, fty, @@ -1078,6 +1088,14 @@ pub fn get_trait_method_def_ids(cdata: Cmd, result } +pub fn get_item_variances(cdata: Cmd, id: ast::NodeId) -> ty::ItemVariances { + let data = cdata.data; + let item_doc = lookup_item(id, data); + let variance_doc = reader::get_doc(item_doc, tag_item_variances); + let mut decoder = reader::Decoder(variance_doc); + Decodable::decode(&mut decoder) +} + pub fn get_provided_trait_methods(intr: @ident_interner, cdata: Cmd, id: ast::NodeId, tcx: ty::ctxt) -> ~[@ty::Method] { diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index aae6901e00aeb..fe9ca6a16f0c2 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -121,17 +121,6 @@ pub fn encode_def_id(ebml_w: &mut writer::Encoder, id: DefId) { ebml_w.wr_tagged_str(tag_def_id, def_to_str(id)); } -fn encode_region_param(ecx: &EncodeContext, - ebml_w: &mut writer::Encoder, - it: @ast::item) { - let opt_rp = ecx.tcx.region_paramd_items.find(&it.id); - for rp in opt_rp.iter() { - ebml_w.start_tag(tag_region_param); - rp.encode(ebml_w); - ebml_w.end_tag(); - } -} - #[deriving(Clone)] struct entry { val: T, @@ -205,11 +194,38 @@ fn encode_ty_type_param_defs(ebml_w: &mut writer::Encoder, } } +fn encode_region_param_defs(ebml_w: &mut writer::Encoder, + ecx: &EncodeContext, + params: @[ty::RegionParameterDef]) { + for param in params.iter() { + ebml_w.start_tag(tag_region_param_def); + + ebml_w.start_tag(tag_region_param_def_ident); + encode_name(ecx, ebml_w, param.ident); + ebml_w.end_tag(); + + ebml_w.wr_tagged_str(tag_region_param_def_def_id, + def_to_str(param.def_id)); + + ebml_w.end_tag(); + } +} + +fn encode_item_variances(ebml_w: &mut writer::Encoder, + ecx: &EncodeContext, + id: ast::NodeId) { + let v = ty::item_variances(ecx.tcx, ast_util::local_def(id)); + ebml_w.start_tag(tag_item_variances); + v.encode(ebml_w); + ebml_w.end_tag(); +} + fn encode_bounds_and_type(ebml_w: &mut writer::Encoder, ecx: &EncodeContext, tpt: &ty::ty_param_bounds_and_ty) { encode_ty_type_param_defs(ebml_w, ecx, tpt.generics.type_param_defs, tag_items_data_item_ty_param_bounds); + encode_region_param_defs(ebml_w, ecx, tpt.generics.region_param_defs); encode_type(ecx, ebml_w, tpt.ty); } @@ -976,7 +992,6 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id)); encode_name(ecx, ebml_w, item.ident); encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident)); - encode_region_param(ecx, ebml_w, item); encode_visibility(ebml_w, vis); ebml_w.end_tag(); } @@ -986,6 +1001,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 't'); + encode_item_variances(ebml_w, ecx, item.id); encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id)); encode_name(ecx, ebml_w, item.ident); encode_attributes(ebml_w, item.attrs); @@ -994,7 +1010,6 @@ fn encode_info_for_item(ecx: &EncodeContext, } (ecx.encode_inlined_item)(ecx, ebml_w, path, ii_item(item)); encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident)); - encode_region_param(ecx, ebml_w, item); // Encode inherent implementations for this enumeration. encode_inherent_implementations(ecx, ebml_w, def_id); @@ -1027,10 +1042,10 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_family(ebml_w, 'S'); encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id)); + encode_item_variances(ebml_w, ecx, item.id); encode_name(ecx, ebml_w, item.ident); encode_attributes(ebml_w, item.attrs); encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident)); - encode_region_param(ecx, ebml_w, item); encode_visibility(ebml_w, vis); /* Encode def_ids for each field and method @@ -1075,7 +1090,6 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'i'); - encode_region_param(ecx, ebml_w, item); encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id)); encode_name(ecx, ebml_w, item.ident); encode_attributes(ebml_w, item.attrs); @@ -1135,7 +1149,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'I'); - encode_region_param(ecx, ebml_w, item); + encode_item_variances(ebml_w, ecx, item.id); let trait_def = ty::lookup_trait_def(tcx, def_id); encode_ty_type_param_defs(ebml_w, ecx, trait_def.generics.type_param_defs, diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index b365e7a48795e..31561e730d541 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -48,7 +48,10 @@ pub enum DefIdSource { TypeWithId, // Identifies a type parameter (`fn foo() { ... }`). - TypeParameter + TypeParameter, + + // Identifies a region parameter (`fn foo<'X>() { ... }`). + RegionParameter, } type conv_did<'self> = &'self fn(source: DefIdSource, ast::DefId) -> ast::DefId; @@ -143,7 +146,7 @@ fn parse_path(st: &mut PState) -> @ast::Path { segments: idents.move_iter().map(|identifier| { ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect() @@ -165,7 +168,7 @@ fn parse_sigil(st: &mut PState) -> ast::Sigil { } } -fn parse_vstore(st: &mut PState) -> ty::vstore { +fn parse_vstore(st: &mut PState, conv: conv_did) -> ty::vstore { assert_eq!(next(st), '/'); let c = peek(st); @@ -178,22 +181,22 @@ fn parse_vstore(st: &mut PState) -> ty::vstore { match next(st) { '~' => ty::vstore_uniq, '@' => ty::vstore_box, - '&' => ty::vstore_slice(parse_region(st)), + '&' => ty::vstore_slice(parse_region(st, conv)), c => st.tcx.sess.bug(format!("parse_vstore(): bad input '{}'", c)) } } -fn parse_trait_store(st: &mut PState) -> ty::TraitStore { +fn parse_trait_store(st: &mut PState, conv: conv_did) -> ty::TraitStore { match next(st) { '~' => ty::UniqTraitStore, '@' => ty::BoxTraitStore, - '&' => ty::RegionTraitStore(parse_region(st)), + '&' => ty::RegionTraitStore(parse_region(st, conv)), c => st.tcx.sess.bug(format!("parse_trait_store(): bad input '{}'", c)) } } fn parse_substs(st: &mut PState, conv: conv_did) -> ty::substs { - let regions = parse_region_substs(st); + let regions = parse_region_substs(st, |x,y| conv(x,y)); let self_ty = parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y)) ); @@ -209,13 +212,13 @@ fn parse_substs(st: &mut PState, conv: conv_did) -> ty::substs { }; } -fn parse_region_substs(st: &mut PState) -> ty::RegionSubsts { +fn parse_region_substs(st: &mut PState, conv: conv_did) -> ty::RegionSubsts { match next(st) { 'e' => ty::ErasedRegions, 'n' => { let mut regions = opt_vec::Empty; while peek(st) != '.' { - let r = parse_region(st); + let r = parse_region(st, |x,y| conv(x,y)); regions.push(r); } assert_eq!(next(st), '.'); @@ -225,48 +228,65 @@ fn parse_region_substs(st: &mut PState) -> ty::RegionSubsts { } } -fn parse_bound_region(st: &mut PState) -> ty::bound_region { +fn parse_bound_region(st: &mut PState, conv: conv_did) -> ty::BoundRegion { match next(st) { - 's' => ty::br_self, - 'a' => { - let id = parse_uint(st); - assert_eq!(next(st), '|'); - ty::br_anon(id) - } - '[' => ty::br_named(st.tcx.sess.ident_of(parse_str(st, ']'))), - 'c' => { - let id = parse_uint(st) as int; - assert_eq!(next(st), '|'); - ty::br_cap_avoid(id, @parse_bound_region(st)) - }, - _ => fail!("parse_bound_region: bad input") + 'a' => { + let id = parse_uint(st); + assert_eq!(next(st), '|'); + ty::BrAnon(id) + } + '[' => { + let def = parse_def(st, RegionParameter, |x,y| conv(x,y)); + let ident = st.tcx.sess.ident_of(parse_str(st, ']')); + ty::BrNamed(def, ident) + } + 'f' => { + let id = parse_uint(st); + assert_eq!(next(st), '|'); + ty::BrFresh(id) + } + _ => fail!("parse_bound_region: bad input") } } -fn parse_region(st: &mut PState) -> ty::Region { +fn parse_region(st: &mut PState, conv: conv_did) -> ty::Region { match next(st) { 'b' => { - ty::re_bound(parse_bound_region(st)) + assert_eq!(next(st), '['); + let id = parse_uint(st) as int; + assert_eq!(next(st), '|'); + let br = parse_bound_region(st, |x,y| conv(x,y)); + assert_eq!(next(st), ']'); + ty::ReLateBound(id, br) + } + 'B' => { + assert_eq!(next(st), '['); + let node_id = parse_uint(st) as int; + assert_eq!(next(st), '|'); + let index = parse_uint(st); + assert_eq!(next(st), '|'); + let nm = st.tcx.sess.ident_of(parse_str(st, ']')); + ty::ReEarlyBound(node_id, index, nm) } 'f' => { assert_eq!(next(st), '['); let id = parse_uint(st) as int; assert_eq!(next(st), '|'); - let br = parse_bound_region(st); + let br = parse_bound_region(st, |x,y| conv(x,y)); assert_eq!(next(st), ']'); - ty::re_free(ty::FreeRegion {scope_id: id, + ty::ReFree(ty::FreeRegion {scope_id: id, bound_region: br}) } 's' => { let id = parse_uint(st) as int; assert_eq!(next(st), '|'); - ty::re_scope(id) + ty::ReScope(id) } 't' => { - ty::re_static + ty::ReStatic } 'e' => { - ty::re_static + ty::ReStatic } _ => fail!("parse_region: bad input") } @@ -331,37 +351,37 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { assert_eq!(next(st), '['); let def = parse_def(st, NominalType, |x,y| conv(x,y)); let substs = parse_substs(st, |x,y| conv(x,y)); - let store = parse_trait_store(st); + let store = parse_trait_store(st, |x,y| conv(x,y)); let mt = parse_mutability(st); let bounds = parse_bounds(st, |x,y| conv(x,y)); assert_eq!(next(st), ']'); return ty::mk_trait(st.tcx, def, substs, store, mt, bounds.builtin_bounds); } 'p' => { - let did = parse_def(st, TypeParameter, conv); + let did = parse_def(st, TypeParameter, |x,y| conv(x,y)); debug!("parsed ty_param: did={:?}", did); return ty::mk_param(st.tcx, parse_uint(st), did); } 's' => { - let did = parse_def(st, TypeParameter, conv); + let did = parse_def(st, TypeParameter, |x,y| conv(x,y)); return ty::mk_self(st.tcx, did); } - '@' => return ty::mk_box(st.tcx, parse_mt(st, conv)), - '~' => return ty::mk_uniq(st.tcx, parse_mt(st, conv)), - '*' => return ty::mk_ptr(st.tcx, parse_mt(st, conv)), + '@' => return ty::mk_box(st.tcx, parse_mt(st, |x,y| conv(x,y))), + '~' => return ty::mk_uniq(st.tcx, parse_mt(st, |x,y| conv(x,y))), + '*' => return ty::mk_ptr(st.tcx, parse_mt(st, |x,y| conv(x,y))), '&' => { - let r = parse_region(st); - let mt = parse_mt(st, conv); + let r = parse_region(st, |x,y| conv(x,y)); + let mt = parse_mt(st, |x,y| conv(x,y)); return ty::mk_rptr(st.tcx, r, mt); } - 'U' => return ty::mk_unboxed_vec(st.tcx, parse_mt(st, conv)), + 'U' => return ty::mk_unboxed_vec(st.tcx, parse_mt(st, |x,y| conv(x,y))), 'V' => { - let mt = parse_mt(st, conv); - let v = parse_vstore(st); + let mt = parse_mt(st, |x,y| conv(x,y)); + let v = parse_vstore(st, |x,y| conv(x,y)); return ty::mk_evec(st.tcx, mt, v); } 'v' => { - let v = parse_vstore(st); + let v = parse_vstore(st, |x,y| conv(x,y)); return ty::mk_estr(st.tcx, v); } 'T' => { @@ -372,10 +392,10 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { return ty::mk_tup(st.tcx, params); } 'f' => { - return ty::mk_closure(st.tcx, parse_closure_ty(st, conv)); + return ty::mk_closure(st.tcx, parse_closure_ty(st, |x,y| conv(x,y))); } 'F' => { - return ty::mk_bare_fn(st.tcx, parse_bare_fn_ty(st, conv)); + return ty::mk_bare_fn(st.tcx, parse_bare_fn_ty(st, |x,y| conv(x,y))); } 'Y' => return ty::mk_type(st.tcx), 'C' => { @@ -397,7 +417,7 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { pos: pos, .. *st }; - let tt = parse_ty(&mut ps, conv); + let tt = parse_ty(&mut ps, |x,y| conv(x,y)); st.tcx.rcache.insert(key, tt); return tt; } @@ -429,7 +449,7 @@ fn parse_mutability(st: &mut PState) -> ast::Mutability { fn parse_mt(st: &mut PState, conv: conv_did) -> ty::mt { let m = parse_mutability(st); - ty::mt { ty: parse_ty(st, conv), mutbl: m } + ty::mt { ty: parse_ty(st, |x,y| conv(x,y)), mutbl: m } } fn parse_def(st: &mut PState, source: DefIdSource, @@ -495,7 +515,7 @@ fn parse_closure_ty(st: &mut PState, conv: conv_did) -> ty::ClosureTy { let sigil = parse_sigil(st); let purity = parse_purity(next(st)); let onceness = parse_onceness(next(st)); - let region = parse_region(st); + let region = parse_region(st, |x,y| conv(x,y)); let bounds = parse_bounds(st, |x,y| conv(x,y)); let sig = parse_sig(st, |x,y| conv(x,y)); ty::ClosureTy { @@ -511,7 +531,7 @@ fn parse_closure_ty(st: &mut PState, conv: conv_did) -> ty::ClosureTy { fn parse_bare_fn_ty(st: &mut PState, conv: conv_did) -> ty::BareFnTy { let purity = parse_purity(next(st)); let abi = parse_abi_set(st); - let sig = parse_sig(st, conv); + let sig = parse_sig(st, |x,y| conv(x,y)); ty::BareFnTy { purity: purity, abis: abi, @@ -521,22 +541,23 @@ fn parse_bare_fn_ty(st: &mut PState, conv: conv_did) -> ty::BareFnTy { fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig { assert_eq!(next(st), '['); + let id = parse_uint(st) as int; + assert_eq!(next(st), '|'); let mut inputs = ~[]; while peek(st) != ']' { inputs.push(parse_ty(st, |x,y| conv(x,y))); } st.pos += 1u; // eat the ']' - let variadic = if peek(st) == 'A' { - st.pos += 1; // eat the 'A' - true - } else { false }; - let ret_ty = parse_ty(st, conv); - ty::FnSig { - bound_lifetime_names: opt_vec::Empty, // FIXME(#4846) - inputs: inputs, - output: ret_ty, - variadic: variadic - } + let variadic = match next(st) { + 'V' => true, + 'N' => false, + r => fail!(format!("Bad variadic: {}", r)), + }; + let ret_ty = parse_ty(st, |x,y| conv(x,y)); + ty::FnSig {binder_id: id, + inputs: inputs, + output: ret_ty, + variadic: variadic} } // Rust metadata parsing diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 5397bf0e768d8..d304db0935ac5 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -155,35 +155,51 @@ fn enc_region_substs(w: @mut MemWriter, cx: @ctxt, substs: &ty::RegionSubsts) { fn enc_region(w: @mut MemWriter, cx: @ctxt, r: ty::Region) { match r { - ty::re_bound(br) => { - mywrite!(w, "b"); + ty::ReLateBound(id, br) => { + mywrite!(w, "b[{}|", id); enc_bound_region(w, cx, br); + mywrite!(w, "]"); + } + ty::ReEarlyBound(node_id, index, ident) => { + mywrite!(w, "B[{}|{}|{}]", + node_id, + index, + cx.tcx.sess.str_of(ident)); } - ty::re_free(ref fr) => { + ty::ReFree(ref fr) => { mywrite!(w, "f[{}|", fr.scope_id); enc_bound_region(w, cx, fr.bound_region); mywrite!(w, "]"); } - ty::re_scope(nid) => mywrite!(w, "s{}|", nid), - ty::re_static => mywrite!(w, "t"), - ty::re_empty => mywrite!(w, "e"), - ty::re_infer(_) => { + ty::ReScope(nid) => { + mywrite!(w, "s{}|", nid); + } + ty::ReStatic => { + mywrite!(w, "t"); + } + ty::ReEmpty => { + mywrite!(w, "e"); + } + ty::ReInfer(_) => { // these should not crop up after typeck cx.diag.handler().bug("Cannot encode region variables"); } } } -fn enc_bound_region(w: @mut MemWriter, cx: @ctxt, br: ty::bound_region) { +fn enc_bound_region(w: @mut MemWriter, cx: @ctxt, br: ty::BoundRegion) { match br { - ty::br_self => mywrite!(w, "s"), - ty::br_anon(idx) => mywrite!(w, "a{}|", idx), - ty::br_named(s) => mywrite!(w, "[{}]", cx.tcx.sess.str_of(s)), - ty::br_cap_avoid(id, br) => { - mywrite!(w, "c{}|", id); - enc_bound_region(w, cx, *br); - } - ty::br_fresh(id) => mywrite!(w, "{}", id), + ty::BrAnon(idx) => { + mywrite!(w, "a{}|", idx); + } + ty::BrNamed(d, s) => { + mywrite!(w, "[{}|{}]", + (cx.ds)(d), + cx.tcx.sess.str_of(s)); + } + ty::BrFresh(id) => { + mywrite!(w, "f{}|", id); + } } } @@ -366,13 +382,15 @@ fn enc_closure_ty(w: @mut MemWriter, cx: @ctxt, ft: &ty::ClosureTy) { } fn enc_fn_sig(w: @mut MemWriter, cx: @ctxt, fsig: &ty::FnSig) { - mywrite!(w, "["); + mywrite!(w, "[{}|", fsig.binder_id); for ty in fsig.inputs.iter() { enc_ty(w, cx, *ty); } mywrite!(w, "]"); if fsig.variadic { - mywrite!(w, "A"); + mywrite!(w, "V"); + } else { + mywrite!(w, "N"); } enc_ty(w, cx, fsig.output); } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index cb8c7b3262fbf..09c0951b9e3c5 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -15,7 +15,8 @@ use driver::session::Session; use e = metadata::encoder; use metadata::decoder; use metadata::tydecode; -use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter}; +use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter, + RegionParameter}; use metadata::tyencode; use middle::freevars::freevar_entry; use middle::typeck::{method_origin, method_map_entry}; @@ -234,6 +235,12 @@ impl tr for ast::DefId { } } +impl tr for Option { + fn tr(&self, xcx: @ExtendedDecodeContext) -> Option { + self.map(|d| xcx.tr_def_id(d)) + } +} + impl tr for Span { fn tr(&self, xcx: @ExtendedDecodeContext) -> Span { xcx.tr_span(*self) @@ -469,24 +476,28 @@ impl tr for ty::AutoRef { impl tr for ty::Region { fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::Region { match *self { - ty::re_bound(br) => ty::re_bound(br.tr(xcx)), - ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)), - ty::re_empty | ty::re_static | ty::re_infer(*) => *self, - ty::re_free(ref fr) => { - ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id), + ty::ReLateBound(id, br) => ty::ReLateBound(xcx.tr_id(id), + br.tr(xcx)), + ty::ReEarlyBound(id, index, ident) => ty::ReEarlyBound(xcx.tr_id(id), + index, + ident), + ty::ReScope(id) => ty::ReScope(xcx.tr_id(id)), + ty::ReEmpty | ty::ReStatic | ty::ReInfer(*) => *self, + ty::ReFree(ref fr) => { + ty::ReFree(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id), bound_region: fr.bound_region.tr(xcx)}) } } } } -impl tr for ty::bound_region { - fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::bound_region { +impl tr for ty::BoundRegion { + fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::BoundRegion { match *self { - ty::br_anon(_) | ty::br_named(_) | ty::br_self | - ty::br_fresh(_) => *self, - ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id), - @br.tr(xcx)) + ty::BrAnon(_) | + ty::BrFresh(_) => *self, + ty::BrNamed(id, ident) => ty::BrNamed(xcx.tr_def_id(id), + ident), } } } @@ -821,8 +832,8 @@ impl ebml_writer_helpers for writer::Encoder { this.emit_type_param_def(ecx, type_param_def); } } - do this.emit_struct_field("region_param", 1) |this| { - tpbt.generics.region_param.encode(this); + do this.emit_struct_field("region_param_defs", 1) |this| { + tpbt.generics.region_param_defs.encode(this); } } } @@ -1086,6 +1097,8 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { // are not used during trans. return do self.read_opaque |this, doc| { + debug!("read_ty({})", type_string(doc)); + let ty = tydecode::parse_ty_data( *doc.data, xcx.dcx.cdata.cnum, @@ -1093,10 +1106,6 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { xcx.dcx.tcx, |s, a| this.convert_def_id(xcx, s, a)); - debug!("read_ty({}) = {}", - type_string(doc), - ty_to_str(xcx.dcx.tcx, ty)); - ty }; @@ -1139,8 +1148,8 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { @this.read_to_vec(|this| this.read_type_param_def(xcx)) }), - region_param: - this.read_struct_field("region_param", + region_param_defs: + this.read_struct_field("region_param_defs", 1, |this| { Decodable::decode(this) @@ -1161,7 +1170,6 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { did: ast::DefId) -> ast::DefId { /*! - * * Converts a def-id that appears in a type. The correct * translation will depend on what kind of def-id this is. * This is a subtle point: type definitions are not @@ -1172,10 +1180,25 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { * However, *type parameters* are cloned along with the function * they are attached to. So we should translate those def-ids * to refer to the new, cloned copy of the type parameter. + * We only see references to free type parameters in the body of + * an inlined function. In such cases, we need the def-id to + * be a local id so that the TypeContents code is able to lookup + * the relevant info in the ty_param_defs table. + * + * *Region parameters*, unfortunately, are another kettle of fish. + * In such cases, def_id's can appear in types to distinguish + * shadowed bound regions and so forth. It doesn't actually + * matter so much what we do to these, since regions are erased + * at trans time, but it's good to keep them consistent just in + * case. We translate them with `tr_def_id()` which will map + * the crate numbers back to the original source crate. + * + * It'd be really nice to refactor the type repr to not include + * def-ids so that all these distinctions were unnecessary. */ let r = match source { - NominalType | TypeWithId => xcx.tr_def_id(did), + NominalType | TypeWithId | RegionParameter => xcx.tr_def_id(did), TypeParameter => xcx.tr_intern_def_id(did) }; debug!("convert_def_id(source={:?}, did={:?})={:?}", source, did, r); diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index a0c6fdc32255f..a5f1709058c46 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -199,7 +199,7 @@ impl<'self> GuaranteeLifetimeContext<'self> { // Make sure that the loan does not exceed the maximum time // that we can root the value, dynamically. - let root_region = ty::re_scope(self.root_scope_id); + let root_region = ty::ReScope(self.root_scope_id); if !self.bccx.is_subregion_of(self.loan_region, root_region) { self.report_error( err_out_of_root_scope(root_region, self.loan_region)); @@ -208,9 +208,9 @@ impl<'self> GuaranteeLifetimeContext<'self> { // Extract the scope id that indicates how long the rooting is required let root_scope = match self.loan_region { - ty::re_scope(id) => id, + ty::ReScope(id) => id, _ => { - // the check above should fail for anything is not re_scope + // the check above should fail for anything is not ReScope self.bccx.tcx.sess.span_bug( cmt_base.span, format!("Cannot issue root for scope region: {:?}", @@ -260,12 +260,12 @@ impl<'self> GuaranteeLifetimeContext<'self> { note_and_explain_region( self.bccx.tcx, "managed value only needs to be frozen for ", - ty::re_scope(root_scope), + ty::ReScope(root_scope), "..."); note_and_explain_region( self.bccx.tcx, "...but due to Issue #6248, it will be frozen for ", - ty::re_scope(cleanup_scope), + ty::ReScope(cleanup_scope), ""); } @@ -324,13 +324,13 @@ impl<'self> GuaranteeLifetimeContext<'self> { match cmt.cat { mc::cat_rvalue(cleanup_scope_id) => { - ty::re_scope(cleanup_scope_id) + ty::ReScope(cleanup_scope_id) } mc::cat_copied_upvar(_) => { - ty::re_scope(self.item_scope_id) + ty::ReScope(self.item_scope_id) } mc::cat_static_item => { - ty::re_static + ty::ReStatic } mc::cat_local(local_id) | mc::cat_arg(local_id) | @@ -338,7 +338,7 @@ impl<'self> GuaranteeLifetimeContext<'self> { self.bccx.tcx.region_maps.encl_region(local_id) } mc::cat_deref(_, _, mc::unsafe_ptr(*)) => { - ty::re_static + ty::ReStatic } mc::cat_deref(_, _, mc::region_ptr(_, r)) => { r diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index dd161b189da27..56c3417852299 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -277,7 +277,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, // Currently these do not use adjustments, so we have to // hardcode this check here (note that the receiver DOES use // adjustments). - let scope_r = ty::re_scope(ex.id); + let scope_r = ty::ReScope(ex.id); let arg_cmt = this.bccx.cat_expr(arg); this.guarantee_valid(arg.id, arg.span, @@ -441,7 +441,7 @@ impl<'self> GatherLoanCtxt<'self> { // a loan for the empty region can never be dereferenced, so // it is always safe - if loan_region == ty::re_empty { + if loan_region == ty::ReEmpty { return; } @@ -470,10 +470,10 @@ impl<'self> GatherLoanCtxt<'self> { restrictions::SafeIf(loan_path, restrictions) => { let loan_scope = match loan_region { - ty::re_scope(id) => id, - ty::re_free(ref fr) => fr.scope_id, + ty::ReScope(id) => id, + ty::ReFree(ref fr) => fr.scope_id, - ty::re_static => { + ty::ReStatic => { // If we get here, an error must have been // reported in // `lifetime::guarantee_lifetime()`, because @@ -485,9 +485,10 @@ impl<'self> GatherLoanCtxt<'self> { return; } - ty::re_empty | - ty::re_bound(*) | - ty::re_infer(*) => { + ty::ReEmpty | + ty::ReLateBound(*) | + ty::ReEarlyBound(*) | + ty::ReInfer(*) => { self.tcx().sess.span_bug( cmt.span, format!("Invalid borrow lifetime: {:?}", loan_region)); @@ -714,7 +715,7 @@ impl<'self> GatherLoanCtxt<'self> { let cmt_discr = match arm_match_ids { None => cmt, Some((arm_id, match_id)) => { - let arm_scope = ty::re_scope(arm_id); + let arm_scope = ty::ReScope(arm_id); if self.bccx.is_subregion_of(scope_r, arm_scope) { self.bccx.cat_discr(cmt, match_id) } else { diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index fd5114889fddb..a570160ce9572 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -540,12 +540,12 @@ pub fn check_cast_for_escaping_regions( target_regions.push(r); } }, - |_| true); + |_| ()); // Check, based on the region associated with the trait, whether it can // possibly escape the enclosing fn item (note that all type parameters // must have been declared on the enclosing fn item). - if target_regions.iter().any(|r| is_re_scope(*r)) { + if target_regions.iter().any(|r| is_ReScope(*r)) { return; /* case (1) */ } @@ -582,12 +582,11 @@ pub fn check_cast_for_escaping_regions( } _ => {} } - true }); - fn is_re_scope(r: ty::Region) -> bool { + fn is_ReScope(r: ty::Region) -> bool { match r { - ty::re_scope(*) => true, + ty::ReScope(*) => true, _ => false } } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 70aa05848f366..eeedd25adac28 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -781,7 +781,7 @@ impl<'self> Visitor<()> for PrivacyVisitor<'self> { debug!("privacy - list {}", pid.node.id); let seg = ast::PathSegment { identifier: pid.node.name, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }; let segs = ~[seg]; diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 29acfe1438138..32ac6ff549213 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -22,22 +22,14 @@ Most of the documentation on regions can be found in use driver::session::Session; -use metadata::csearch; -use middle::resolve; -use middle::ty::{region_variance, rv_covariant, rv_invariant}; -use middle::ty::{rv_contravariant, FreeRegion}; +use middle::ty::{FreeRegion}; use middle::ty; use std::hashmap::{HashMap, HashSet}; -use syntax::ast_map; use syntax::codemap::Span; -use syntax::print::pprust; -use syntax::parse::token; -use syntax::parse::token::special_idents; use syntax::{ast, visit}; use syntax::visit::{Visitor,fn_kind}; use syntax::ast::{Block,item,fn_decl,NodeId,Arm,Pat,Stmt,Expr,Local}; -use syntax::ast::{Ty,TypeMethod,struct_field}; /** The region maps encode information about region relationships. @@ -74,7 +66,6 @@ pub struct Context { struct RegionResolutionVisitor { sess: Session, - def_map: resolve::DefMap, // Generated maps: region_maps: @mut RegionMaps, @@ -146,7 +137,7 @@ impl RegionMaps { pub fn encl_region(&self, id: ast::NodeId) -> ty::Region { //! Returns the narrowest scope region that encloses `id`, if any. - ty::re_scope(self.encl_scope(id)) + ty::ReScope(self.encl_scope(id)) } pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId) @@ -236,19 +227,19 @@ impl RegionMaps { sub_region == super_region || { match (sub_region, super_region) { - (_, ty::re_static) => { + (_, ty::ReStatic) => { true } - (ty::re_scope(sub_scope), ty::re_scope(super_scope)) => { + (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => { self.is_subscope_of(sub_scope, super_scope) } - (ty::re_scope(sub_scope), ty::re_free(ref fr)) => { + (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => { self.is_subscope_of(sub_scope, fr.scope_id) } - (ty::re_free(sub_fr), ty::re_free(super_fr)) => { + (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => { self.sub_free_region(sub_fr, super_fr) } @@ -504,7 +495,6 @@ impl Visitor for RegionResolutionVisitor { } pub fn resolve_crate(sess: Session, - def_map: resolve::DefMap, crate: &ast::Crate) -> @mut RegionMaps { let region_maps = @mut RegionMaps { @@ -516,483 +506,9 @@ pub fn resolve_crate(sess: Session, var_parent: None}; let mut visitor = RegionResolutionVisitor { sess: sess, - def_map: def_map, region_maps: region_maps, }; visit::walk_crate(&mut visitor, crate, cx); return region_maps; } -// ___________________________________________________________________________ -// Determining region parameterization -// -// Infers which type defns must be region parameterized---this is done -// by scanning their contents to see whether they reference a region -// type, directly or indirectly. This is a fixed-point computation. -// -// We do it in two passes. First we walk the AST and construct a map -// from each type defn T1 to other defns which make use of it. For example, -// if we have a type like: -// -// type S = *int; -// type T = S; -// -// Then there would be a map entry from S to T. During the same walk, -// we also construct add any types that reference regions to a set and -// a worklist. We can then process the worklist, propagating indirect -// dependencies until a fixed point is reached. - -pub type region_paramd_items = @mut HashMap; - -#[deriving(Eq)] -pub struct region_dep { - ambient_variance: region_variance, - id: ast::NodeId -} - -pub struct DetermineRpCtxt { - sess: Session, - ast_map: ast_map::map, - def_map: resolve::DefMap, - region_paramd_items: region_paramd_items, - dep_map: @mut HashMap, - worklist: ~[ast::NodeId], - - // the innermost enclosing item id - item_id: ast::NodeId, - - // true when we are within an item but not within a method. - // see long discussion on region_is_relevant(). - anon_implies_rp: bool, - - // encodes the context of the current type; invariant if - // mutable, covariant otherwise - ambient_variance: region_variance, -} - -pub fn join_variance(variance1: region_variance, - variance2: region_variance) - -> region_variance { - match (variance1, variance2) { - (rv_invariant, _) => {rv_invariant} - (_, rv_invariant) => {rv_invariant} - (rv_covariant, rv_contravariant) => {rv_invariant} - (rv_contravariant, rv_covariant) => {rv_invariant} - (rv_covariant, rv_covariant) => {rv_covariant} - (rv_contravariant, rv_contravariant) => {rv_contravariant} - } -} - -/// Combines the ambient variance with the variance of a -/// particular site to yield the final variance of the reference. -/// -/// Example: if we are checking function arguments then the ambient -/// variance is contravariant. If we then find a `&'r T` pointer, `r` -/// appears in a co-variant position. This implies that this -/// occurrence of `r` is contra-variant with respect to the current -/// item, and hence the function returns `rv_contravariant`. -pub fn add_variance(ambient_variance: region_variance, - variance: region_variance) - -> region_variance { - match (ambient_variance, variance) { - (rv_invariant, _) => rv_invariant, - (_, rv_invariant) => rv_invariant, - (rv_covariant, c) => c, - (c, rv_covariant) => c, - (rv_contravariant, rv_contravariant) => rv_covariant - } -} - -impl DetermineRpCtxt { - pub fn add_variance(&self, variance: region_variance) -> region_variance { - add_variance(self.ambient_variance, variance) - } - - /// Records that item `id` is region-parameterized with the - /// variance `variance`. If `id` was already parameterized, then - /// the new variance is joined with the old variance. - pub fn add_rp(&mut self, id: ast::NodeId, variance: region_variance) { - assert!(id != 0); - let old_variance = self.region_paramd_items.find(&id).map(|x| *x); - let joined_variance = match old_variance { - None => variance, - Some(v) => join_variance(v, variance) - }; - - debug!("add_rp() variance for {}: {:?} == {:?} ^ {:?}", - ast_map::node_id_to_str(self.ast_map, id, - token::get_ident_interner()), - joined_variance, old_variance, variance); - - if Some(joined_variance) != old_variance { - let region_paramd_items = self.region_paramd_items; - region_paramd_items.insert(id, joined_variance); - self.worklist.push(id); - } - } - - /// Indicates that the region-parameterization of the current item - /// is dependent on the region-parameterization of the item - /// `from`. Put another way, it indicates that the current item - /// contains a value of type `from`, so if `from` is - /// region-parameterized, so is the current item. - pub fn add_dep(&mut self, from: ast::NodeId) { - debug!("add dependency from {} -> {} ({} -> {}) with variance {:?}", - from, self.item_id, - ast_map::node_id_to_str(self.ast_map, from, - token::get_ident_interner()), - ast_map::node_id_to_str(self.ast_map, self.item_id, - token::get_ident_interner()), - self.ambient_variance); - let vec = do self.dep_map.find_or_insert_with(from) |_| { - @mut ~[] - }; - let dep = region_dep { - ambient_variance: self.ambient_variance, - id: self.item_id - }; - if !vec.iter().any(|x| x == &dep) { vec.push(dep); } - } - - // Determines whether a reference to a region that appears in the - // AST implies that the enclosing type is region-parameterized (RP). - // This point is subtle. Here are some examples to make it more - // concrete. - // - // 1. impl foo for &int { ... } - // 2. impl foo for &'self int { ... } - // 3. impl foo for bar { fn m(@self) -> &'self int { ... } } - // 4. impl foo for bar { fn m(&self) -> &'self int { ... } } - // 5. impl foo for bar { fn m(&self) -> &int { ... } } - // - // In case 1, the anonymous region is being referenced, - // but it appears in a context where the anonymous region - // resolves to self, so the impl foo is RP. - // - // In case 2, the self parameter is written explicitly. - // - // In case 3, the method refers to the region `self`, so that - // implies that the impl must be region parameterized. (If the - // type bar is not region parameterized, that is an error, because - // the self region is effectively unconstrained, but that is - // detected elsewhere). - // - // In case 4, the method refers to the region `self`, but the - // `self` region is bound by the `&self` receiver, and so this - // does not require that `bar` be RP. - // - // In case 5, the anonymous region is referenced, but it - // bound by the method, so it does not refer to self. This impl - // need not be region parameterized. - // - // Normally, & or &self implies that the enclosing item is RP. - // However, within a function, & is always bound. Within a method - // with &self type, &self is also bound. We detect those last two - // cases via flags (anon_implies_rp and self_implies_rp) that are - // true when the anon or self region implies RP. - pub fn region_is_relevant(&self, r: &Option) -> bool { - match r { - &None => { - self.anon_implies_rp - } - &Some(ref l) if l.ident == special_idents::statik => { - false - } - &Some(ref l) if l.ident == special_idents::self_ => { - true - } - &Some(_) => { - false - } - } - } - - pub fn with(@mut self, - item_id: ast::NodeId, - anon_implies_rp: bool, - f: &fn()) { - let old_item_id = self.item_id; - let old_anon_implies_rp = self.anon_implies_rp; - self.item_id = item_id; - self.anon_implies_rp = anon_implies_rp; - debug!("with_item_id({}, {})", - item_id, - anon_implies_rp); - let _i = ::util::common::indenter(); - f(); - self.item_id = old_item_id; - self.anon_implies_rp = old_anon_implies_rp; - } - - pub fn with_ambient_variance(@mut self, - variance: region_variance, - f: &fn()) { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.add_variance(variance); - f(); - self.ambient_variance = old_ambient_variance; - } -} - -fn determine_rp_in_item(visitor: &mut DetermineRpVisitor, - item: @ast::item) { - do visitor.cx.with(item.id, true) { - visit::walk_item(visitor, item, ()); - } -} - -fn determine_rp_in_fn(visitor: &mut DetermineRpVisitor, - fk: &visit::fn_kind, - decl: &ast::fn_decl, - body: &ast::Block, - _: Span, - _: ast::NodeId) { - let cx = visitor.cx; - do cx.with(cx.item_id, false) { - do cx.with_ambient_variance(rv_contravariant) { - for a in decl.inputs.iter() { - visitor.visit_ty(&a.ty, ()); - } - } - visitor.visit_ty(&decl.output, ()); - let generics = visit::generics_of_fn(fk); - visitor.visit_generics(&generics, ()); - visitor.visit_block(body, ()); - } -} - -fn determine_rp_in_ty_method(visitor: &mut DetermineRpVisitor, - ty_m: &ast::TypeMethod) { - let cx = visitor.cx; - do cx.with(cx.item_id, false) { - visit::walk_ty_method(visitor, ty_m, ()); - } -} - -fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor, - ty: &ast::Ty) { - let cx = visitor.cx; - - // we are only interested in types that will require an item to - // be region-parameterized. if cx.item_id is zero, then this type - // is not a member of a type defn nor is it a constitutent of an - // impl etc. So we can ignore it and its components. - if cx.item_id == 0 { return; } - - // if this type directly references a region pointer like &'r ty, - // add to the worklist/set. Note that &'r ty is contravariant with - // respect to &r, because &'r ty can be used whereever a *smaller* - // region is expected (and hence is a supertype of those - // locations) - let sess = cx.sess; - match ty.node { - ast::ty_rptr(ref r, _) => { - debug!("referenced rptr type {}", - pprust::ty_to_str(ty, sess.intr())); - - if cx.region_is_relevant(r) { - let rv = cx.add_variance(rv_contravariant); - cx.add_rp(cx.item_id, rv) - } - } - - ast::ty_closure(ref f) => { - debug!("referenced fn type: {}", - pprust::ty_to_str(ty, sess.intr())); - match f.region { - Some(_) => { - if cx.region_is_relevant(&f.region) { - let rv = cx.add_variance(rv_contravariant); - cx.add_rp(cx.item_id, rv) - } - } - None => { - if f.sigil == ast::BorrowedSigil && cx.anon_implies_rp { - let rv = cx.add_variance(rv_contravariant); - cx.add_rp(cx.item_id, rv) - } - } - } - } - - _ => {} - } - - // if this references another named type, add the dependency - // to the dep_map. If the type is not defined in this crate, - // then check whether it is region-parameterized and consider - // that as a direct dependency. - match ty.node { - ast::ty_path(ref path, _, id) => { - match cx.def_map.find(&id) { - Some(&ast::DefTy(did)) | - Some(&ast::DefTrait(did)) | - Some(&ast::DefStruct(did)) => { - if did.crate == ast::LOCAL_CRATE { - if cx.region_is_relevant(&path.segments.last().lifetime) { - cx.add_dep(did.node); - } - } else { - let cstore = sess.cstore; - match csearch::get_region_param(cstore, did) { - None => {} - Some(variance) => { - debug!("reference to external, rp'd type {}", - pprust::ty_to_str(ty, sess.intr())); - if cx.region_is_relevant(&path.segments.last().lifetime) { - let rv = cx.add_variance(variance); - cx.add_rp(cx.item_id, rv) - } - } - } - } - } - _ => {} - } - } - _ => {} - } - - match ty.node { - ast::ty_box(ref mt) | ast::ty_uniq(ref mt) | ast::ty_vec(ref mt) | - ast::ty_rptr(_, ref mt) | ast::ty_ptr(ref mt) => { - visit_mt(visitor, mt); - } - - ast::ty_path(ref path, _, _) => { - // type parameters are---for now, anyway---always invariant - do cx.with_ambient_variance(rv_invariant) { - for tp in path.segments.iter().flat_map(|s| s.types.iter()) { - visitor.visit_ty(tp, ()); - } - } - } - - ast::ty_closure(@ast::TyClosure {decl: ref decl, _}) | - ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => { - // fn() binds the & region, so do not consider &T types that - // appear *inside* a fn() type to affect the enclosing item: - do cx.with(cx.item_id, false) { - // parameters are contravariant - do cx.with_ambient_variance(rv_contravariant) { - for a in decl.inputs.iter() { - visitor.visit_ty(&a.ty, ()); - } - } - visitor.visit_ty(&decl.output, ()); - } - } - - _ => { - visit::walk_ty(visitor, ty, ()); - } - } - - fn visit_mt(visitor: &mut DetermineRpVisitor, - mt: &ast::mt) { - let cx = visitor.cx; - // mutability is invariant - if mt.mutbl == ast::MutMutable { - do cx.with_ambient_variance(rv_invariant) { - visitor.visit_ty(mt.ty, ()); - } - } else { - visitor.visit_ty(mt.ty, ()); - } - } -} - -fn determine_rp_in_struct_field(visitor: &mut DetermineRpVisitor, - cm: @ast::struct_field) { - visit::walk_struct_field(visitor, cm, ()); -} - -struct DetermineRpVisitor { - cx: @mut DetermineRpCtxt -} - -impl Visitor<()> for DetermineRpVisitor { - - fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl, - b:&Block, s:Span, n:NodeId, _:()) { - determine_rp_in_fn(self, fk, fd, b, s, n); - } - fn visit_item(&mut self, i:@item, _:()) { - determine_rp_in_item(self, i); - } - fn visit_ty(&mut self, t:&Ty, _:()) { - determine_rp_in_ty(self, t); - } - fn visit_ty_method(&mut self, t:&TypeMethod, _:()) { - determine_rp_in_ty_method(self, t); - } - fn visit_struct_field(&mut self, s:@struct_field, _:()) { - determine_rp_in_struct_field(self, s); - } - -} - -pub fn determine_rp_in_crate(sess: Session, - ast_map: ast_map::map, - def_map: resolve::DefMap, - crate: &ast::Crate) - -> region_paramd_items { - let cx = @mut DetermineRpCtxt { - sess: sess, - ast_map: ast_map, - def_map: def_map, - region_paramd_items: @mut HashMap::new(), - dep_map: @mut HashMap::new(), - worklist: ~[], - item_id: 0, - anon_implies_rp: false, - ambient_variance: rv_covariant - }; - - // Gather up the base set, worklist and dep_map - let mut visitor = DetermineRpVisitor { cx: cx }; - visit::walk_crate(&mut visitor, crate, ()); - - // Propagate indirect dependencies - // - // Each entry in the worklist is the id of an item C whose region - // parameterization has been updated. So we pull ids off of the - // worklist, find the current variance, and then iterate through - // all of the dependent items (that is, those items that reference - // C). For each dependent item D, we combine the variance of C - // with the ambient variance where the reference occurred and then - // update the region-parameterization of D to reflect the result. - { - let cx = &mut *cx; - while cx.worklist.len() != 0 { - let c_id = cx.worklist.pop(); - let c_variance = cx.region_paramd_items.get_copy(&c_id); - debug!("popped {} from worklist", c_id); - match cx.dep_map.find(&c_id) { - None => {} - Some(deps) => { - for dep in deps.iter() { - let v = add_variance(dep.ambient_variance, c_variance); - cx.add_rp(dep.id, v); - } - } - } - } - } - - debug!("{}", { - debug!("Region variance results:"); - let region_paramd_items = cx.region_paramd_items; - for (&key, &value) in region_paramd_items.iter() { - debug!("item {:?} ({}) is parameterized with variance {:?}", - key, - ast_map::node_id_to_str(ast_map, key, - token::get_ident_interner()), - value); - } - "----" - }); - - // return final set - return cx.region_paramd_items; -} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 9f848b4c2276c..23fef5e351674 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -4182,7 +4182,7 @@ impl Resolver { if path.segments .iter() - .any(|s| s.lifetime.is_some()) { + .any(|s| !s.lifetimes.is_empty()) { self.session.span_err(path.span, "lifetime parameters \ are not allowed on \ diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs new file mode 100644 index 0000000000000..b21720f2e30a0 --- /dev/null +++ b/src/librustc/middle/resolve_lifetime.rs @@ -0,0 +1,321 @@ +// Copyright 2012-2013 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. + +/*! + * Name resolution for lifetimes. + * + * Name resolution for lifetimes follows MUCH simpler rules than the + * full resolve. For example, lifetime names are never exported or + * used between functions, and they operate in a purely top-down + * way. Therefore we break lifetime name resolution into a separate pass. + */ + +use driver::session; +use std::hashmap::HashMap; +use syntax::ast; +use syntax::codemap::Span; +use syntax::opt_vec::OptVec; +use syntax::parse::token::special_idents; +use syntax::print::pprust::{lifetime_to_str}; +use syntax::visit; +use syntax::visit::Visitor; + +// maps the id of each lifetime reference to the lifetime decl +// that it corresponds to +pub type NamedRegionMap = HashMap; + +struct LifetimeContext { + sess: session::Session, + named_region_map: @mut NamedRegionMap, +} + +enum ScopeChain<'self> { + ItemScope(&'self OptVec), + FnScope(ast::NodeId, &'self OptVec, &'self ScopeChain<'self>), + BlockScope(ast::NodeId, &'self ScopeChain<'self>), + RootScope +} + +pub fn crate(sess: session::Session, + crate: &ast::Crate) + -> @mut NamedRegionMap { + let mut ctxt = LifetimeContext { + sess: sess, + named_region_map: @mut HashMap::new() + }; + visit::walk_crate(&mut ctxt, crate, &RootScope); + sess.abort_if_errors(); + ctxt.named_region_map +} + +impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { + fn visit_item(&mut self, + item: @ast::item, + _: &'self ScopeChain<'self>) { + let scope = match item.node { + ast::item_fn(*) | // fn lifetimes get added in visit_fn below + ast::item_mod(*) | + ast::item_mac(*) | + ast::item_foreign_mod(*) | + ast::item_static(*) => { + RootScope + } + ast::item_ty(_, ref generics) | + ast::item_enum(_, ref generics) | + ast::item_struct(_, ref generics) | + ast::item_impl(ref generics, _, _, _) | + ast::item_trait(ref generics, _, _) => { + self.check_lifetime_names(&generics.lifetimes); + ItemScope(&generics.lifetimes) + } + }; + debug!("entering scope {:?}", scope); + visit::walk_item(self, item, &scope); + debug!("exiting scope {:?}", scope); + } + + fn visit_fn(&mut self, + fk: &visit::fn_kind, + fd: &ast::fn_decl, + b: &ast::Block, + s: Span, + n: ast::NodeId, + scope: &'self ScopeChain<'self>) { + match *fk { + visit::fk_item_fn(_, generics, _, _) | + visit::fk_method(_, generics, _) => { + let scope1 = FnScope(n, &generics.lifetimes, scope); + self.check_lifetime_names(&generics.lifetimes); + debug!("pushing fn scope id={} due to item/method", n); + visit::walk_fn(self, fk, fd, b, s, n, &scope1); + debug!("popping fn scope id={} due to item/method", n); + } + visit::fk_anon(*) | visit::fk_fn_block(*) => { + visit::walk_fn(self, fk, fd, b, s, n, scope); + } + } + } + + fn visit_ty(&mut self, + ty: &ast::Ty, + scope: &'self ScopeChain<'self>) { + match ty.node { + ast::ty_closure(@ast::TyClosure { lifetimes: ref lifetimes, _ }) | + ast::ty_bare_fn(@ast::TyBareFn { lifetimes: ref lifetimes, _ }) => { + let scope1 = FnScope(ty.id, lifetimes, scope); + self.check_lifetime_names(lifetimes); + debug!("pushing fn scope id={} due to type", ty.id); + visit::walk_ty(self, ty, &scope1); + debug!("popping fn scope id={} due to type", ty.id); + } + _ => { + visit::walk_ty(self, ty, scope); + } + } + } + + fn visit_ty_method(&mut self, + m: &ast::TypeMethod, + scope: &'self ScopeChain<'self>) { + let scope1 = FnScope(m.id, &m.generics.lifetimes, scope); + self.check_lifetime_names(&m.generics.lifetimes); + debug!("pushing fn scope id={} due to ty_method", m.id); + visit::walk_ty_method(self, m, &scope1); + debug!("popping fn scope id={} due to ty_method", m.id); + } + + fn visit_block(&mut self, + b: &ast::Block, + scope: &'self ScopeChain<'self>) { + let scope1 = BlockScope(b.id, scope); + debug!("pushing block scope {}", b.id); + visit::walk_block(self, b, &scope1); + debug!("popping block scope {}", b.id); + } + + fn visit_lifetime_ref(&mut self, + lifetime_ref: &ast::Lifetime, + scope: &'self ScopeChain<'self>) { + if lifetime_ref.ident == special_idents::statik { + self.insert_lifetime(lifetime_ref, ast::DefStaticRegion); + return; + } + self.resolve_lifetime_ref(lifetime_ref, scope); + } +} + +impl LifetimeContext { + fn resolve_lifetime_ref(&self, + lifetime_ref: &ast::Lifetime, + scope: &ScopeChain) { + // Walk up the scope chain, tracking the number of fn scopes + // that we pass through, until we find a lifetime with the + // given name or we run out of scopes. If we encounter a code + // block, then the lifetime is not bound but free, so switch + // over to `resolve_free_lifetime_ref()` to complete the + // search. + let mut depth = 0; + let mut scope = scope; + loop { + match *scope { + BlockScope(id, s) => { + return self.resolve_free_lifetime_ref(id, lifetime_ref, s); + } + + RootScope => { + break; + } + + ItemScope(lifetimes) => { + match search_lifetimes(lifetimes, lifetime_ref) { + Some((index, decl_id)) => { + let def = ast::DefEarlyBoundRegion(index, decl_id); + self.insert_lifetime(lifetime_ref, def); + return; + } + None => { + break; + } + } + } + + FnScope(id, lifetimes, s) => { + match search_lifetimes(lifetimes, lifetime_ref) { + Some((_index, decl_id)) => { + let def = ast::DefLateBoundRegion(id, depth, decl_id); + self.insert_lifetime(lifetime_ref, def); + return; + } + + None => { + depth += 1; + scope = s; + } + } + } + } + } + + self.unresolved_lifetime_ref(lifetime_ref); + } + + fn resolve_free_lifetime_ref(&self, + scope_id: ast::NodeId, + lifetime_ref: &ast::Lifetime, + scope: &ScopeChain) { + // Walk up the scope chain, tracking the outermost free scope, + // until we encounter a scope that contains the named lifetime + // or we run out of scopes. + let mut scope_id = scope_id; + let mut scope = scope; + let mut search_result = None; + loop { + match *scope { + BlockScope(id, s) => { + scope_id = id; + scope = s; + } + + RootScope => { + break; + } + + ItemScope(lifetimes) => { + search_result = search_lifetimes(lifetimes, lifetime_ref); + break; + } + + FnScope(_, lifetimes, s) => { + search_result = search_lifetimes(lifetimes, lifetime_ref); + if search_result.is_some() { + break; + } + scope = s; + } + } + } + + match search_result { + Some((_depth, decl_id)) => { + let def = ast::DefFreeRegion(scope_id, decl_id); + self.insert_lifetime(lifetime_ref, def); + } + + None => { + self.unresolved_lifetime_ref(lifetime_ref); + } + } + + } + + fn unresolved_lifetime_ref(&self, + lifetime_ref: &ast::Lifetime) { + self.sess.span_err( + lifetime_ref.span, + format!("use of undeclared lifetime name `'{}`", + self.sess.str_of(lifetime_ref.ident))); + } + + fn check_lifetime_names(&self, lifetimes: &OptVec) { + for i in range(0, lifetimes.len()) { + let lifetime_i = lifetimes.get(i); + + let special_idents = [special_idents::statik]; + for lifetime in lifetimes.iter() { + if special_idents.iter().any(|&i| i == lifetime.ident) { + self.sess.span_err( + lifetime.span, + format!("illegal lifetime parameter name: `{}`", + self.sess.str_of(lifetime.ident))); + } + } + + for j in range(i + 1, lifetimes.len()) { + let lifetime_j = lifetimes.get(j); + + if lifetime_i.ident == lifetime_j.ident { + self.sess.span_err( + lifetime_j.span, + format!("lifetime name `'{}` declared twice in \ + the same scope", + self.sess.str_of(lifetime_j.ident))); + } + } + } + } + + fn insert_lifetime(&self, + lifetime_ref: &ast::Lifetime, + def: ast::DefRegion) { + if lifetime_ref.id == ast::DUMMY_NODE_ID { + self.sess.span_bug(lifetime_ref.span, + "Lifetime reference not renumbered, \ + probably a bug in syntax::fold"); + } + + debug!("lifetime_ref={} id={} resolved to {:?}", + lifetime_to_str(lifetime_ref, + self.sess.intr()), + lifetime_ref.id, + def); + self.named_region_map.insert(lifetime_ref.id, def); + } +} + +fn search_lifetimes(lifetimes: &OptVec, + lifetime_ref: &ast::Lifetime) + -> Option<(uint, ast::NodeId)> { + for (i, lifetime_decl) in lifetimes.iter().enumerate() { + if lifetime_decl.ident == lifetime_ref.ident { + return Some((i, lifetime_decl.id)); + } + } + return None; +} diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 009745ff2adb8..c9abf71e2b6ec 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -10,10 +10,11 @@ // Type substitutions. - use middle::ty; +use middle::ty_fold; +use middle::ty_fold::TypeFolder; use syntax::opt_vec::OptVec; -use util::ppaux::Repr; +use std::at_vec; /////////////////////////////////////////////////////////////////////////// // Public trait `Subst` @@ -33,39 +34,43 @@ pub trait Subst { // to all subst methods but ran into trouble due to the limitations of // our current method/trait matching algorithm. - Niko -trait EffectfulSubst { - fn effectfulSubst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Self; -} - impl Subst for ty::t { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::t { if ty::substs_is_noop(substs) { - return *self; + *self } else { - return self.effectfulSubst(tcx, substs); + let mut folder = SubstFolder {tcx: tcx, substs: substs}; + folder.fold_ty(*self) } } } -impl EffectfulSubst for ty::t { - fn effectfulSubst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::t { - if !ty::type_needs_subst(*self) { - return *self; +struct SubstFolder<'self> { + tcx: ty::ctxt, + substs: &'self ty::substs +} + +impl<'self> TypeFolder for SubstFolder<'self> { + fn tcx(&self) -> ty::ctxt { self.tcx } + + fn fold_region(&mut self, r: ty::Region) -> ty::Region { + r.subst(self.tcx, self.substs) + } + + fn fold_ty(&mut self, t: ty::t) -> ty::t { + if !ty::type_needs_subst(t) { + return t; } - match ty::get(*self).sty { + match ty::get(t).sty { ty::ty_param(p) => { - substs.tps[p.idx] + self.substs.tps[p.idx] } ty::ty_self(_) => { - substs.self_ty.expect("ty_self not found in substs") + self.substs.self_ty.expect("ty_self not found in substs") } _ => { - ty::fold_regions_and_ty( - tcx, *self, - |r| r.subst(tcx, substs), - |t| t.effectfulSubst(tcx, substs), - |t| t.effectfulSubst(tcx, substs)) + ty_fold::super_fold_ty(self, t) } } } @@ -80,6 +85,12 @@ impl Subst for ~[T] { } } +impl Subst for @[T] { + fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> @[T] { + at_vec::map(*self, |t| t.subst(tcx, substs)) + } +} + impl Subst for OptVec { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> OptVec { self.map(|t| t.subst(tcx, substs)) @@ -134,7 +145,8 @@ impl Subst for ty::RegionSubsts { impl Subst for ty::BareFnTy { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::BareFnTy { - ty::fold_bare_fn_ty(self, |t| t.subst(tcx, substs)) + let mut folder = SubstFolder {tcx: tcx, substs: substs}; + folder.fold_bare_fn_ty(self) } } @@ -161,35 +173,30 @@ impl Subst for ty::Generics { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Generics { ty::Generics { type_param_defs: self.type_param_defs.subst(tcx, substs), - region_param: self.region_param + region_param_defs: self.region_param_defs.subst(tcx, substs), } } } +impl Subst for ty::RegionParameterDef { + fn subst(&self, _: ty::ctxt, _: &ty::substs) -> ty::RegionParameterDef { + *self + } +} + impl Subst for ty::Region { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Region { - // Note: This routine only handles the self region, because it - // is only concerned with substitutions of regions that appear - // in types. Region substitution of the bound regions that - // appear in a function signature is done using the - // specialized routine + fn subst(&self, _tcx: ty::ctxt, substs: &ty::substs) -> ty::Region { + // Note: This routine only handles regions that are bound on + // type declarationss and other outer declarations, not those + // bound in *fn types*. Region substitution of the bound + // regions that appear in a function signature is done using + // the specialized routine // `middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig()`. - // As we transition to the new region syntax this distinction - // will most likely disappear. match self { - &ty::re_bound(ty::br_self) => { + &ty::ReEarlyBound(_, i, _) => { match substs.regions { - ty::ErasedRegions => ty::re_static, - ty::NonerasedRegions(ref regions) => { - if regions.len() != 1 { - tcx.sess.bug( - format!("ty::Region\\#subst(): \ - Reference to self region when \ - given substs with no self region: {}", - substs.repr(tcx))); - } - *regions.get(0) - } + ty::ErasedRegions => ty::ReStatic, + ty::NonerasedRegions(ref regions) => *regions.get(i), } } _ => *self diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 9bafea6d861be..9dddf96e34d57 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -1048,7 +1048,7 @@ fn extract_vec_elems(bcx: @mut Block, let slice_len = Sub(bcx, len, slice_len_offset); let slice_ty = ty::mk_evec(bcx.tcx(), ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}, - ty::vstore_slice(ty::re_static) + ty::vstore_slice(ty::ReStatic) ); let scratch = scratch_datum(bcx, slice_ty, "", false); Store(bcx, slice_begin, @@ -1697,7 +1697,7 @@ fn compile_submatch_continue(mut bcx: @mut Block, let t = node_id_type(bcx, pat_id); let Result {bcx: after_cx, val: matches} = { do with_scope_result(bcx, None, - "compare_scope") |bcx| { + "compaReScope") |bcx| { match trans_opt(bcx, opt) { single_result( Result {bcx, val}) => { diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 9f497afb1213f..06fe4717f58e1 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -181,7 +181,7 @@ pub fn const_expr(cx: @mut CrateContext, e: &ast::Expr) -> (ValueRef, bool) { let adjustment = cx.tcx.adjustments.find_copy(&e.id); match adjustment { None => { } - Some(@ty::AutoAddEnv(ty::re_static, ast::BorrowedSigil)) => { + Some(@ty::AutoAddEnv(ty::ReStatic, ast::BorrowedSigil)) => { llconst = C_struct([llconst, C_null(Type::opaque_box(cx).ptr_to())], false) } Some(@ty::AutoAddEnv(ref r, ref s)) => { @@ -211,11 +211,11 @@ pub fn const_expr(cx: @mut CrateContext, e: &ast::Expr) -> (ValueRef, bool) { }; match *autoref { ty::AutoUnsafe(m) | - ty::AutoPtr(ty::re_static, m) => { + ty::AutoPtr(ty::ReStatic, m) => { assert!(m != ast::MutMutable); llconst = llptr; } - ty::AutoBorrowVec(ty::re_static, m) => { + ty::AutoBorrowVec(ty::ReStatic, m) => { assert!(m != ast::MutMutable); assert_eq!(abi::slice_elt_base, 0); assert_eq!(abi::slice_elt_len, 1); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 1efa7f763d8f9..01cf102275023 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -588,7 +588,7 @@ impl Datum { // result (which will be by-value). Note that it is not // significant *which* region we pick here. let llval = self.to_ref_llval(bcx); - let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static, + let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, self.ty); Datum {val: llval, ty: rptr_ty, mode: ByValue} } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index f291f088dec6c..ddf9354ad3835 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -271,7 +271,7 @@ pub fn trans_to_datum(bcx: @mut Block, expr: &ast::Expr) -> DatumBlock { // real one, but it will have the same runtime representation let slice_ty = ty::mk_evec(tcx, ty::mt { ty: unit_ty, mutbl: ast::MutImmutable }, - ty::vstore_slice(ty::re_static)); + ty::vstore_slice(ty::ReStatic)); let scratch = scratch_datum(bcx, slice_ty, "__adjust", false); diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index f461120e4c38a..0993d3322f1e6 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -339,7 +339,7 @@ pub fn make_visit_glue(bcx: @mut Block, v: ValueRef, t: ty::t) -> @mut Block { do with_scope(bcx, None, "visitor cleanup") |bcx| { let mut bcx = bcx; let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(), - ty::re_static) { + ty::ReStatic) { Ok(pair) => pair, Err(s) => { bcx.tcx().sess.fatal(s); diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 2f4fcfed20be9..0781f724d48c3 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -59,7 +59,7 @@ impl Reflector { // We're careful to not use first class aggregates here because that // will kick us off fast isel. (Issue #4352.) let bcx = self.bcx; - let str_vstore = ty::vstore_slice(ty::re_static); + let str_vstore = ty::vstore_slice(ty::ReStatic); let str_ty = ty::mk_estr(bcx.tcx(), str_vstore); let scratch = scratch_datum(bcx, str_ty, "", false); let len = C_uint(bcx.ccx(), s.len()); diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 604321a0492a1..36af13d34e656 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -14,6 +14,7 @@ use middle::trans::common::*; use middle::trans::foreign; use middle::ty; use util::ppaux; +use util::ppaux::Repr; use middle::trans::type_::Type; @@ -172,14 +173,16 @@ pub fn sizing_type_of(cx: &mut CrateContext, t: ty::t) -> Type { // NB: If you update this, be sure to update `sizing_type_of()` as well. pub fn type_of(cx: &mut CrateContext, t: ty::t) -> Type { - debug!("type_of {:?}: {:?}", t, ty::get(t)); - // Check the cache. match cx.lltypes.find(&t) { - Some(&t) => return t, + Some(&llty) => { + return llty; + } None => () } + debug!("type_of {} {:?}", t.repr(cx.tcx), t); + // Replace any typedef'd types with their equivalent non-typedef // type. This ensures that all LLVM nominal types that contain // Rust types are defined as the same LLVM types. If we don't do @@ -189,6 +192,12 @@ pub fn type_of(cx: &mut CrateContext, t: ty::t) -> Type { if t != t_norm { let llty = type_of(cx, t_norm); + debug!("--> normalized {} {:?} to {} {:?} llty={}", + t.repr(cx.tcx), + t, + t_norm.repr(cx.tcx), + t_norm, + cx.tn.type_to_str(llty)); cx.lltypes.insert(t, llty); return llty; } @@ -299,6 +308,10 @@ pub fn type_of(cx: &mut CrateContext, t: ty::t) -> Type { ty::ty_err(*) => cx.tcx.sess.bug("type_of with ty_err") }; + debug!("--> mapped t={} {:?} to llty={}", + t.repr(cx.tcx), + t, + cx.tn.type_to_str(llty)); cx.lltypes.insert(t, llty); // If this was an enum or struct, fill in the type now. diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 96bb2897e0e26..5072a95ddcf1b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -17,9 +17,12 @@ use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem}; use middle::lang_items::OpaqueStructLangItem; use middle::freevars; use middle::resolve; +use middle::resolve_lifetime; use middle::ty; use middle::subst::Subst; use middle::typeck; +use middle::ty_fold; +use middle::ty_fold::TypeFolder; use middle; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_str}; use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str}; @@ -206,13 +209,19 @@ pub enum ast_ty_to_ty_cache_entry { atttce_resolved(t) /* resolved to a type, irrespective of region */ } -pub type opt_region_variance = Option; +#[deriving(Clone, Eq, Decodable, Encodable)] +pub struct ItemVariances { + self_param: Option, + type_params: OptVec, + region_params: OptVec +} #[deriving(Clone, Eq, Decodable, Encodable)] -pub enum region_variance { - rv_covariant, - rv_invariant, - rv_contravariant, +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter } #[deriving(Decodable, Encodable)] @@ -258,8 +267,9 @@ struct ctxt_ { sess: session::Session, def_map: resolve::DefMap, + named_region_map: @mut resolve_lifetime::NamedRegionMap, + region_maps: @mut middle::region::RegionMaps, - region_paramd_items: middle::region::region_paramd_items, // Stores the types for various nodes in the AST. Note that this table // is not guaranteed to be populated until after typeck. See @@ -304,6 +314,10 @@ struct ctxt_ { provided_method_sources: @mut HashMap, supertraits: @mut HashMap, + // Maps from def-id of a type or region parameter to its + // (inferred) variance. + item_variance_map: @mut HashMap, + // A mapping from the def ID of an enum or struct type to the def ID // of the method that implements its destructor. If the type is not // present in this map, it does not have a destructor. This map is @@ -431,14 +445,17 @@ pub struct ClosureTy { * Signature of a function type, which I have arbitrarily * decided to use to refer to the input/output types. * - * - `lifetimes` is the list of region names bound in this fn. + * - `binder_id` is the node id where this fn type appeared; + * it is used to identify all the bound regions appearing + * in the input/output types that are bound by this fn type + * (vs some enclosing or enclosed fn type) * - `inputs` is the list of arguments and their modes. * - `output` is the return type. * - `variadic` indicates whether this is a varidic function. (only true for foreign fns) */ #[deriving(Clone, Eq, IterBytes)] pub struct FnSig { - bound_lifetime_names: OptVec, + binder_id: ast::NodeId, inputs: ~[t], output: t, variadic: bool @@ -453,86 +470,75 @@ pub struct param_ty { /// Representation of regions: #[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] pub enum Region { - /// Bound regions are found (primarily) in function types. They indicate - /// region parameters that have yet to be replaced with actual regions - /// (analogous to type parameters, except that due to the monomorphic - /// nature of our type system, bound type parameters are always replaced - /// with fresh type variables whenever an item is referenced, so type - /// parameters only appear "free" in types. Regions in contrast can - /// appear free or bound.). When a function is called, all bound regions - /// tied to that function's node-id are replaced with fresh region - /// variables whose value is then inferred. - re_bound(bound_region), + // Region bound in a type or fn declaration which will be + // substituted 'early' -- that is, at the same time when type + // parameters are substituted. + ReEarlyBound(/* param id */ ast::NodeId, /*index*/ uint, ast::Ident), + + // Region bound in a function scope, which will be substituted when the + // function is called. The first argument must be the `binder_id` of + // some enclosing function signature. + ReLateBound(/* binder_id */ ast::NodeId, BoundRegion), /// When checking a function body, the types of all arguments and so forth /// that refer to bound region parameters are modified to refer to free /// region parameters. - re_free(FreeRegion), + ReFree(FreeRegion), /// A concrete region naming some expression within the current function. - re_scope(NodeId), + ReScope(NodeId), /// Static data that has an "infinite" lifetime. Top in the region lattice. - re_static, + ReStatic, /// A region variable. Should not exist after typeck. - re_infer(InferRegion), + ReInfer(InferRegion), /// Empty lifetime is for data that is never accessed. - /// Bottom in the region lattice. We treat re_empty somewhat + /// Bottom in the region lattice. We treat ReEmpty somewhat /// specially; at least right now, we do not generate instances of /// it during the GLB computations, but rather /// generate an error instead. This is to improve error messages. - /// The only way to get an instance of re_empty is to have a region + /// The only way to get an instance of ReEmpty is to have a region /// variable with no constraints. - re_empty, + ReEmpty, } impl Region { pub fn is_bound(&self) -> bool { match self { - &re_bound(*) => true, + &ty::ReEarlyBound(*) => true, + &ty::ReLateBound(*) => true, _ => false } } } -#[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] +#[deriving(Clone, Eq, TotalOrd, TotalEq, IterBytes, Encodable, Decodable, ToStr)] pub struct FreeRegion { scope_id: NodeId, - bound_region: bound_region + bound_region: BoundRegion } -#[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] -pub enum bound_region { - /// The self region for structs, impls (&T in a type defn or &'self T) - br_self, - +#[deriving(Clone, Eq, TotalEq, TotalOrd, IterBytes, Encodable, Decodable, ToStr)] +pub enum BoundRegion { /// An anonymous region parameter for a given fn (&T) - br_anon(uint), + BrAnon(uint), /// Named region parameters for functions (a in &'a T) - br_named(ast::Ident), + /// + /// The def-id is needed to distinguish free regions in + /// the event of shadowing. + BrNamed(ast::DefId, ast::Ident), /// Fresh bound identifiers created during GLB computations. - br_fresh(uint), - - /** - * Handles capture-avoiding substitution in a rather subtle case. If you - * have a closure whose argument types are being inferred based on the - * expected type, and the expected type includes bound regions, then we - * will wrap those bound regions in a br_cap_avoid() with the id of the - * fn expression. This ensures that the names are not "captured" by the - * enclosing scope, which may define the same names. For an example of - * where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs - * and regions-ret-borrowed-1.rs. */ - br_cap_avoid(ast::NodeId, @bound_region), + BrFresh(uint), } /** * Represents the values to use when substituting lifetime parameters. * If the value is `ErasedRegions`, then this subst is occurring during - * trans, and all region parameters will be replaced with `ty::re_static`. */ + * trans, and all region parameters will be replaced with `ty::ReStatic`. */ #[deriving(Clone, Eq, IterBytes)] pub enum RegionSubsts { ErasedRegions, @@ -697,8 +703,8 @@ pub enum type_err { terr_regions_does_not_outlive(Region, Region), terr_regions_not_same(Region, Region), terr_regions_no_overlap(Region, Region), - terr_regions_insufficiently_polymorphic(bound_region, Region), - terr_regions_overly_polymorphic(bound_region, Region), + terr_regions_insufficiently_polymorphic(BoundRegion, Region), + terr_regions_overly_polymorphic(BoundRegion, Region), terr_vstores_differ(terr_vstore_kind, expected_found), terr_trait_stores_differ(terr_vstore_kind, expected_found), terr_in_field(@type_err, ast::Ident), @@ -774,7 +780,7 @@ pub enum InferTy { #[deriving(Clone, Encodable, Decodable, IterBytes, ToStr)] pub enum InferRegion { ReVar(RegionVid), - ReSkolemized(uint, bound_region) + ReSkolemized(uint, BoundRegion) } impl cmp::Eq for InferRegion { @@ -863,12 +869,21 @@ pub struct TypeParameterDef { bounds: @ParamBounds } -/// Information about the type/lifetime parametesr associated with an item. +#[deriving(Encodable, Decodable, Clone)] +pub struct RegionParameterDef { + ident: ast::Ident, + def_id: ast::DefId, +} + +/// Information about the type/lifetime parameters associated with an item. /// Analogous to ast::Generics. #[deriving(Clone)] pub struct Generics { + /// List of type parameters declared on the item. type_param_defs: @~[TypeParameterDef], - region_param: Option, + + /// List of region parameters declared on the item. + region_param_defs: @[RegionParameterDef], } impl Generics { @@ -877,6 +892,33 @@ impl Generics { } } +/// When type checking, we use the `ParameterEnvironment` to track +/// details about the type/lifetime parameters that are in scope. +/// It primarily stores the bounds information. +/// +/// Note: This information might seem to be redundant with the data in +/// `tcx.ty_param_defs`, but it is not. That table contains the +/// parameter definitions from an "outside" perspective, but this +/// struct will contain the bounds for a parameter as seen from inside +/// the function body. Currently the only real distinction is that +/// bound lifetime parameters are replaced with free ones, but in the +/// future I hope to refine the representation of types so as to make +/// more distinctions clearer. +pub struct ParameterEnvironment { + /// A substitution that can be applied to move from + /// the "outer" view of a type or method to the "inner" view. + /// In general, this means converting from bound parameters to + /// free parameters. Since we currently represent bound/free type + /// parameters in the same way, this only has an affect on regions. + free_substs: ty::substs, + + /// Bound on the Self parameter + self_param_bound: Option<@TraitRef>, + + /// Bounds on each numbered type parameter + type_param_bounds: ~[ParamBounds], +} + /// A polytype. /// /// - `bounds`: The list of bounds for each type parameter. The length of the @@ -919,13 +961,15 @@ pub fn new_ty_hash() -> @mut HashMap { pub fn mk_ctxt(s: session::Session, dm: resolve::DefMap, + named_region_map: @mut resolve_lifetime::NamedRegionMap, amap: ast_map::map, freevars: freevars::freevar_map, region_maps: @mut middle::region::RegionMaps, - region_paramd_items: middle::region::region_paramd_items, lang_items: middle::lang_items::LanguageItems) -> ctxt { @ctxt_ { + named_region_map: named_region_map, + item_variance_map: @mut HashMap::new(), diag: s.diagnostic(), interner: @mut HashMap::new(), next_id: @mut primitives::LAST_PRIMITIVE_ID, @@ -933,7 +977,6 @@ pub fn mk_ctxt(s: session::Session, sess: s, def_map: dm, region_maps: region_maps, - region_paramd_items: region_paramd_items, node_types: @mut HashMap::new(), node_type_substs: @mut HashMap::new(), trait_refs: @mut HashMap::new(), @@ -978,7 +1021,7 @@ pub fn mk_ctxt(s: session::Session, // Interns a type/name combination, stores the resulting box in cx.interner, // and returns the box as cast to an unsafe ptr (see comments for t above). -fn mk_t(cx: ctxt, st: sty) -> t { +pub fn mk_t(cx: ctxt, st: sty) -> t { // Check for primitive types. match st { ty_nil => return mk_nil(), @@ -987,6 +1030,8 @@ fn mk_t(cx: ctxt, st: sty) -> t { ty_int(i) => return mk_mach_int(i), ty_uint(u) => return mk_mach_uint(u), ty_float(f) => return mk_mach_float(f), + ty_char => return mk_char(), + ty_bot => return mk_bot(), _ => {} }; @@ -1000,7 +1045,7 @@ fn mk_t(cx: ctxt, st: sty) -> t { fn rflags(r: Region) -> uint { (has_regions as uint) | { match r { - ty::re_infer(_) => needs_infer as uint, + ty::ReInfer(_) => needs_infer as uint, _ => 0u } } @@ -1246,14 +1291,17 @@ pub fn mk_bare_fn(cx: ctxt, fty: BareFnTy) -> t { mk_t(cx, ty_bare_fn(fty)) } -pub fn mk_ctor_fn(cx: ctxt, input_tys: &[ty::t], output: ty::t) -> t { +pub fn mk_ctor_fn(cx: ctxt, + binder_id: ast::NodeId, + input_tys: &[ty::t], + output: ty::t) -> t { let input_args = input_tys.map(|t| *t); mk_bare_fn(cx, BareFnTy { purity: ast::impure_fn, abis: AbiSet::Rust(), sig: FnSig { - bound_lifetime_names: opt_vec::Empty, + binder_id: binder_id, inputs: input_args, output: output, variadic: false @@ -1333,224 +1381,70 @@ pub fn maybe_walk_ty(ty: t, f: &fn(t) -> bool) { } } -pub fn fold_sty_to_ty(tcx: ty::ctxt, sty: &sty, foldop: &fn(t) -> t) -> t { - mk_t(tcx, fold_sty(sty, foldop)) +// Folds types from the bottom up. +pub fn fold_ty(cx: ctxt, t0: t, fldop: &fn(t) -> t) -> t { + let mut f = ty_fold::BottomUpFolder {tcx: cx, fldop: fldop}; + f.fold_ty(t0) } -pub fn fold_sig(sig: &FnSig, fldop: &fn(t) -> t) -> FnSig { - let args = sig.inputs.map(|arg| fldop(*arg)); - - FnSig { - bound_lifetime_names: sig.bound_lifetime_names.clone(), - inputs: args, - output: fldop(sig.output), - variadic: sig.variadic - } +pub fn walk_regions_and_ty(cx: ctxt, + ty: t, + fldr: &fn(r: Region), + fldt: &fn(t: t)) + -> t { + ty_fold::RegionFolder::general(cx, + |r| { fldr(r); r }, + |t| { fldt(t); t }).fold_ty(ty) } -pub fn fold_bare_fn_ty(fty: &BareFnTy, fldop: &fn(t) -> t) -> BareFnTy { - BareFnTy {sig: fold_sig(&fty.sig, fldop), - abis: fty.abis, - purity: fty.purity} +pub fn fold_regions(cx: ctxt, + ty: t, + fldr: &fn(r: Region) -> Region) + -> t { + ty_fold::RegionFolder::regions(cx, fldr).fold_ty(ty) } -fn fold_sty(sty: &sty, fldop: &fn(t) -> t) -> sty { - fn fold_substs(substs: &substs, fldop: &fn(t) -> t) -> substs { - substs {regions: substs.regions.clone(), - self_ty: substs.self_ty.map(|t| fldop(t)), - tps: substs.tps.map(|t| fldop(*t))} - } +// Substitute *only* type parameters. Used in trans where regions are erased. +pub fn subst_tps(tcx: ctxt, tps: &[t], self_ty_opt: Option, typ: t) -> t { + let mut subst = TpsSubst { tcx: tcx, self_ty_opt: self_ty_opt, tps: tps }; + return subst.fold_ty(typ); - match *sty { - ty_box(ref tm) => { - ty_box(mt {ty: fldop(tm.ty), mutbl: tm.mutbl}) - } - ty_uniq(ref tm) => { - ty_uniq(mt {ty: fldop(tm.ty), mutbl: tm.mutbl}) - } - ty_ptr(ref tm) => { - ty_ptr(mt {ty: fldop(tm.ty), mutbl: tm.mutbl}) - } - ty_unboxed_vec(ref tm) => { - ty_unboxed_vec(mt {ty: fldop(tm.ty), mutbl: tm.mutbl}) - } - ty_evec(ref tm, vst) => { - ty_evec(mt {ty: fldop(tm.ty), mutbl: tm.mutbl}, vst) - } - ty_enum(tid, ref substs) => { - ty_enum(tid, fold_substs(substs, fldop)) - } - ty_trait(did, ref substs, st, mutbl, bounds) => { - ty_trait(did, fold_substs(substs, fldop), st, mutbl, bounds) - } - ty_tup(ref ts) => { - let new_ts = ts.map(|tt| fldop(*tt)); - ty_tup(new_ts) - } - ty_bare_fn(ref f) => { - ty_bare_fn(fold_bare_fn_ty(f, fldop)) - } - ty_closure(ref f) => { - let sig = fold_sig(&f.sig, fldop); - ty_closure(ClosureTy { - sig: sig, - purity: f.purity, - sigil: f.sigil, - onceness: f.onceness, - region: f.region, - bounds: f.bounds, - }) - } - ty_rptr(r, ref tm) => { - ty_rptr(r, mt {ty: fldop(tm.ty), mutbl: tm.mutbl}) - } - ty_struct(did, ref substs) => { - ty_struct(did, fold_substs(substs, fldop)) - } - ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | ty_err | - ty_opaque_box | ty_infer(_) | ty_param(*) | ty_self(_) => { - (*sty).clone() - } + pub struct TpsSubst<'self> { + tcx: ctxt, + self_ty_opt: Option, + tps: &'self [t], } -} -// Folds types from the bottom up. -pub fn fold_ty(cx: ctxt, t0: t, fldop: &fn(t) -> t) -> t { - let sty = fold_sty(&get(t0).sty, |t| fold_ty(cx, fldop(t), |t| fldop(t))); - fldop(mk_t(cx, sty)) -} - -pub fn walk_regions_and_ty( - cx: ctxt, - ty: t, - walkr: &fn(r: Region), - walkt: &fn(t: t) -> bool) { + impl<'self> TypeFolder for TpsSubst<'self> { + fn tcx(&self) -> ty::ctxt { self.tcx } - if (walkt(ty)) { - fold_regions_and_ty( - cx, ty, - |r| { walkr(r); r }, - |t| { walk_regions_and_ty(cx, t, |r| walkr(r), |t| walkt(t)); t }, - |t| { walk_regions_and_ty(cx, t, |r| walkr(r), |t| walkt(t)); t }); - } -} - -pub fn fold_regions_and_ty( - cx: ctxt, - ty: t, - fldr: &fn(r: Region) -> Region, - fldfnt: &fn(t: t) -> t, - fldt: &fn(t: t) -> t) -> t { - - fn fold_substs( - substs: &substs, - fldr: &fn(r: Region) -> Region, - fldt: &fn(t: t) -> t) - -> substs { - let regions = match substs.regions { - ErasedRegions => ErasedRegions, - NonerasedRegions(ref regions) => { - NonerasedRegions(regions.map(|r| fldr(*r))) + fn fold_ty(&mut self, t: ty::t) -> ty::t { + if self.tps.len() == 0u && self.self_ty_opt.is_none() { + return t; } - }; - substs { - regions: regions, - self_ty: substs.self_ty.map(|t| fldt(t)), - tps: substs.tps.map(|t| fldt(*t)) - } - } + let tb = ty::get(t); + if self.self_ty_opt.is_none() && !tbox_has_flag(tb, has_params) { + return t; + } - let tb = ty::get(ty); - match tb.sty { - ty::ty_rptr(r, mt) => { - let m_r = fldr(r); - let m_t = fldt(mt.ty); - ty::mk_rptr(cx, m_r, mt {ty: m_t, mutbl: mt.mutbl}) - } - ty_estr(vstore_slice(r)) => { - let m_r = fldr(r); - ty::mk_estr(cx, vstore_slice(m_r)) - } - ty_evec(mt, vstore_slice(r)) => { - let m_r = fldr(r); - let m_t = fldt(mt.ty); - ty::mk_evec(cx, mt {ty: m_t, mutbl: mt.mutbl}, vstore_slice(m_r)) - } - ty_enum(def_id, ref substs) => { - ty::mk_enum(cx, def_id, fold_substs(substs, fldr, fldt)) - } - ty_struct(def_id, ref substs) => { - ty::mk_struct(cx, def_id, fold_substs(substs, fldr, fldt)) - } - ty_trait(def_id, ref substs, st, mutbl, bounds) => { - let st = match st { - RegionTraitStore(region) => RegionTraitStore(fldr(region)), - st => st, - }; - ty::mk_trait(cx, def_id, fold_substs(substs, fldr, fldt), st, mutbl, bounds) - } - ty_bare_fn(ref f) => { - ty::mk_bare_fn(cx, BareFnTy { - sig: fold_sig(&f.sig, fldfnt), - purity: f.purity, - abis: f.abis.clone(), - }) - } - ty_closure(ref f) => { - ty::mk_closure(cx, ClosureTy { - region: fldr(f.region), - sig: fold_sig(&f.sig, fldfnt), - purity: f.purity, - sigil: f.sigil, - onceness: f.onceness, - bounds: f.bounds, - }) - } - ref sty => { - fold_sty_to_ty(cx, sty, |t| fldt(t)) - } - } -} + match ty::get(t).sty { + ty_param(p) => { + self.tps[p.idx] + } -// n.b. this function is intended to eventually replace fold_region() below, -// that is why its name is so similar. -pub fn fold_regions( - cx: ctxt, - ty: t, - fldr: &fn(r: Region, in_fn: bool) -> Region) -> t { - fn do_fold(cx: ctxt, ty: t, in_fn: bool, - fldr: &fn(Region, bool) -> Region) -> t { - debug!("do_fold(ty={}, in_fn={})", ty_to_str(cx, ty), in_fn); - if !type_has_regions(ty) { return ty; } - fold_regions_and_ty( - cx, ty, - |r| fldr(r, in_fn), - |t| do_fold(cx, t, true, |r,b| fldr(r,b)), - |t| do_fold(cx, t, in_fn, |r,b| fldr(r,b))) - } - do_fold(cx, ty, false, fldr) -} + ty_self(_) => { + match self.self_ty_opt { + None => self.tcx.sess.bug("ty_self unexpected here"), + Some(self_ty) => self_ty + } + } -// Substitute *only* type parameters. Used in trans where regions are erased. -pub fn subst_tps(cx: ctxt, tps: &[t], self_ty_opt: Option, typ: t) -> t { - if tps.len() == 0u && self_ty_opt.is_none() { return typ; } - let tb = ty::get(typ); - if self_ty_opt.is_none() && !tbox_has_flag(tb, has_params) { return typ; } - match tb.sty { - ty_param(p) => tps[p.idx], - ty_self(_) => { - match self_ty_opt { - None => cx.sess.bug("ty_self unexpected here"), - Some(self_ty) => { - subst_tps(cx, tps, self_ty_opt, self_ty) + _ => { + ty_fold::super_fold_ty(self, t) } } } - ref sty => { - fold_sty_to_ty(cx, sty, |t| subst_tps(cx, tps, self_ty_opt, t)) - } } } @@ -2256,7 +2150,7 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { ast::MutMutable => TC::ReachesMutable | TC::OwnsAffine, ast::MutImmutable => TC::None, }; - b | (TC::ReachesBorrowed).when(region != ty::re_static) + b | (TC::ReachesBorrowed).when(region != ty::ReStatic) } fn closure_contents(cx: ctxt, cty: &ClosureTy) -> TypeContents { @@ -2722,55 +2616,6 @@ pub fn index_sty(sty: &sty) -> Option { } } -/** - * Enforces an arbitrary but consistent total ordering over - * free regions. This is needed for establishing a consistent - * LUB in region_inference. */ -impl cmp::TotalOrd for FreeRegion { - fn cmp(&self, other: &FreeRegion) -> Ordering { - cmp::cmp2(&self.scope_id, &self.bound_region, - &other.scope_id, &other.bound_region) - } -} - -impl cmp::TotalEq for FreeRegion { - fn equals(&self, other: &FreeRegion) -> bool { - *self == *other - } -} - -/** - * Enforces an arbitrary but consistent total ordering over - * bound regions. This is needed for establishing a consistent - * LUB in region_inference. */ -impl cmp::TotalOrd for bound_region { - fn cmp(&self, other: &bound_region) -> Ordering { - match (self, other) { - (&ty::br_self, &ty::br_self) => cmp::Equal, - (&ty::br_self, _) => cmp::Less, - - (&ty::br_anon(ref a1), &ty::br_anon(ref a2)) => a1.cmp(a2), - (&ty::br_anon(*), _) => cmp::Less, - - (&ty::br_named(ref a1), &ty::br_named(ref a2)) => a1.name.cmp(&a2.name), - (&ty::br_named(*), _) => cmp::Less, - - (&ty::br_cap_avoid(ref a1, @ref b1), - &ty::br_cap_avoid(ref a2, @ref b2)) => cmp::cmp2(a1, b1, a2, b2), - (&ty::br_cap_avoid(*), _) => cmp::Less, - - (&ty::br_fresh(ref a1), &ty::br_fresh(ref a2)) => a1.cmp(a2), - (&ty::br_fresh(*), _) => cmp::Less, - } - } -} - -impl cmp::TotalEq for bound_region { - fn equals(&self, other: &bound_region) -> bool { - *self == *other - } -} - pub fn node_id_to_trait_ref(cx: ctxt, id: ast::NodeId) -> @ty::TraitRef { match cx.trait_refs.find(&id) { Some(&t) => t, @@ -3679,7 +3524,6 @@ fn lookup_locally_or_in_crate_store( load_external: &fn() -> V) -> V { /*! - * * Helper for looking things up in the various maps * that are populated during typeck::collect (e.g., * `cx.methods`, `cx.tcache`, etc). All of these share @@ -3689,8 +3533,8 @@ fn lookup_locally_or_in_crate_store( * the crate loading code (and cache the result for the future). */ - match map.find(&def_id) { - Some(&ref v) => { return (*v).clone(); } + match map.find_copy(&def_id) { + Some(v) => { return v; } None => { } } @@ -3728,7 +3572,7 @@ pub fn method(cx: ctxt, id: ast::DefId) -> @Method { pub fn trait_method_def_ids(cx: ctxt, id: ast::DefId) -> @~[DefId] { lookup_locally_or_in_crate_store( - "methods", id, cx.trait_method_def_ids, + "trait_method_def_ids", id, cx.trait_method_def_ids, || @csearch::get_trait_method_def_ids(cx.cstore, id)) } @@ -4354,77 +4198,57 @@ pub fn ty_params_to_tys(tcx: ty::ctxt, generics: &ast::Generics) -> ~[t] { /// Returns an equivalent type with all the typedefs and self regions removed. pub fn normalize_ty(cx: ctxt, t: t) -> t { - fn normalize_mt(cx: ctxt, mt: mt) -> mt { - mt { ty: normalize_ty(cx, mt.ty), mutbl: mt.mutbl } - } - fn normalize_vstore(vstore: vstore) -> vstore { - match vstore { - vstore_fixed(*) | vstore_uniq | vstore_box => vstore, - vstore_slice(_) => vstore_slice(re_static) - } - } - - match cx.normalized_cache.find(&t) { - Some(&t) => return t, - None => () - } - - let t = match get(t).sty { - ty_evec(mt, vstore) => - // This type has a vstore. Get rid of it - mk_evec(cx, normalize_mt(cx, mt), normalize_vstore(vstore)), - - ty_estr(vstore) => - // This type has a vstore. Get rid of it - mk_estr(cx, normalize_vstore(vstore)), + let u = TypeNormalizer(cx).fold_ty(t); + return u; - ty_rptr(_, mt) => - // This type has a region. Get rid of it - mk_rptr(cx, re_static, normalize_mt(cx, mt)), + struct TypeNormalizer(ctxt); - ty_closure(ref closure_ty) => { - mk_closure(cx, ClosureTy { - region: ty::re_static, - ..(*closure_ty).clone() - }) - } + impl TypeFolder for TypeNormalizer { + fn tcx(&self) -> ty::ctxt { **self } - ty_enum(did, ref r) => { - match (*r).regions { - NonerasedRegions(_) => { - // trans doesn't care about regions - mk_enum(cx, did, substs {regions: ty::ErasedRegions, - self_ty: None, - tps: (*r).tps.clone()}) + fn fold_ty(&mut self, t: ty::t) -> ty::t { + match self.tcx().normalized_cache.find_copy(&t) { + Some(u) => { + return u; } - ErasedRegions => { - t + None => { + let t_norm = ty_fold::super_fold_ty(self, t); + self.tcx().normalized_cache.insert(t, t_norm); + return t_norm; } } } - ty_struct(did, ref r) => { - match (*r).regions { - NonerasedRegions(_) => { - // Ditto. - mk_struct(cx, did, substs {regions: ty::ErasedRegions, - self_ty: None, - tps: (*r).tps.clone()}) - } - ErasedRegions => { - t - } + fn fold_vstore(&mut self, vstore: vstore) -> vstore { + match vstore { + vstore_fixed(*) | vstore_uniq | vstore_box => vstore, + vstore_slice(_) => vstore_slice(ReStatic) } } - _ => - t - }; + fn fold_region(&mut self, _: ty::Region) -> ty::Region { + ty::ReStatic + } + + fn fold_substs(&mut self, + substs: &substs) + -> substs { + substs { regions: ErasedRegions, + self_ty: ty_fold::fold_opt_ty(self, substs.self_ty), + tps: ty_fold::fold_ty_vec(self, substs.tps) } + } - let sty = fold_sty(&get(t).sty, |t| { normalize_ty(cx, t) }); - let t_norm = mk_t(cx, sty); - cx.normalized_cache.insert(t, t_norm); - return t_norm; + fn fold_sig(&mut self, + sig: &ty::FnSig) + -> ty::FnSig { + // The binder-id is only relevant to bound regions, which + // are erased at trans time. + ty::FnSig { binder_id: ast::DUMMY_NODE_ID, + inputs: ty_fold::fold_ty_vec(self, sig.inputs), + output: self.fold_ty(sig.output), + variadic: sig.variadic } + } + } } pub trait ExprTyProvider { @@ -4597,6 +4421,12 @@ pub fn visitor_object_ty(tcx: ctxt, EmptyBuiltinBounds()))) } +pub fn item_variances(tcx: ctxt, item_id: ast::DefId) -> @ItemVariances { + lookup_locally_or_in_crate_store( + "item_variance_map", item_id, tcx.item_variance_map, + || @csearch::get_item_variances(tcx.cstore, item_id)) +} + /// Records a trait-to-implementation mapping. fn record_trait_implementation(tcx: ctxt, trait_def_id: DefId, @@ -4737,10 +4567,16 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: @str) -> u64 { let mut hash = SipState::new(0, 0); let region = |_hash: &mut SipState, r: Region| { match r { - re_static => {} - - re_empty | re_bound(*) | re_free(*) | re_scope(*) | re_infer(*) => + ReStatic => {} + + ReEmpty | + ReEarlyBound(*) | + ReLateBound(*) | + ReFree(*) | + ReScope(*) | + ReInfer(*) => { tcx.sess.bug("non-static region found when hashing a type") + } } }; let vstore = |hash: &mut SipState, v: vstore| { @@ -4878,3 +4714,90 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: @str) -> u64 { hash.result_u64() } + +impl Variance { + pub fn to_str(self) -> &'static str { + match self { + Covariant => "+", + Contravariant => "-", + Invariant => "o", + Bivariant => "*", + } + } +} + +pub fn construct_parameter_environment( + tcx: ctxt, + self_bound: Option<@TraitRef>, + item_type_params: &[TypeParameterDef], + method_type_params: &[TypeParameterDef], + item_region_params: &[RegionParameterDef], + free_id: ast::NodeId) + -> ParameterEnvironment +{ + /*! See `ParameterEnvironment` struct def'n for details */ + + // + // Construct the free substs. + // + + // map Self => Self + let self_ty = self_bound.map(|t| ty::mk_self(tcx, t.def_id)); + + // map A => A + let num_item_type_params = item_type_params.len(); + let num_method_type_params = method_type_params.len(); + let num_type_params = num_item_type_params + num_method_type_params; + let type_params = vec::from_fn(num_type_params, |i| { + let def_id = if i < num_item_type_params { + item_type_params[i].def_id + } else { + method_type_params[i - num_item_type_params].def_id + }; + + ty::mk_param(tcx, i, def_id) + }); + + // map bound 'a => free 'a + let region_params = item_region_params.iter(). + map(|r| ty::ReFree(ty::FreeRegion { + scope_id: free_id, + bound_region: ty::BrNamed(r.def_id, r.ident)})). + collect(); + + let free_substs = substs { + self_ty: self_ty, + tps: type_params, + regions: ty::NonerasedRegions(region_params) + }; + + // + // Compute the bounds on Self and the type parameters. + // + + let self_bound_substd = self_bound.map(|b| b.subst(tcx, &free_substs)); + let type_param_bounds_substd = vec::from_fn(num_type_params, |i| { + if i < num_item_type_params { + (*item_type_params[i].bounds).subst(tcx, &free_substs) + } else { + let j = i - num_item_type_params; + (*method_type_params[j].bounds).subst(tcx, &free_substs) + } + }); + + ty::ParameterEnvironment { + free_substs: free_substs, + self_param_bound: self_bound_substd, + type_param_bounds: type_param_bounds_substd, + } +} + +impl substs { + pub fn empty() -> substs { + substs { + self_ty: None, + tps: ~[], + regions: NonerasedRegions(opt_vec::Empty) + } + } +} diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs new file mode 100644 index 0000000000000..dc4fca0176a5f --- /dev/null +++ b/src/librustc/middle/ty_fold.rs @@ -0,0 +1,286 @@ +// Copyright 2012-2013 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. + +// Generalized type folding mechanism. + +use middle::ty; +use util::ppaux::Repr; + +pub trait TypeFolder { + fn tcx(&self) -> ty::ctxt; + + fn fold_ty(&mut self, t: ty::t) -> ty::t { + super_fold_ty(self, t) + } + + fn fold_mt(&mut self, t: &ty::mt) -> ty::mt { + super_fold_mt(self, t) + } + + fn fold_trait_ref(&mut self, t: &ty::TraitRef) -> ty::TraitRef { + super_fold_trait_ref(self, t) + } + + fn fold_sty(&mut self, sty: &ty::sty) -> ty::sty { + super_fold_sty(self, sty) + } + + fn fold_substs(&mut self, + substs: &ty::substs) + -> ty::substs { + super_fold_substs(self, substs) + } + + fn fold_sig(&mut self, + sig: &ty::FnSig) + -> ty::FnSig { + super_fold_sig(self, sig) + } + + fn fold_bare_fn_ty(&mut self, + fty: &ty::BareFnTy) + -> ty::BareFnTy { + ty::BareFnTy { sig: self.fold_sig(&fty.sig), + abis: fty.abis, + purity: fty.purity } + } + + fn fold_closure_ty(&mut self, + fty: &ty::ClosureTy) + -> ty::ClosureTy { + ty::ClosureTy { + region: self.fold_region(fty.region), + sig: self.fold_sig(&fty.sig), + purity: fty.purity, + sigil: fty.sigil, + onceness: fty.onceness, + bounds: fty.bounds, + } + } + + fn fold_region(&mut self, r: ty::Region) -> ty::Region { + r + } + + fn fold_vstore(&mut self, vstore: ty::vstore) -> ty::vstore { + super_fold_vstore(self, vstore) + } + + fn fold_trait_store(&mut self, s: ty::TraitStore) -> ty::TraitStore { + super_fold_trait_store(self, s) + } +} + +pub fn fold_opt_ty(this: &mut T, + t: Option) + -> Option { + t.map(|t| this.fold_ty(t)) +} + +pub fn fold_ty_vec(this: &mut T, + tys: &[ty::t]) + -> ~[ty::t] { + tys.map(|t| this.fold_ty(*t)) +} + +pub fn super_fold_ty(this: &mut T, + t: ty::t) + -> ty::t { + ty::mk_t(this.tcx(), this.fold_sty(&ty::get(t).sty)) +} + +pub fn super_fold_substs(this: &mut T, + substs: &ty::substs) + -> ty::substs { + let regions = match substs.regions { + ty::ErasedRegions => { + ty::ErasedRegions + } + ty::NonerasedRegions(ref regions) => { + ty::NonerasedRegions(regions.map(|r| this.fold_region(*r))) + } + }; + + ty::substs { regions: regions, + self_ty: fold_opt_ty(this, substs.self_ty), + tps: fold_ty_vec(this, substs.tps), } +} + +pub fn super_fold_sig(this: &mut T, + sig: &ty::FnSig) + -> ty::FnSig { + ty::FnSig { binder_id: sig.binder_id, + inputs: fold_ty_vec(this, sig.inputs), + output: this.fold_ty(sig.output), + variadic: sig.variadic } +} + +pub fn super_fold_trait_ref(this: &mut T, + t: &ty::TraitRef) + -> ty::TraitRef { + ty::TraitRef { + def_id: t.def_id, + substs: this.fold_substs(&t.substs) + } +} + +pub fn super_fold_mt(this: &mut T, + mt: &ty::mt) -> ty::mt { + ty::mt {ty: this.fold_ty(mt.ty), + mutbl: mt.mutbl} +} + +pub fn super_fold_sty(this: &mut T, + sty: &ty::sty) -> ty::sty { + match *sty { + ty::ty_box(ref tm) => { + ty::ty_box(this.fold_mt(tm)) + } + ty::ty_uniq(ref tm) => { + ty::ty_uniq(this.fold_mt(tm)) + } + ty::ty_ptr(ref tm) => { + ty::ty_ptr(this.fold_mt(tm)) + } + ty::ty_unboxed_vec(ref tm) => { + ty::ty_unboxed_vec(this.fold_mt(tm)) + } + ty::ty_evec(ref tm, vst) => { + ty::ty_evec(this.fold_mt(tm), + this.fold_vstore(vst)) + } + ty::ty_enum(tid, ref substs) => { + ty::ty_enum(tid, this.fold_substs(substs)) + } + ty::ty_trait(did, ref substs, st, mutbl, bounds) => { + ty::ty_trait(did, + this.fold_substs(substs), + this.fold_trait_store(st), + mutbl, + bounds) + } + ty::ty_tup(ref ts) => { + ty::ty_tup(fold_ty_vec(this, *ts)) + } + ty::ty_bare_fn(ref f) => { + ty::ty_bare_fn(this.fold_bare_fn_ty(f)) + } + ty::ty_closure(ref f) => { + ty::ty_closure(this.fold_closure_ty(f)) + } + ty::ty_rptr(r, ref tm) => { + ty::ty_rptr(this.fold_region(r), + ty::mt {ty: this.fold_ty(tm.ty), + mutbl: tm.mutbl}) + } + ty::ty_struct(did, ref substs) => { + ty::ty_struct(did, + this.fold_substs(substs)) + } + ty::ty_estr(vst) => { + ty::ty_estr(this.fold_vstore(vst)) + } + ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | + ty::ty_int(_) | ty::ty_uint(_) | + ty::ty_float(_) | ty::ty_type | + ty::ty_opaque_closure_ptr(_) | + ty::ty_err | ty::ty_opaque_box | ty::ty_infer(_) | + ty::ty_param(*) | ty::ty_self(_) => { + (*sty).clone() + } + } +} + +pub fn super_fold_vstore(this: &mut T, + vstore: ty::vstore) + -> ty::vstore { + match vstore { + ty::vstore_fixed(i) => ty::vstore_fixed(i), + ty::vstore_uniq => ty::vstore_uniq, + ty::vstore_box => ty::vstore_box, + ty::vstore_slice(r) => ty::vstore_slice(this.fold_region(r)), + } +} + +pub fn super_fold_trait_store(this: &mut T, + trait_store: ty::TraitStore) + -> ty::TraitStore { + match trait_store { + ty::UniqTraitStore => ty::UniqTraitStore, + ty::BoxTraitStore => ty::BoxTraitStore, + ty::RegionTraitStore(r) => ty::RegionTraitStore(this.fold_region(r)), + } +} + +/////////////////////////////////////////////////////////////////////////// +// Some sample folders + +pub struct BottomUpFolder<'self> { + tcx: ty::ctxt, + fldop: &'self fn(ty::t) -> ty::t, +} + +impl<'self> TypeFolder for BottomUpFolder<'self> { + fn tcx(&self) -> ty::ctxt { self.tcx } + + fn fold_ty(&mut self, ty: ty::t) -> ty::t { + let t1 = super_fold_ty(self, ty); + (self.fldop)(t1) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Region folder + +pub struct RegionFolder<'self> { + tcx: ty::ctxt, + fld_t: &'self fn(ty::t) -> ty::t, + fld_r: &'self fn(ty::Region) -> ty::Region, +} + +impl<'self> RegionFolder<'self> { + pub fn general(tcx: ty::ctxt, + fld_r: &'self fn(ty::Region) -> ty::Region, + fld_t: &'self fn(ty::t) -> ty::t) + -> RegionFolder<'self> { + RegionFolder { + tcx: tcx, + fld_t: fld_t, + fld_r: fld_r + } + } + + pub fn regions(tcx: ty::ctxt, + fld_r: &'self fn(ty::Region) -> ty::Region) + -> RegionFolder<'self> { + fn noop(t: ty::t) -> ty::t { t } + + RegionFolder { + tcx: tcx, + fld_t: noop, + fld_r: fld_r + } + } +} + +impl<'self> TypeFolder for RegionFolder<'self> { + fn tcx(&self) -> ty::ctxt { self.tcx } + + fn fold_ty(&mut self, ty: ty::t) -> ty::t { + debug!("RegionFolder.fold_ty({})", ty.repr(self.tcx())); + let t1 = super_fold_ty(self, ty); + (self.fld_t)(t1) + } + + fn fold_region(&mut self, r: ty::Region) -> ty::Region { + debug!("RegionFolder.fold_region({})", r.repr(self.tcx())); + (self.fld_r)(r) + } +} diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 45a6d709b04fb..57581306b5d5d 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -23,13 +23,10 @@ * In the check phase, when the @FnCtxt is used as the `AstConv`, * `get_item_ty()` just looks up the item type in `tcx.tcache`. * - * The `RegionScope` trait controls how region references are - * handled. It has two methods which are used to resolve anonymous - * region references (e.g., `&T`) and named region references (e.g., - * `&a.T`). There are numerous region scopes that can be used, but most - * commonly you want either `EmptyRscope`, which permits only the static - * region, or `TypeRscope`, which permits the self region if the type in - * question is parameterized by a region. + * The `RegionScope` trait controls what happens when the user does + * not specify a region in some location where a region is required + * (e.g., if the user writes `&Foo` as a type rather than `&'a Foo`). + * See the `rscope` module for more details. * * Unlike the `AstConv` trait, the region scope can change as we descend * the type. This is to accommodate the fact that (a) fn types are binding @@ -57,20 +54,17 @@ use middle::const_eval; use middle::ty::{substs}; use middle::ty::{ty_param_substs_and_ty}; use middle::ty; -use middle::typeck::rscope::in_binding_rscope; -use middle::typeck::rscope::{RegionScope, RegionError}; -use middle::typeck::rscope::RegionParamNames; +use middle::typeck::rscope; +use middle::typeck::rscope::{RegionScope}; use middle::typeck::lookup_def_tcx; -use std::result; +use std::vec; use syntax::abi::AbiSet; use syntax::{ast, ast_util}; use syntax::codemap::Span; use syntax::opt_vec::OptVec; use syntax::opt_vec; use syntax::print::pprust::{lifetime_to_str, path_to_str}; -use syntax::parse::token::special_idents; -use util::common::indenter; pub trait AstConv { fn tcx(&self) -> ty::ctxt; @@ -81,64 +75,90 @@ pub trait AstConv { fn ty_infer(&self, span: Span) -> ty::t; } -pub fn get_region_reporting_err( +pub fn ast_region_to_region( tcx: ty::ctxt, - span: Span, - a_r: &Option, - res: Result) -> ty::Region + lifetime: &ast::Lifetime) + -> ty::Region { - match res { - result::Ok(r) => r, - result::Err(ref e) => { - let descr = match a_r { - &None => ~"anonymous lifetime", - &Some(ref a) => format!("lifetime {}", - lifetime_to_str(a, tcx.sess.intr())) - }; - tcx.sess.span_err( - span, - format!("Illegal {}: {}", - descr, e.msg)); - e.replacement + let r = match tcx.named_region_map.find(&lifetime.id) { + None => { + // should have been recorded by the `resolve_lifetime` pass + tcx.sess.span_bug(lifetime.span, "unresolved lifetime"); } - } + + Some(&ast::DefStaticRegion) => { + ty::ReStatic + } + + Some(&ast::DefLateBoundRegion(binder_id, _, id)) => { + ty::ReLateBound(binder_id, ty::BrNamed(ast_util::local_def(id), + lifetime.ident)) + } + + Some(&ast::DefEarlyBoundRegion(index, id)) => { + ty::ReEarlyBound(id, index, lifetime.ident) + } + + Some(&ast::DefFreeRegion(scope_id, id)) => { + ty::ReFree(ty::FreeRegion { + scope_id: scope_id, + bound_region: ty::BrNamed(ast_util::local_def(id), + lifetime.ident) + }) + } + }; + + debug!("ast_region_to_region(lifetime={} id={}) yields {}", + lifetime_to_str(lifetime, tcx.sess.intr()), + lifetime.id, + r.repr(tcx)); + + r } -pub fn ast_region_to_region( +fn opt_ast_region_to_region( this: &AC, rscope: &RS, default_span: Span, opt_lifetime: &Option) -> ty::Region { - let (span, res) = match opt_lifetime { - &None => { - (default_span, rscope.anon_region(default_span)) - } - &Some(ref lifetime) if lifetime.ident == special_idents::statik => { - (lifetime.span, Ok(ty::re_static)) - } - &Some(ref lifetime) if lifetime.ident == special_idents::self_ => { - (lifetime.span, rscope.self_region(lifetime.span)) + let r = match *opt_lifetime { + Some(ref lifetime) => { + ast_region_to_region(this.tcx(), lifetime) } - &Some(ref lifetime) => { - (lifetime.span, rscope.named_region(lifetime.span, - lifetime.ident)) + + None => { + match rscope.anon_regions(default_span, 1) { + Err(()) => { + debug!("optional region in illegal location"); + this.tcx().sess.span_err( + default_span, "missing lifetime specifier"); + ty::ReStatic + } + + Ok(rs) => { + rs[0] + } + } } }; - get_region_reporting_err(this.tcx(), span, opt_lifetime, res) + debug!("opt_ast_region_to_region(opt_lifetime={:?}) yields {}", + opt_lifetime.as_ref().map( + |e| lifetime_to_str(e, this.tcx().sess.intr())), + r.repr(this.tcx())); + + r } -fn ast_path_substs( +fn ast_path_substs( this: &AC, rscope: &RS, - def_id: ast::DefId, decl_generics: &ty::Generics, self_ty: Option, path: &ast::Path) -> ty::substs { /*! - * * Given a path `path` that refers to an item `I` with the * declared generics `decl_generics`, returns an appropriate * set of substitutions for this particular reference to `I`. @@ -149,30 +169,28 @@ fn ast_path_substs( // If the type is parameterized by the this region, then replace this // region with the current anon region binding (in other words, // whatever & would get replaced with). - let regions = match (&decl_generics.region_param, - &path.segments.last().lifetime) { - (&None, &None) => { - opt_vec::Empty - } - (&None, &Some(_)) => { + let expected_num_region_params = decl_generics.region_param_defs.len(); + let supplied_num_region_params = path.segments.last().lifetimes.len(); + let regions = if expected_num_region_params == supplied_num_region_params { + path.segments.last().lifetimes.map( + |l| ast_region_to_region(this.tcx(), l)) + } else { + let anon_regions = + rscope.anon_regions(path.span, expected_num_region_params); + + if supplied_num_region_params != 0 || anon_regions.is_err() { tcx.sess.span_err( path.span, - format!("no region bound is allowed on `{}`, \ - which is not declared as containing region pointers", - ty::item_path_str(tcx, def_id))); - opt_vec::Empty + format!("wrong number of lifetime parameters: \ + expected {} but found {}", + expected_num_region_params, + supplied_num_region_params)); } - (&Some(_), &None) => { - let res = rscope.anon_region(path.span); - let r = get_region_reporting_err(this.tcx(), path.span, &None, res); - opt_vec::with(r) - } - (&Some(_), &Some(_)) => { - opt_vec::with( - ast_region_to_region(this, - rscope, - path.span, - &path.segments.last().lifetime)) + + match anon_regions { + Ok(v) => opt_vec::from(v), + Err(()) => opt_vec::from(vec::from_fn(expected_num_region_params, + |_| ty::ReStatic)) // hokey } }; @@ -200,7 +218,7 @@ fn ast_path_substs( } pub fn ast_path_to_substs_and_ty( + RS:RegionScope>( this: &AC, rscope: &RS, did: ast::DefId, @@ -212,12 +230,12 @@ pub fn ast_path_to_substs_and_ty( +pub fn ast_path_to_trait_ref( this: &AC, rscope: &RS, trait_def_id: ast::DefId, @@ -230,7 +248,6 @@ pub fn ast_path_to_trait_ref( ast_path_substs( this, rscope, - trait_def.trait_ref.def_id, &trait_def.generics, self_ty, path); @@ -240,7 +257,7 @@ pub fn ast_path_to_trait_ref( return trait_ref; } -pub fn ast_path_to_ty( +pub fn ast_path_to_ty( this: &AC, rscope: &RS, did: ast::DefId, @@ -260,12 +277,11 @@ pub static NO_REGIONS: uint = 1; pub static NO_TPS: uint = 2; // Parses the programmer's textual representation of a type into our -// internal notion of a type. `getter` is a function that returns the type -// corresponding to a definition ID: -pub fn ast_ty_to_ty( +// internal notion of a type. +pub fn ast_ty_to_ty( this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t { - fn ast_mt_to_mt( + fn ast_mt_to_mt( this: &AC, rscope: &RS, mt: &ast::mt) -> ty::mt { ty::mt {ty: ast_ty_to_ty(this, rscope, mt.ty), mutbl: mt.mutbl} @@ -274,7 +290,7 @@ pub fn ast_ty_to_ty( // Handle @, ~, and & being able to mean estrs and evecs. // If a_seq_ty is a str or a vec, make it an estr/evec. // Also handle first-class trait types. - fn mk_pointer( + fn mk_pointer( this: &AC, rscope: &RS, a_seq_ty: &ast::mt, @@ -282,6 +298,7 @@ pub fn ast_ty_to_ty( constr: &fn(ty::mt) -> ty::t) -> ty::t { let tcx = this.tcx(); + debug!("mk_pointer(vst={:?})", vst); match a_seq_ty.ty.node { ast::ty_vec(ref mt) => { @@ -289,6 +306,7 @@ pub fn ast_ty_to_ty( if a_seq_ty.mutbl == ast::MutMutable { mt = ty::mt { ty: mt.ty, mutbl: a_seq_ty.mutbl }; } + debug!("&[]: vst={:?}", vst); return ty::mk_evec(tcx, mt, vst); } ast::ty_path(ref path, ref bounds, id) => { @@ -347,7 +365,7 @@ pub fn ast_ty_to_ty( } if (flags & NO_REGIONS) != 0u { - if path.segments.last().lifetime.is_some() { + if !path.segments.last().lifetimes.is_empty() { tcx.sess.span_err( path.span, "region parameters are not allowed on this type"); @@ -387,7 +405,8 @@ pub fn ast_ty_to_ty( ty::mk_ptr(tcx, ast_mt_to_mt(this, rscope, mt)) } ast::ty_rptr(ref region, ref mt) => { - let r = ast_region_to_region(this, rscope, ast_ty.span, region); + let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region); + debug!("ty_rptr r={}", r.repr(this.tcx())); mk_pointer(this, rscope, mt, ty::vstore_slice(r), |tmt| ty::mk_rptr(tcx, r, tmt)) } @@ -399,8 +418,8 @@ pub fn ast_ty_to_ty( if bf.decl.variadic && !bf.abis.is_c() { tcx.sess.span_err(ast_ty.span, "variadic function must have C calling convention"); } - ty::mk_bare_fn(tcx, ty_of_bare_fn(this, rscope, bf.purity, - bf.abis, &bf.lifetimes, &bf.decl)) + ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.purity, + bf.abis, &bf.decl)) } ast::ty_closure(ref f) => { if f.sigil == ast::ManagedSigil { @@ -411,12 +430,13 @@ pub fn ast_ty_to_ty( let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, match f.sigil { // Use corresponding trait store to figure out default bounds // if none were specified. - ast::BorrowedSigil => ty::RegionTraitStore(ty::re_empty), // dummy region + ast::BorrowedSigil => ty::RegionTraitStore(ty::ReEmpty), // dummy region ast::OwnedSigil => ty::UniqTraitStore, ast::ManagedSigil => ty::BoxTraitStore, }); let fn_decl = ty_of_closure(this, rscope, + ast_ty.id, f.sigil, f.purity, f.onceness, @@ -424,7 +444,6 @@ pub fn ast_ty_to_ty( &f.region, &f.decl, None, - &f.lifetimes, ast_ty.span); ty::mk_closure(tcx, fn_decl) } @@ -551,7 +570,7 @@ pub fn ast_ty_to_ty( } pub fn ty_of_arg( + RS:RegionScope>( this: &AC, rscope: &RS, a: &ast::arg, @@ -564,46 +583,15 @@ pub fn ty_of_arg( - this: &AC, - ast_lifetimes: &OptVec) -> OptVec -{ - /*! - * - * Converts a list of lifetimes into a list of bound identifier - * names. Does not permit special names like 'static or 'this to - * be bound. Note that this function is for use in closures, - * methods, and fn definitions. It is legal to bind 'this in a - * type. Eventually this distinction should go away and the same - * rules should apply everywhere ('this would not be a special name - * at that point). - */ - - let special_idents = [special_idents::statik, special_idents::self_]; - let mut bound_lifetime_names = opt_vec::Empty; - ast_lifetimes.map_to_vec(|ast_lifetime| { - if special_idents.iter().any(|&i| i == ast_lifetime.ident) { - this.tcx().sess.span_err( - ast_lifetime.span, - format!("illegal lifetime parameter name: `{}`", - lifetime_to_str(ast_lifetime, this.tcx().sess.intr()))); - } else { - bound_lifetime_names.push(ast_lifetime.ident); - } - }); - bound_lifetime_names -} - struct SelfInfo { untransformed_self_ty: ty::t, explicit_self: ast::explicit_self } -pub fn ty_of_method( +pub fn ty_of_method( this: &AC, - rscope: &RS, + id: ast::NodeId, purity: ast::purity, - lifetimes: &OptVec, untransformed_self_ty: ty::t, explicit_self: ast::explicit_self, decl: &ast::fn_decl) -> (Option, ty::BareFnTy) @@ -613,40 +601,35 @@ pub fn ty_of_method( explicit_self: explicit_self }; let (a, b) = ty_of_method_or_bare_fn( - this, rscope, purity, AbiSet::Rust(), lifetimes, Some(&self_info), decl); + this, id, purity, AbiSet::Rust(), Some(&self_info), decl); (a.unwrap(), b) } -pub fn ty_of_bare_fn( +pub fn ty_of_bare_fn( this: &AC, - rscope: &RS, + id: ast::NodeId, purity: ast::purity, abi: AbiSet, - lifetimes: &OptVec, decl: &ast::fn_decl) -> ty::BareFnTy { - let (_, b) = ty_of_method_or_bare_fn( - this, rscope, purity, abi, lifetimes, None, decl); + let (_, b) = ty_of_method_or_bare_fn(this, id, purity, + abi, None, decl); b } -fn ty_of_method_or_bare_fn( +fn ty_of_method_or_bare_fn( this: &AC, - rscope: &RS, + id: ast::NodeId, purity: ast::purity, abi: AbiSet, - lifetimes: &OptVec, opt_self_info: Option<&SelfInfo>, decl: &ast::fn_decl) -> (Option>, ty::BareFnTy) { - debug!("ty_of_bare_fn"); + debug!("ty_of_method_or_bare_fn"); // new region names that appear inside of the fn decl are bound to // that function type - let bound_lifetime_names = bound_lifetimes(this, lifetimes); - let rb = - in_binding_rscope(rscope, - RegionParamNames(bound_lifetime_names.clone())); + let rb = rscope::BindingRscope::new(id); let opt_transformed_self_ty = do opt_self_info.map |self_info| { transform_self_ty(this, &rb, self_info) @@ -663,15 +646,13 @@ fn ty_of_method_or_bare_fn( ty::BareFnTy { purity: purity, abis: abi, - sig: ty::FnSig { - bound_lifetime_names: bound_lifetime_names, - inputs: input_tys, - output: output_ty, - variadic: decl.variadic - } + sig: ty::FnSig {binder_id: id, + inputs: input_tys, + output: output_ty, + variadic: decl.variadic} }); - fn transform_self_ty( + fn transform_self_ty( this: &AC, rscope: &RS, self_info: &SelfInfo) -> Option @@ -683,9 +664,9 @@ fn ty_of_method_or_bare_fn( } ast::sty_region(ref lifetime, mutability) => { let region = - ast_region_to_region(this, rscope, - self_info.explicit_self.span, - lifetime); + opt_ast_region_to_region(this, rscope, + self_info.explicit_self.span, + lifetime); Some(ty::mk_rptr(this.tcx(), region, ty::mt {ty: self_info.untransformed_self_ty, mutbl: mutability})) @@ -704,9 +685,10 @@ fn ty_of_method_or_bare_fn( } } -pub fn ty_of_closure( +pub fn ty_of_closure( this: &AC, rscope: &RS, + id: ast::NodeId, sigil: ast::Sigil, purity: ast::purity, onceness: ast::Onceness, @@ -714,34 +696,27 @@ pub fn ty_of_closure( opt_lifetime: &Option, decl: &ast::fn_decl, expected_sig: Option, - lifetimes: &OptVec, span: Span) -> ty::ClosureTy { - // The caller should not both provide explicit bound lifetime - // names and expected types. Either we infer the bound lifetime - // names or they are provided, but not both. - assert!(lifetimes.is_empty() || expected_sig.is_none()); - debug!("ty_of_fn_decl"); - let _i = indenter(); // resolve the function bound region in the original region // scope `rscope`, not the scope of the function parameters let bound_region = match opt_lifetime { - &Some(_) => { - ast_region_to_region(this, rscope, span, opt_lifetime) + &Some(ref lifetime) => { + ast_region_to_region(this.tcx(), lifetime) } &None => { match sigil { ast::OwnedSigil | ast::ManagedSigil => { // @fn(), ~fn() default to static as the bound // on their upvars: - ty::re_static + ty::ReStatic } ast::BorrowedSigil => { // &fn() defaults as normal for an omitted lifetime: - ast_region_to_region(this, rscope, span, opt_lifetime) + opt_ast_region_to_region(this, rscope, span, opt_lifetime) } } } @@ -749,10 +724,7 @@ pub fn ty_of_closure( // new region names that appear inside of the fn decl are bound to // that function type - let bound_lifetime_names = bound_lifetimes(this, lifetimes); - let rb = - in_binding_rscope(rscope, - RegionParamNames(bound_lifetime_names.clone())); + let rb = rscope::BindingRscope::new(id); let input_tys = do decl.inputs.iter().enumerate().map |(i, a)| { let expected_arg_ty = do expected_sig.as_ref().and_then |e| { @@ -776,12 +748,10 @@ pub fn ty_of_closure( onceness: onceness, region: bound_region, bounds: bounds, - sig: ty::FnSig { - bound_lifetime_names: bound_lifetime_names, - inputs: input_tys, - output: output_ty, - variadic: decl.variadic - } + sig: ty::FnSig {binder_id: id, + inputs: input_tys, + output: output_ty, + variadic: decl.variadic} } } @@ -832,7 +802,7 @@ fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option { + (&None, ty::RegionTraitStore(ty::ReStatic)) => { let mut set = ty::EmptyBuiltinBounds(); set.add(ty::BoundStatic); set } // &'r Trait is sugar for &'r Trait:. diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index af1d5ce3cc608..870b29882fd6e 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -85,7 +85,6 @@ use middle::ty::*; use middle::ty; use middle::typeck::check::{FnCtxt, impl_self_ty}; use middle::typeck::check::{structurally_resolved_type}; -use middle::typeck::check::vtable::VtableContext; use middle::typeck::check::vtable; use middle::typeck::check; use middle::typeck::infer; @@ -99,7 +98,6 @@ use util::ppaux::Repr; use std::hashmap::HashSet; use std::result; use std::vec; -use extra::list::Nil; use syntax::ast::{DefId, sty_value, sty_region, sty_box}; use syntax::ast::{sty_uniq, sty_static, NodeId}; use syntax::ast::{MutMutable, MutImmutable}; @@ -265,8 +263,7 @@ impl<'self> LookupContext<'self> { self.search_for_autosliced_method(self_ty, autoderefs) } - fn deref(&self, ty: ty::t) - -> Option { + fn deref(&self, ty: ty::t) -> Option { match ty::deref(self.tcx(), ty, false) { None => None, Some(t) => { @@ -327,11 +324,10 @@ impl<'self> LookupContext<'self> { ty_param(p) => { self.push_inherent_candidates_from_param(self_ty, p); } - ty_self(self_did) => { + ty_self(*) => { // Call is of the form "self.foo()" and appears in one // of a trait's default method implementations. - self.push_inherent_candidates_from_self( - self_ty, self_did); + self.push_inherent_candidates_from_self(self_ty); } _ => { /* No bound methods in these types */ } } @@ -448,32 +444,20 @@ impl<'self> LookupContext<'self> { param_ty: param_ty) { debug!("push_inherent_candidates_from_param(param_ty={:?})", param_ty); - let _indenter = indenter(); - - let tcx = self.tcx(); - let type_param_def = match tcx.ty_param_defs.find(¶m_ty.def_id.node) { - Some(t) => t, - None => { - tcx.sess.span_bug( - self.expr.span, - format!("No param def for {:?}", param_ty)); - } - }; - self.push_inherent_candidates_from_bounds( - rcvr_ty, type_param_def.bounds.trait_bounds, + rcvr_ty, + self.fcx.inh.param_env.type_param_bounds[param_ty.idx].trait_bounds, param_numbered(param_ty.idx)); } fn push_inherent_candidates_from_self(&self, - self_ty: ty::t, - did: DefId) { - let tcx = self.tcx(); - - let trait_ref = ty::lookup_trait_def(tcx, did).trait_ref; + rcvr_ty: ty::t) { + debug!("push_inherent_candidates_from_self()"); self.push_inherent_candidates_from_bounds( - self_ty, &[trait_ref], param_self); + rcvr_ty, + [self.fcx.inh.param_env.self_param_bound.unwrap()], + param_self) } fn push_inherent_candidates_from_bounds(&self, @@ -574,10 +558,7 @@ impl<'self> LookupContext<'self> { // determine the `self` of the impl with fresh // variables for each parameter: let location_info = &vtable::location_info_for_expr(self.self_expr); - let vcx = VtableContext { - ccx: self.fcx.ccx, - infcx: self.fcx.infcx() - }; + let vcx = self.fcx.vtable_context(); let ty::ty_param_substs_and_ty { substs: impl_substs, ty: impl_ty @@ -1010,7 +991,7 @@ impl<'self> LookupContext<'self> { }; let (_, opt_transformed_self_ty, fn_sig) = replace_bound_regions_in_fn_sig( - tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig, + tcx, Some(transformed_self_ty), &bare_fn_ty.sig, |br| self.fcx.infcx().next_region_var( infer::BoundRegionInFnCall(self.expr.span, br))); let transformed_self_ty = opt_transformed_self_ty.unwrap(); diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 32284584b6583..98d154a8d73b6 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -81,10 +81,12 @@ use middle::const_eval; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::lint::unreachable_code; +use middle::subst::Subst; use middle::ty::{FnSig, VariantInfo}; use middle::ty::{ty_param_bounds_and_ty, ty_param_substs_and_ty}; use middle::ty::{substs, param_ty, Disr, ExprTyProvider}; use middle::ty; +use middle::ty_fold::TypeFolder; use middle::typeck::astconv::AstConv; use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty}; use middle::typeck::astconv; @@ -99,22 +101,18 @@ use middle::typeck::check::vtable::{LocationInfo, VtableContext}; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; use middle::typeck::infer; -use middle::typeck::rscope::bound_self_region; -use middle::typeck::rscope::{RegionError}; use middle::typeck::rscope::RegionScope; -use middle::typeck::{isr_alist, lookup_def_ccx}; +use middle::typeck::{lookup_def_ccx}; use middle::typeck::no_params; use middle::typeck::{require_same_types, method_map, vtable_map}; use util::common::{block_query, indenter, loop_query}; -use util::ppaux::{bound_region_ptr_to_str}; +use util::ppaux::UserString; use util::ppaux; - use std::hashmap::HashMap; use std::result; use std::util::replace; use std::vec; -use extra::list::Nil; use syntax::abi::AbiSet; use syntax::ast::{provided, required}; use syntax::ast; @@ -127,7 +125,6 @@ use syntax::codemap; use syntax::opt_vec::OptVec; use syntax::opt_vec; use syntax::parse::token; -use syntax::parse::token::special_idents; use syntax::print::pprust; use syntax::visit; use syntax::visit::Visitor; @@ -157,9 +154,10 @@ pub struct SelfInfo { /// Here, the function `foo()` and the closure passed to /// `bar()` will each have their own `FnCtxt`, but they will /// share the inherited fields. -pub struct inherited { +pub struct Inherited { infcx: @mut infer::InferCtxt, locals: @mut HashMap, + param_env: ty::ParameterEnvironment, // Temporary tables: node_types: @mut HashMap, @@ -249,22 +247,25 @@ pub struct FnCtxt { // function return type. fn_kind: FnKind, - in_scope_regions: isr_alist, - - inh: @inherited, + inh: @Inherited, ccx: @mut CrateCtxt, } -pub fn blank_inherited(ccx: @mut CrateCtxt) -> @inherited { - @inherited { - infcx: infer::new_infer_ctxt(ccx.tcx), - locals: @mut HashMap::new(), - node_types: @mut HashMap::new(), - node_type_substs: @mut HashMap::new(), - adjustments: @mut HashMap::new(), - method_map: @mut HashMap::new(), - vtable_map: @mut HashMap::new(), +impl Inherited { + fn new(tcx: ty::ctxt, + param_env: ty::ParameterEnvironment) + -> Inherited { + Inherited { + infcx: infer::new_infer_ctxt(tcx), + locals: @mut HashMap::new(), + param_env: param_env, + node_types: @mut HashMap::new(), + node_type_substs: @mut HashMap::new(), + adjustments: @mut HashMap::new(), + method_map: @mut HashMap::new(), + vtable_map: @mut HashMap::new(), + } } } @@ -272,17 +273,19 @@ pub fn blank_inherited(ccx: @mut CrateCtxt) -> @inherited { pub fn blank_fn_ctxt(ccx: @mut CrateCtxt, rty: ty::t, region_bnd: ast::NodeId) - -> @mut FnCtxt { -// It's kind of a kludge to manufacture a fake function context -// and statement context, but we might as well do write the code only once + -> @mut FnCtxt { + // It's kind of a kludge to manufacture a fake function context + // and statement context, but we might as well do write the code only once + let param_env = ty::ParameterEnvironment { free_substs: substs::empty(), + self_param_bound: None, + type_param_bounds: ~[] }; @mut FnCtxt { err_count_on_creation: ccx.tcx.sess.err_count(), ret_ty: rty, ps: PurityState::function(ast::impure_fn, 0), region_lb: region_bnd, - in_scope_regions: @Nil, fn_kind: Vanilla, - inh: blank_inherited(ccx), + inh: @Inherited::new(ccx.tcx, param_env), ccx: ccx } } @@ -315,14 +318,15 @@ pub fn check_bare_fn(ccx: @mut CrateCtxt, decl: &ast::fn_decl, body: &ast::Block, id: ast::NodeId, - self_info: Option) { - let fty = ty::node_id_to_type(ccx.tcx, id); + self_info: Option, + fty: ty::t, + param_env: ty::ParameterEnvironment) { match ty::get(fty).sty { ty::ty_bare_fn(ref fn_ty) => { let fcx = check_fn(ccx, self_info, fn_ty.purity, &fn_ty.sig, decl, id, body, Vanilla, - @Nil, blank_inherited(ccx));; + @Inherited::new(ccx.tcx, param_env)); vtable::resolve_in_block(fcx, body); regionck::regionck_fn(fcx, body); @@ -411,39 +415,35 @@ pub fn check_fn(ccx: @mut CrateCtxt, id: ast::NodeId, body: &ast::Block, fn_kind: FnKind, - inherited_isr: isr_alist, - inherited: @inherited) -> @mut FnCtxt + inherited: @Inherited) -> @mut FnCtxt { /*! - * * Helper used by check_bare_fn and check_expr_fn. Does the * grungy work of checking a function body and returns the * function context used for that purpose, since in the case of a * fn item there is still a bit more to do. * * - ... - * - inherited_isr: regions in scope from the enclosing fn (if any) * - inherited: other fields inherited from the enclosing fn (if any) */ let tcx = ccx.tcx; let err_count_on_creation = tcx.sess.err_count(); - // ______________________________________________________________________ // First, we have to replace any bound regions in the fn and self // types with free ones. The free region references will be bound // the node_id of the body block. - let (isr, opt_self_info, fn_sig) = { + let (opt_self_info, fn_sig) = { let opt_self_ty = opt_self_info.map(|i| i.self_ty); - let (isr, opt_self_ty, fn_sig) = + let (_, opt_self_ty, fn_sig) = replace_bound_regions_in_fn_sig( - tcx, inherited_isr, opt_self_ty, fn_sig, - |br| ty::re_free(ty::FreeRegion {scope_id: body.id, + tcx, opt_self_ty, fn_sig, + |br| ty::ReFree(ty::FreeRegion {scope_id: body.id, bound_region: br})); let opt_self_info = opt_self_info.map( |si| SelfInfo {self_ty: opt_self_ty.unwrap(), .. si}); - (isr, opt_self_info, fn_sig) + (opt_self_info, fn_sig) }; relate_free_regions(tcx, opt_self_info.map(|s| s.self_ty), &fn_sig); @@ -456,7 +456,6 @@ pub fn check_fn(ccx: @mut CrateCtxt, ppaux::ty_to_str(tcx, ret_ty), opt_self_info.map(|si| ppaux::ty_to_str(tcx, si.self_ty))); - // ______________________________________________________________________ // Create the function context. This is either derived from scratch or, // in the case of function expressions, based on the outer context. let fcx: @mut FnCtxt = { @@ -465,7 +464,6 @@ pub fn check_fn(ccx: @mut CrateCtxt, ret_ty: ret_ty, ps: PurityState::function(purity, id), region_lb: body.id, - in_scope_regions: isr, fn_kind: fn_kind, inh: inherited, ccx: ccx @@ -536,26 +534,6 @@ pub fn check_fn(ccx: @mut CrateCtxt, } } -pub fn check_method(ccx: @mut CrateCtxt, - method: @ast::method) -{ - let method_def_id = local_def(method.id); - let method_ty = ty::method(ccx.tcx, method_def_id); - let opt_self_info = method_ty.transformed_self_ty.map(|ty| { - SelfInfo {self_ty: ty, - self_id: method.self_id, - span: method.explicit_self.span} - }); - - check_bare_fn( - ccx, - &method.decl, - &method.body, - method.id, - opt_self_info - ); -} - pub fn check_no_duplicate_fields(tcx: ty::ctxt, fields: ~[(ast::Ident, Span)]) { let mut field_names = HashMap::new(); @@ -566,7 +544,7 @@ pub fn check_no_duplicate_fields(tcx: ty::ctxt, match orig_sp { Some(orig_sp) => { tcx.sess.span_err(sp, format!("Duplicate field name {} in record type declaration", - tcx.sess.str_of(id))); + tcx.sess.str_of(id))); tcx.sess.span_note(orig_sp, "First declaration of this field occurred here"); break; } @@ -603,18 +581,46 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { it.id); } ast::item_fn(ref decl, _, _, _, ref body) => { - check_bare_fn(ccx, decl, body, it.id, None); + let fn_tpt = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id)); + + // FIXME(#5121) -- won't work for lifetimes that appear in type bounds + let param_env = ty::construct_parameter_environment( + ccx.tcx, + None, + *fn_tpt.generics.type_param_defs, + [], + [], + body.id); + + check_bare_fn(ccx, decl, body, it.id, None, fn_tpt.ty, param_env); } - ast::item_impl(_, _, _, ref ms) => { - let rp = ccx.tcx.region_paramd_items.find(&it.id).map(|x| *x); - debug!("item_impl {} with id {} rp {:?}", - ccx.tcx.sess.str_of(it.ident), it.id, rp); + ast::item_impl(_, ref opt_trait_ref, _, ref ms) => { + debug!("item_impl {} with id {}", ccx.tcx.sess.str_of(it.ident), it.id); + + let impl_tpt = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id)); for m in ms.iter() { - check_method(ccx, *m); + check_method_body(ccx, &impl_tpt.generics, None, *m); + } + + match *opt_trait_ref { + Some(ref ast_trait_ref) => { + let impl_trait_ref = + ty::node_id_to_trait_ref(ccx.tcx, ast_trait_ref.ref_id); + check_impl_methods_against_trait(ccx, + it.span, + &impl_tpt.generics, + ast_trait_ref, + impl_trait_ref, + *ms); + vtable::resolve_impl(ccx, it, &impl_tpt.generics, + impl_trait_ref); + } + None => { } } - vtable::resolve_impl(ccx, it); + } ast::item_trait(_, _, ref trait_methods) => { + let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id)); for trait_method in (*trait_methods).iter() { match *trait_method { required(*) => { @@ -622,7 +628,8 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { // bodies to check. } provided(m) => { - check_method(ccx, m); + check_method_body(ccx, &trait_def.generics, + Some(trait_def.trait_ref), m); } } } @@ -662,6 +669,362 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { } } +fn check_method_body(ccx: @mut CrateCtxt, + item_generics: &ty::Generics, + self_bound: Option<@ty::TraitRef>, + method: @ast::method) { + /*! + * Type checks a method body. + * + * # Parameters + * - `item_generics`: generics defined on the impl/trait that contains + * the method + * - `self_bound`: bound for the `Self` type parameter, if any + * - `method`: the method definition + */ + + debug!("check_method_body(item_generics={}, \ + self_bound={}, \ + method.id={})", + item_generics.repr(ccx.tcx), + self_bound.repr(ccx.tcx), + method.id); + let method_def_id = local_def(method.id); + let method_ty = ty::method(ccx.tcx, method_def_id); + let method_generics = &method_ty.generics; + + let param_env = + ty::construct_parameter_environment( + ccx.tcx, + self_bound, + *item_generics.type_param_defs, + *method_generics.type_param_defs, + item_generics.region_param_defs, + method.body.id); + + // Compute the self type and fty from point of view of inside fn + let opt_self_info = method_ty.transformed_self_ty.map(|ty| { + SelfInfo {self_ty: ty.subst(ccx.tcx, ¶m_env.free_substs), + self_id: method.self_id, + span: method.explicit_self.span} + }); + let fty = ty::node_id_to_type(ccx.tcx, method.id); + let fty = fty.subst(ccx.tcx, ¶m_env.free_substs); + + check_bare_fn( + ccx, + &method.decl, + &method.body, + method.id, + opt_self_info, + fty, + param_env); +} + +fn check_impl_methods_against_trait(ccx: @mut CrateCtxt, + impl_span: Span, + impl_generics: &ty::Generics, + ast_trait_ref: &ast::trait_ref, + impl_trait_ref: &ty::TraitRef, + impl_methods: &[@ast::method]) { + // Locate trait methods + let tcx = ccx.tcx; + let trait_methods = ty::trait_methods(tcx, impl_trait_ref.def_id); + + // Check existing impl methods to see if they are both present in trait + // and compatible with trait signature + for impl_method in impl_methods.iter() { + let impl_method_def_id = local_def(impl_method.id); + let impl_method_ty = ty::method(ccx.tcx, impl_method_def_id); + + // If this is an impl of a trait method, find the corresponding + // method definition in the trait. + let opt_trait_method_ty = + trait_methods.iter(). + find(|tm| tm.ident.name == impl_method_ty.ident.name); + match opt_trait_method_ty { + Some(trait_method_ty) => { + compare_impl_method(ccx.tcx, + impl_generics, + impl_method_ty, + impl_method.span, + impl_method.body.id, + *trait_method_ty, + &impl_trait_ref.substs); + } + None => { + tcx.sess.span_err( + impl_method.span, + format!("method `{}` is not a member of trait `{}`", + tcx.sess.str_of(impl_method_ty.ident), + pprust::path_to_str(&ast_trait_ref.path, + tcx.sess.intr()))); + } + } + } + + // Check for missing methods from trait + let provided_methods = ty::provided_trait_methods(tcx, + impl_trait_ref.def_id); + let mut missing_methods = ~[]; + for trait_method in trait_methods.iter() { + let is_implemented = + impl_methods.iter().any( + |m| m.ident.name == trait_method.ident.name); + let is_provided = + provided_methods.iter().any( + |m| m.ident.name == trait_method.ident.name); + if !is_implemented && !is_provided { + missing_methods.push( + format!("`{}`", ccx.tcx.sess.str_of(trait_method.ident))); + } + } + + if !missing_methods.is_empty() { + tcx.sess.span_err( + impl_span, + format!("not all trait methods implemented, missing: {}", + missing_methods.connect(", "))); + } +} + +/** + * Checks that a method from an impl/class conforms to the signature of + * the same method as declared in the trait. + * + * # Parameters + * + * - impl_generics: the generics declared on the impl itself (not the method!) + * - impl_m: type of the method we are checking + * - impl_m_span: span to use for reporting errors + * - impl_m_body_id: id of the method body + * - trait_m: the method in the trait + * - trait_substs: the substitutions used on the type of the trait + */ +pub fn compare_impl_method(tcx: ty::ctxt, + impl_generics: &ty::Generics, + impl_m: @ty::Method, + impl_m_span: Span, + impl_m_body_id: ast::NodeId, + trait_m: &ty::Method, + trait_substs: &ty::substs) { + debug!("compare_impl_method()"); + let infcx = infer::new_infer_ctxt(tcx); + + let impl_tps = impl_generics.type_param_defs.len(); + + // Try to give more informative error messages about self typing + // mismatches. Note that any mismatch will also be detected + // below, where we construct a canonical function type that + // includes the self parameter as a normal parameter. It's just + // that the error messages you get out of this code are a bit more + // inscrutable, particularly for cases where one method has no + // self. + match (&trait_m.explicit_self, &impl_m.explicit_self) { + (&ast::sty_static, &ast::sty_static) => {} + (&ast::sty_static, _) => { + tcx.sess.span_err( + impl_m_span, + format!("method `{}` has a `{}` declaration in the impl, \ + but not in the trait", + tcx.sess.str_of(trait_m.ident), + pprust::explicit_self_to_str(&impl_m.explicit_self, + tcx.sess.intr()))); + return; + } + (_, &ast::sty_static) => { + tcx.sess.span_err( + impl_m_span, + format!("method `{}` has a `{}` declaration in the trait, \ + but not in the impl", + tcx.sess.str_of(trait_m.ident), + pprust::explicit_self_to_str(&trait_m.explicit_self, + tcx.sess.intr()))); + return; + } + _ => { + // Let the type checker catch other errors below + } + } + + let num_impl_m_type_params = impl_m.generics.type_param_defs.len(); + let num_trait_m_type_params = trait_m.generics.type_param_defs.len(); + if num_impl_m_type_params != num_trait_m_type_params { + tcx.sess.span_err( + impl_m_span, + format!("method `{}` has {} type parameter(s), but its trait \ + declaration has {} type parameter(s)", + tcx.sess.str_of(trait_m.ident), + num_impl_m_type_params, + num_trait_m_type_params)); + return; + } + + if impl_m.fty.sig.inputs.len() != trait_m.fty.sig.inputs.len() { + tcx.sess.span_err( + impl_m_span, + format!("method `{}` has {} parameter(s) \ + but the trait has {} parameter(s)", + tcx.sess.str_of(trait_m.ident), + impl_m.fty.sig.inputs.len(), + trait_m.fty.sig.inputs.len())); + return; + } + + for (i, trait_param_def) in trait_m.generics.type_param_defs.iter().enumerate() { + // For each of the corresponding impl ty param's bounds... + let impl_param_def = &impl_m.generics.type_param_defs[i]; + + // Check that the impl does not require any builtin-bounds + // that the trait does not guarantee: + let extra_bounds = + impl_param_def.bounds.builtin_bounds - + trait_param_def.bounds.builtin_bounds; + if !extra_bounds.is_empty() { + tcx.sess.span_err( + impl_m_span, + format!("in method `{}`, \ + type parameter {} requires `{}`, \ + which is not required by \ + the corresponding type parameter \ + in the trait declaration", + tcx.sess.str_of(trait_m.ident), + i, + extra_bounds.user_string(tcx))); + return; + } + + // FIXME(#2687)---we should be checking that the bounds of the + // trait imply the bounds of the subtype, but it appears we + // are...not checking this. + if impl_param_def.bounds.trait_bounds.len() != + trait_param_def.bounds.trait_bounds.len() + { + tcx.sess.span_err( + impl_m_span, + format!("in method `{}`, \ + type parameter {} has {} trait bound(s), but the \ + corresponding type parameter in \ + the trait declaration has {} trait bound(s)", + tcx.sess.str_of(trait_m.ident), + i, impl_param_def.bounds.trait_bounds.len(), + trait_param_def.bounds.trait_bounds.len())); + return; + } + } + + // Create a substitution that maps the type parameters on the impl + // to themselves and which replace any references to bound regions + // in the self type with free regions. So, for example, if the + // impl type is "&'self str", then this would replace the self + // type with a free region `self`. + let dummy_impl_tps: ~[ty::t] = + impl_generics.type_param_defs.iter().enumerate(). + map(|(i,t)| ty::mk_param(tcx, i, t.def_id)). + collect(); + let dummy_method_tps: ~[ty::t] = + impl_m.generics.type_param_defs.iter().enumerate(). + map(|(i,t)| ty::mk_param(tcx, i + impl_tps, t.def_id)). + collect(); + let dummy_impl_regions: OptVec = + impl_generics.region_param_defs.iter(). + map(|l| ty::ReFree(ty::FreeRegion { + scope_id: impl_m_body_id, + bound_region: ty::BrNamed(l.def_id, l.ident)})). + collect(); + let dummy_substs = ty::substs { + tps: vec::append(dummy_impl_tps, dummy_method_tps), + regions: ty::NonerasedRegions(dummy_impl_regions), + self_ty: None }; + + // We are going to create a synthetic fn type that includes + // both the method's self argument and its normal arguments. + // So a method like `fn(&self, a: uint)` would be converted + // into a function `fn(self: &T, a: uint)`. + let mut trait_fn_args = ~[]; + let mut impl_fn_args = ~[]; + + // For both the trait and the impl, create an argument to + // represent the self argument (unless this is a static method). + // This argument will have the *transformed* self type. + for &t in trait_m.transformed_self_ty.iter() { + trait_fn_args.push(t); + } + for &t in impl_m.transformed_self_ty.iter() { + impl_fn_args.push(t); + } + + // Add in the normal arguments. + trait_fn_args.push_all(trait_m.fty.sig.inputs); + impl_fn_args.push_all(impl_m.fty.sig.inputs); + + // Create a bare fn type for trait/impl that includes self argument + let trait_fty = + ty::mk_bare_fn(tcx, + ty::BareFnTy { + purity: trait_m.fty.purity, + abis: trait_m.fty.abis, + sig: ty::FnSig { + binder_id: trait_m.fty.sig.binder_id, + inputs: trait_fn_args, + output: trait_m.fty.sig.output, + variadic: false + } + }); + let impl_fty = + ty::mk_bare_fn(tcx, + ty::BareFnTy { + purity: impl_m.fty.purity, + abis: impl_m.fty.abis, + sig: ty::FnSig { + binder_id: impl_m.fty.sig.binder_id, + inputs: impl_fn_args, + output: impl_m.fty.sig.output, + variadic: false + } + }); + + // Perform substitutions so that the trait/impl methods are expressed + // in terms of the same set of type/region parameters: + // - replace trait type parameters with those from `trait_substs`, + // except with any reference to bound self replaced with `dummy_self_r` + // - replace method parameters on the trait with fresh, dummy parameters + // that correspond to the parameters we will find on the impl + // - replace self region with a fresh, dummy region + let impl_fty = { + debug!("impl_fty (pre-subst): {}", ppaux::ty_to_str(tcx, impl_fty)); + impl_fty.subst(tcx, &dummy_substs) + }; + debug!("impl_fty (post-subst): {}", ppaux::ty_to_str(tcx, impl_fty)); + let trait_fty = { + let substs { regions: trait_regions, + tps: trait_tps, + self_ty: self_ty } = trait_substs.subst(tcx, &dummy_substs); + let substs = substs { + regions: trait_regions, + tps: vec::append(trait_tps, dummy_method_tps), + self_ty: self_ty, + }; + debug!("trait_fty (pre-subst): {} substs={}", + trait_fty.repr(tcx), substs.repr(tcx)); + trait_fty.subst(tcx, &substs) + }; + debug!("trait_fty (post-subst): {}", trait_fty.repr(tcx)); + + match infer::mk_subty(infcx, false, infer::MethodCompatCheck(impl_m_span), + impl_fty, trait_fty) { + result::Ok(()) => {} + result::Err(ref terr) => { + tcx.sess.span_err( + impl_m_span, + format!("method `{}` has an incompatible type: {}", + tcx.sess.str_of(trait_m.ident), + ty::type_err_to_str(tcx, terr))); + ty::note_and_explain_type_err(tcx, terr); + } + } +} + impl AstConv for FnCtxt { fn tcx(&self) -> ty::ctxt { self.ccx.tcx } @@ -682,48 +1045,26 @@ impl FnCtxt { pub fn infcx(&self) -> @mut infer::InferCtxt { self.inh.infcx } + pub fn err_count_since_creation(&self) -> uint { self.ccx.tcx.sess.err_count() - self.err_count_on_creation } - pub fn search_in_scope_regions(&self, - span: Span, - br: ty::bound_region) - -> Result { - let in_scope_regions = self.in_scope_regions; - match in_scope_regions.find(br) { - Some(r) => result::Ok(r), - None => { - let blk_br = ty::br_named(special_idents::blk); - if br == blk_br { - result::Ok(self.block_region()) - } else { - result::Err(RegionError { - msg: { - format!("named region `{}` not in scope here", - bound_region_ptr_to_str(self.tcx(), br)) - }, - replacement: { - self.infcx().next_region_var( - infer::BoundRegionError(span)) - } - }) - } - } + + pub fn vtable_context<'a>(&'a self) -> VtableContext<'a> { + VtableContext { + infcx: self.infcx(), + param_env: &self.inh.param_env } } } -impl RegionScope for FnCtxt { - fn anon_region(&self, span: Span) -> Result { - result::Ok(self.infcx().next_region_var(infer::MiscVariable(span))) - } - fn self_region(&self, span: Span) -> Result { - self.search_in_scope_regions(span, ty::br_self) - } - fn named_region(&self, +impl RegionScope for @mut infer::InferCtxt { + fn anon_regions(&self, span: Span, - id: ast::Ident) -> Result { - self.search_in_scope_regions(span, ty::br_named(id)) + count: uint) -> Result<~[ty::Region], ()> { + Ok(vec::from_fn( + count, + |_| self.next_region_var(infer::MiscVariable(span)))) } } @@ -746,7 +1087,7 @@ impl FnCtxt { } pub fn block_region(&self) -> ty::Region { - ty::re_scope(self.region_lb) + ty::ReScope(self.region_lb) } #[inline] @@ -805,7 +1146,7 @@ impl FnCtxt { } pub fn to_ty(&self, ast_t: &ast::Ty) -> ty::t { - ast_ty_to_ty(self, self, ast_t) + ast_ty_to_ty(self, &self.infcx(), ast_t) } pub fn pat_to_str(&self, pat: @ast::Pat) -> ~str { @@ -817,7 +1158,7 @@ impl FnCtxt { Some(&t) => t, None => { self.tcx().sess.bug(format!("no type for expr in fcx {}", - self.tag())); + self.tag())); } } } @@ -828,10 +1169,10 @@ impl FnCtxt { None => { self.tcx().sess.bug( format!("no type for node {}: {} in fcx {}", - id, ast_map::node_id_to_str( - self.tcx().items, id, - token::get_ident_interner()), - self.tag())); + id, ast_map::node_id_to_str( + self.tcx().items, id, + token::get_ident_interner()), + self.tag())); } } } @@ -842,10 +1183,9 @@ impl FnCtxt { None => { self.tcx().sess.bug( format!("no type substs for node {}: {} in fcx {}", - id, ast_map::node_id_to_str( - self.tcx().items, id, - token::get_ident_interner()), - self.tag())); + id, ast_map::node_id_to_str(self.tcx().items, id, + token::get_ident_interner()), + self.tag())); } } } @@ -924,20 +1264,6 @@ impl FnCtxt { v } - pub fn region_var_if_parameterized(&self, - rp: Option, - span: Span) - -> OptVec { - match rp { - None => opt_vec::Empty, - Some(_) => { - opt_vec::with( - self.infcx().next_region_var( - infer::BoundRegionInTypeOrImpl(span))) - } - } - } - pub fn type_error_message(&self, sp: Span, mk_msg: &fn(~str) -> ~str, @@ -1028,10 +1354,10 @@ pub fn check_lit(fcx: @mut FnCtxt, lit: @ast::lit) -> ty::t { let tcx = fcx.ccx.tcx; match lit.node { - ast::lit_str(*) => ty::mk_estr(tcx, ty::vstore_slice(ty::re_static)), + ast::lit_str(*) => ty::mk_estr(tcx, ty::vstore_slice(ty::ReStatic)), ast::lit_binary(*) => { ty::mk_evec(tcx, ty::mt{ ty: ty::mk_u8(), mutbl: ast::MutImmutable }, - ty::vstore_slice(ty::re_static)) + ty::vstore_slice(ty::ReStatic)) } ast::lit_char(_) => ty::mk_char(), ast::lit_int(_, t) => ty::mk_mach_int(t), @@ -1105,20 +1431,22 @@ pub fn impl_self_ty(vcx: &VtableContext, -> ty_param_substs_and_ty { let tcx = vcx.tcx(); - let (n_tps, region_param, raw_ty) = { + let (n_tps, n_rps, raw_ty) = { let ity = ty::lookup_item_type(tcx, did); - (ity.generics.type_param_defs.len(), ity.generics.region_param, ity.ty) + (ity.generics.type_param_defs.len(), + ity.generics.region_param_defs.len(), + ity.ty) }; - let regions = ty::NonerasedRegions(if region_param.is_some() { - opt_vec::with(vcx.infcx.next_region_var( - infer::BoundRegionInTypeOrImpl(location_info.span))) - } else { - opt_vec::Empty - }); + let rps = + vcx.infcx.next_region_vars( + infer::BoundRegionInTypeOrImpl(location_info.span), + n_rps); let tps = vcx.infcx.next_ty_vars(n_tps); - let substs = substs {regions: regions, self_ty: None, tps: tps}; + let substs = substs {regions: ty::NonerasedRegions(opt_vec::from(rps)), + self_ty: None, + tps: tps}; let substd_ty = ty::subst(tcx, &substs, raw_ty); ty_param_substs_and_ty { substs: substs, ty: substd_ty } @@ -1174,22 +1502,21 @@ fn check_type_parameter_positions_in_path(function_context: @mut FnCtxt, // Verify that no lifetimes or type parameters are present anywhere // except the final two elements of the path. for i in range(0, path.segments.len() - 2) { - match path.segments[i].lifetime { - None => {} - Some(lifetime) => { - function_context.tcx() - .sess - .span_err(lifetime.span, - "lifetime parameters may not \ - appear here") - } + for lifetime in path.segments[i].lifetimes.iter() { + function_context.tcx() + .sess + .span_err(lifetime.span, + "lifetime parameters may not \ + appear here"); + break; } for typ in path.segments[i].types.iter() { function_context.tcx() .sess .span_err(typ.span, - "type parameters may not appear here") + "type parameters may not appear here"); + break; } } @@ -1197,7 +1524,7 @@ fn check_type_parameter_positions_in_path(function_context: @mut FnCtxt, // rest of typechecking will (attempt to) infer everything. if path.segments .iter() - .all(|s| s.lifetime.is_none() && s.types.is_empty()) { + .all(|s| s.lifetimes.is_empty() && s.types.is_empty()) { return } @@ -1219,26 +1546,17 @@ fn check_type_parameter_positions_in_path(function_context: @mut FnCtxt, // Make sure lifetime parameterization agrees with the trait or // implementation type. - match (generics.region_param, trait_segment.lifetime) { - (Some(_), None) => { - function_context.tcx() - .sess - .span_err(path.span, - format!("this {} has a lifetime \ - parameter but no \ - lifetime was specified", - name)) - } - (None, Some(_)) => { - function_context.tcx() - .sess - .span_err(path.span, - format!("this {} has no lifetime \ - parameter but a lifetime \ - was specified", - name)) - } - (Some(_), Some(_)) | (None, None) => {} + let trait_region_parameter_count = generics.region_param_defs.len(); + let supplied_region_parameter_count = trait_segment.lifetimes.len(); + if trait_region_parameter_count != supplied_region_parameter_count + && supplied_region_parameter_count != 0 { + function_context.tcx() + .sess + .span_err(path.span, + format!("expected {} lifetime parameter(s), \ + found {} lifetime parameter(s)", + trait_region_parameter_count, + supplied_region_parameter_count)); } // Make sure the number of type parameters supplied on the trait @@ -1276,15 +1594,13 @@ fn check_type_parameter_positions_in_path(function_context: @mut FnCtxt, // Verify that no lifetimes or type parameters are present on // the penultimate segment of the path. let segment = &path.segments[path.segments.len() - 2]; - match segment.lifetime { - None => {} - Some(lifetime) => { - function_context.tcx() - .sess - .span_err(lifetime.span, - "lifetime parameters may not - appear here") - } + for lifetime in segment.lifetimes.iter() { + function_context.tcx() + .sess + .span_err(lifetime.span, + "lifetime parameters may not + appear here"); + break; } for typ in segment.types.iter() { function_context.tcx() @@ -1292,10 +1608,7 @@ fn check_type_parameter_positions_in_path(function_context: @mut FnCtxt, .span_err(typ.span, "type parameters may not appear \ here"); - function_context.tcx() - .sess - .span_note(typ.span, - format!("this is a {:?}", def)); + break; } } } @@ -1556,7 +1869,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // In that case, we check each argument against "error" in order to // set up all the node type bindings. let error_fn_sig = FnSig { - bound_lifetime_names: opt_vec::Empty, + binder_id: ast::CRATE_NODE_ID, inputs: err_args(args.len()), output: ty::mk_err(), variadic: false @@ -1577,7 +1890,6 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // signature with region variables let (_, _, fn_sig) = replace_bound_regions_in_fn_sig(fcx.tcx(), - @Nil, None, fn_sig, |br| fcx.infcx() @@ -1908,10 +2220,10 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, expected: Option) { let tcx = fcx.ccx.tcx; - // Find the expected input/output types (if any). Careful to - // avoid capture of bound regions in the expected type. See - // def'n of br_cap_avoid() for a more lengthy explanation of - // what's going on here. + // Find the expected input/output types (if any). Substitute + // fresh bound regions for any bound regions we find in the + // expected types so as to avoid capture. + // // Also try to pick up inferred purity and sigil, defaulting // to impure and block. Note that we only will use those for // block syntax lambdas; that is, lambdas without explicit @@ -1927,11 +2239,10 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, expected_bounds) = { match expected_sty { Some(ty::ty_closure(ref cenv)) => { - let id = expr.id; let (_, _, sig) = replace_bound_regions_in_fn_sig( - tcx, @Nil, None, &cenv.sig, - |br| ty::re_bound(ty::br_cap_avoid(id, @br))); + tcx, None, &cenv.sig, + |_| fcx.inh.infcx.fresh_bound_region(expr.id)); (Some(sig), cenv.purity, cenv.sigil, cenv.onceness, cenv.bounds) } @@ -1952,7 +2263,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // construct the function type let fn_ty = astconv::ty_of_closure(fcx, - fcx, + &fcx.infcx(), + expr.id, sigil, purity, expected_onceness, @@ -1960,13 +2272,12 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, &None, decl, expected_sig, - &opt_vec::Empty, expr.span); let fty_sig; let fty = if error_happened { fty_sig = FnSig { - bound_lifetime_names: opt_vec::Empty, + binder_id: ast::CRATE_NODE_ID, inputs: fn_ty.sig.inputs.map(|_| ty::mk_err()), output: ty::mk_err(), variadic: false @@ -1989,7 +2300,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, sigil); check_fn(fcx.ccx, None, inherited_purity, &fty_sig, - decl, id, body, fn_kind, fcx.in_scope_regions, fcx.inh); + decl, id, body, fn_kind, fcx.inh); } @@ -2168,50 +2479,18 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // Look up the number of type parameters and the raw type, and // determine whether the class is region-parameterized. - let type_parameter_count; - let region_parameterized; - let raw_type; - if class_id.crate == ast::LOCAL_CRATE { - region_parameterized = - tcx.region_paramd_items.find(&class_id.node). - map(|x| *x); - match tcx.items.find(&class_id.node) { - Some(&ast_map::node_item(@ast::item { - node: ast::item_struct(_, ref generics), - _ - }, _)) => { - - type_parameter_count = generics.ty_params.len(); - - let self_region = - bound_self_region(region_parameterized); - - raw_type = ty::mk_struct(tcx, class_id, substs { - regions: ty::NonerasedRegions(self_region), - self_ty: None, - tps: ty::ty_params_to_tys( - tcx, - generics) - }); - } - _ => { - tcx.sess.span_bug(span, - "resolve didn't map this to a class"); - } - } - } else { - let item_type = ty::lookup_item_type(tcx, class_id); - type_parameter_count = item_type.generics.type_param_defs.len(); - region_parameterized = item_type.generics.region_param; - raw_type = item_type.ty; - } + let item_type = ty::lookup_item_type(tcx, class_id); + let type_parameter_count = item_type.generics.type_param_defs.len(); + let region_parameter_count = item_type.generics.region_param_defs.len(); + let raw_type = item_type.ty; // Generate the struct type. - let regions = - fcx.region_var_if_parameterized(region_parameterized, span); + let regions = fcx.infcx().next_region_vars( + infer::BoundRegionInTypeOrImpl(span), + region_parameter_count); let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); let substitutions = substs { - regions: ty::NonerasedRegions(regions), + regions: ty::NonerasedRegions(opt_vec::from(regions)), self_ty: None, tps: type_parameters }; @@ -2258,48 +2537,18 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // Look up the number of type parameters and the raw type, and // determine whether the enum is region-parameterized. - let type_parameter_count; - let region_parameterized; - let raw_type; - if enum_id.crate == ast::LOCAL_CRATE { - region_parameterized = - tcx.region_paramd_items.find(&enum_id.node).map(|x| *x); - match tcx.items.find(&enum_id.node) { - Some(&ast_map::node_item(@ast::item { - node: ast::item_enum(_, ref generics), - _ - }, _)) => { - - type_parameter_count = generics.ty_params.len(); - - let regions = bound_self_region(region_parameterized); - - raw_type = ty::mk_enum(tcx, enum_id, substs { - regions: ty::NonerasedRegions(regions), - self_ty: None, - tps: ty::ty_params_to_tys( - tcx, - generics) - }); - } - _ => { - tcx.sess.span_bug(span, - "resolve didn't map this to an enum"); - } - } - } else { - let item_type = ty::lookup_item_type(tcx, enum_id); - type_parameter_count = item_type.generics.type_param_defs.len(); - region_parameterized = item_type.generics.region_param; - raw_type = item_type.ty; - } + let item_type = ty::lookup_item_type(tcx, enum_id); + let type_parameter_count = item_type.generics.type_param_defs.len(); + let region_parameter_count = item_type.generics.region_param_defs.len(); + let raw_type = item_type.ty; // Generate the enum type. - let regions = - fcx.region_var_if_parameterized(region_parameterized, span); + let regions = fcx.infcx().next_region_vars( + infer::BoundRegionInTypeOrImpl(span), + region_parameter_count); let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); let substitutions = substs { - regions: ty::NonerasedRegions(regions), + regions: ty::NonerasedRegions(opt_vec::from(regions)), self_ty: None, tps: type_parameters }; @@ -3445,28 +3694,25 @@ pub fn instantiate_path(fcx: @mut FnCtxt, ty_param_count, ty_substs_len); - // determine the region bound, using the value given by the user + // determine the region parameters, using the value given by the user // (if any) and otherwise using a fresh region variable - let regions = match pth.segments.last().lifetime { - Some(_) => { // user supplied a lifetime parameter... - match tpt.generics.region_param { - None => { // ...but the type is not lifetime parameterized! - fcx.ccx.tcx.sess.span_err - (span, "this item is not region-parameterized"); - opt_vec::Empty - } - Some(_) => { // ...and the type is lifetime parameterized, ok. - opt_vec::with( - ast_region_to_region(fcx, - fcx, - span, - &pth.segments.last().lifetime)) - } - } - } - None => { // no lifetime parameter supplied, insert default - fcx.region_var_if_parameterized(tpt.generics.region_param, span) + let num_expected_regions = tpt.generics.region_param_defs.len(); + let num_supplied_regions = pth.segments.last().lifetimes.len(); + let regions = if num_expected_regions == num_supplied_regions { + pth.segments.last().lifetimes.map( + |l| ast_region_to_region(fcx.tcx(), l)) + } else { + if num_supplied_regions != 0 { + fcx.ccx.tcx.sess.span_err( + span, + format!("expected {} lifetime parameter(s), \ + found {} lifetime parameter(s)", + num_expected_regions, num_supplied_regions)); } + + opt_vec::from(fcx.infcx().next_region_vars( + infer::BoundRegionInTypeOrImpl(span), + num_expected_regions)) }; // Special case: If there is a self parameter, omit it from the list of @@ -3642,18 +3888,14 @@ pub fn check_bounds_are_used(ccx: @mut CrateCtxt, if tps.len() == 0u { return; } let mut tps_used = vec::from_elem(tps.len(), false); - ty::walk_regions_and_ty( - ccx.tcx, ty, - |_r| {}, - |t| { + ty::walk_ty(ty, |t| { match ty::get(t).sty { - ty::ty_param(param_ty {idx, _}) => { - debug!("Found use of ty param \\#{}", idx); - tps_used[idx] = true; - } - _ => () + ty::ty_param(param_ty {idx, _}) => { + debug!("Found use of ty param \\#{}", idx); + tps_used[idx] = true; + } + _ => () } - true }); for (i, b) in tps_used.iter().enumerate() { @@ -3680,19 +3922,19 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { //We only care about the operation here match split[1] { "cxchg" => (0, ~[ty::mk_mut_rptr(tcx, - ty::re_bound(ty::br_anon(0)), + ty::ReLateBound(it.id, ty::BrAnon(0)), ty::mk_int()), ty::mk_int(), ty::mk_int() ], ty::mk_int()), "load" => (0, ~[ - ty::mk_imm_rptr(tcx, ty::re_bound(ty::br_anon(0)), ty::mk_int()) + ty::mk_imm_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), ty::mk_int()) ], ty::mk_int()), "store" => (0, ~[ - ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), ty::mk_int()), + ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), ty::mk_int()), ty::mk_int() ], ty::mk_nil()), @@ -3700,7 +3942,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" | "umin" => { (0, ~[ty::mk_mut_rptr(tcx, - ty::re_bound(ty::br_anon(0)), + ty::ReLateBound(it.id, ty::BrAnon(0)), ty::mk_int()), ty::mk_int() ], ty::mk_int()) } "fence" => { @@ -3726,7 +3968,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { "move_val" | "move_val_init" => { (1u, ~[ - ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), param(ccx, 0)), + ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)), param(ccx, 0u) ], ty::mk_nil()) @@ -3738,7 +3980,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { "atomic_xchg_rel" | "atomic_xadd_rel" | "atomic_xsub_rel" => { (0, ~[ - ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), ty::mk_int()), + ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), ty::mk_int()), ty::mk_int() ], ty::mk_int()) @@ -3761,7 +4003,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { Ok(t) => t, Err(s) => { tcx.sess.span_fatal(it.span, s); } }; - let region = ty::re_bound(ty::br_anon(0)); + let region = ty::ReLateBound(it.id, ty::BrAnon(0)); let visitor_object_ty = match ty::visitor_object_ty(tcx, region) { Ok((_, vot)) => vot, Err(s) => { tcx.sess.span_fatal(it.span, s); } @@ -3953,12 +4195,10 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { purity: ast::unsafe_fn, abis: AbiSet::Intrinsic(), - sig: FnSig { - bound_lifetime_names: opt_vec::Empty, - inputs: inputs, - output: output, - variadic: false - } + sig: FnSig {binder_id: it.id, + inputs: inputs, + output: output, + variadic: false} }); let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id)); let i_n_tps = i_ty.generics.type_param_defs.len(); @@ -3974,3 +4214,4 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { ppaux::ty_to_str(ccx.tcx, fty))); } } + diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index c0aa669d920ab..b21d36777c293 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -29,7 +29,7 @@ this point a bit better. use middle::freevars::get_freevars; -use middle::ty::{re_scope}; +use middle::ty::{ReScope}; use middle::ty; use middle::typeck::check::FnCtxt; use middle::typeck::check::regionmanip::relate_nested_regions; @@ -64,7 +64,7 @@ fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::Def) -> ty::Region { DefUpvar(_, subdef, closure_id, body_id) => { match ty::ty_closure_sigil(fcx.node_ty(closure_id)) { BorrowedSigil => encl_region_of_def(fcx, *subdef), - ManagedSigil | OwnedSigil => re_scope(body_id) + ManagedSigil | OwnedSigil => ReScope(body_id) } } _ => { @@ -317,7 +317,7 @@ fn visit_expr(rcx: &mut Rcx, expr: @ast::Expr) { // // FIXME(#6268) remove to support nested method calls constrain_regions_in_type_of_node( - rcx, expr.id, ty::re_scope(expr.id), + rcx, expr.id, ty::ReScope(expr.id), infer::AutoBorrow(expr.span)); } } @@ -416,7 +416,7 @@ fn visit_expr(rcx: &mut Rcx, expr: @ast::Expr) { // // FIXME(#6268) nested method calls requires that this rule change let ty0 = rcx.resolve_node_type(expr.id); - constrain_regions_in_type(rcx, ty::re_scope(expr.id), + constrain_regions_in_type(rcx, ty::ReScope(expr.id), infer::AddrOf(expr.span), ty0); visit::walk_expr(rcx, expr, ()); } @@ -474,7 +474,7 @@ fn check_expr_fn_block(rcx: &mut Rcx, // (since otherwise that would require // infinite stack). constrain_free_variables(rcx, region, expr); - let repeating_scope = ty::re_scope(rcx.repeating_scope); + let repeating_scope = ty::ReScope(rcx.repeating_scope); rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), region, repeating_scope); } @@ -500,7 +500,7 @@ fn constrain_callee(rcx: &mut Rcx, call_expr: @ast::Expr, callee_expr: @ast::Expr) { - let call_region = ty::re_scope(call_expr.id); + let call_region = ty::ReScope(call_expr.id); let callee_ty = rcx.resolve_node_type(callee_id); match ty::get(callee_ty).sty { @@ -535,8 +535,14 @@ fn constrain_call(rcx: &mut Rcx, //! appear in the arguments appropriately. let tcx = rcx.fcx.tcx(); - debug!("constrain_call(call_expr={}, implicitly_ref_args={:?})", - call_expr.repr(tcx), implicitly_ref_args); + debug!("constrain_call(call_expr={}, \ + receiver={}, \ + arg_exprs={}, \ + implicitly_ref_args={:?})", + call_expr.repr(tcx), + receiver.repr(tcx), + arg_exprs.repr(tcx), + implicitly_ref_args); let callee_ty = rcx.resolve_node_type(callee_id); if ty::type_is_error(callee_ty) { // Bail, as function type is unknown @@ -549,9 +555,11 @@ fn constrain_call(rcx: &mut Rcx, // // FIXME(#6268) to support nested method calls, should be callee_id let callee_scope = call_expr.id; - let callee_region = ty::re_scope(callee_scope); + let callee_region = ty::ReScope(callee_scope); for &arg_expr in arg_exprs.iter() { + debug!("Argument"); + // ensure that any regions appearing in the argument type are // valid for at least the lifetime of the function: constrain_regions_in_type_of_node( @@ -569,6 +577,7 @@ fn constrain_call(rcx: &mut Rcx, // as loop above, but for receiver for &r in receiver.iter() { + debug!("Receiver"); constrain_regions_in_type_of_node( rcx, r.id, callee_region, infer::CallRcvr(r.span)); if implicitly_ref_args { @@ -595,7 +604,7 @@ fn constrain_derefs(rcx: &mut Rcx, * the deref expr. */ let tcx = rcx.fcx.tcx(); - let r_deref_expr = ty::re_scope(deref_expr.id); + let r_deref_expr = ty::ReScope(deref_expr.id); for i in range(0u, derefs) { debug!("constrain_derefs(deref_expr=?, derefd_ty={}, derefs={:?}/{:?}", rcx.fcx.infcx().ty_to_str(derefd_ty), @@ -641,7 +650,7 @@ fn constrain_index(rcx: &mut Rcx, debug!("constrain_index(index_expr=?, indexed_ty={}", rcx.fcx.infcx().ty_to_str(indexed_ty)); - let r_index_expr = ty::re_scope(index_expr.id); + let r_index_expr = ty::ReScope(index_expr.id); match ty::get(indexed_ty).sty { ty::ty_estr(ty::vstore_slice(r_ptr)) | ty::ty_evec(_, ty::vstore_slice(r_ptr)) => { @@ -727,9 +736,9 @@ fn constrain_regions_in_type( ty_to_str(tcx, ty)); do relate_nested_regions(tcx, Some(minimum_lifetime), ty) |r_sub, r_sup| { - debug!("relate(r_sub={}, r_sup={})", - region_to_str(tcx, "", false, r_sub), - region_to_str(tcx, "", false, r_sup)); + debug!("relate_nested_regions(r_sub={}, r_sup={})", + r_sub.repr(tcx), + r_sup.repr(tcx)); if r_sup.is_bound() || r_sub.is_bound() { // a bound region is one which appears inside an fn type. @@ -903,7 +912,7 @@ pub mod guarantor { let expr_cat = categorize(rcx, expr); debug!("guarantor::for_by_ref(expr={:?}, callee_scope={:?}) category={:?}", expr.id, callee_scope, expr_cat); - let minimum_lifetime = ty::re_scope(callee_scope); + let minimum_lifetime = ty::ReScope(callee_scope); for guarantor in expr_cat.guarantor.iter() { mk_subregion_due_to_derefence(rcx, expr.span, minimum_lifetime, *guarantor); diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 9ba709f765106..ec11adbfa3dab 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -10,155 +10,41 @@ // #[warn(deprecated_mode)]; - use middle::ty; - -use middle::typeck::isr_alist; -use util::common::indenter; -use util::ppaux::region_to_str; +use middle::ty_fold; +use middle::ty_fold::TypeFolder; +use std::hashmap::HashMap; +use util::ppaux::Repr; use util::ppaux; -use extra::list::Cons; - // Helper functions related to manipulating region types. pub fn replace_bound_regions_in_fn_sig( tcx: ty::ctxt, - isr: isr_alist, opt_self_ty: Option, fn_sig: &ty::FnSig, - mapf: &fn(ty::bound_region) -> ty::Region) - -> (isr_alist, Option, ty::FnSig) + mapf: &fn(ty::BoundRegion) -> ty::Region) + -> (HashMap, Option, ty::FnSig) { - let mut all_tys = ty::tys_in_fn_sig(fn_sig); - - for &t in opt_self_ty.iter() { all_tys.push(t) } - - debug!("replace_bound_regions_in_fn_sig(self_ty={:?}, fn_sig={}, \ - all_tys={:?})", - opt_self_ty.map(|t| ppaux::ty_to_str(tcx, t)), - ppaux::fn_sig_to_str(tcx, fn_sig), - all_tys.map(|t| ppaux::ty_to_str(tcx, *t))); - let _i = indenter(); - - let isr = do create_bound_region_mapping(tcx, isr, all_tys) |br| { - debug!("br={:?}", br); - mapf(br) - }; - let new_fn_sig = ty::fold_sig(fn_sig, |t| { - replace_bound_regions(tcx, isr, t) - }); - let new_self_ty = opt_self_ty.map(|t| replace_bound_regions(tcx, isr, t)); - - debug!("result of replace_bound_regions_in_fn_sig: \ - new_self_ty={:?}, \ - fn_sig={}", - new_self_ty.map(|t| ppaux::ty_to_str(tcx, t)), - ppaux::fn_sig_to_str(tcx, &new_fn_sig)); - - return (isr, new_self_ty, new_fn_sig); - - // Takes `isr`, a (possibly empty) mapping from in-scope region - // names ("isr"s) to their corresponding regions; `tys`, a list of - // types, and `to_r`, a closure that takes a bound_region and - // returns a region. Returns an updated version of `isr`, - // extended with the in-scope region names from all of the bound - // regions appearing in the types in the `tys` list (if they're - // not in `isr` already), with each of those in-scope region names - // mapped to a region that's the result of applying `to_r` to - // itself. - fn create_bound_region_mapping( - tcx: ty::ctxt, - isr: isr_alist, - tys: ~[ty::t], - to_r: &fn(ty::bound_region) -> ty::Region) -> isr_alist { - - // Takes `isr` (described above), `to_r` (described above), - // and `r`, a region. If `r` is anything other than a bound - // region, or if it's a bound region that already appears in - // `isr`, then we return `isr` unchanged. If `r` is a bound - // region that doesn't already appear in `isr`, we return an - // updated isr_alist that now contains a mapping from `r` to - // the result of calling `to_r` on it. - fn append_isr(isr: isr_alist, - to_r: &fn(ty::bound_region) -> ty::Region, - r: ty::Region) -> isr_alist { - match r { - ty::re_empty | ty::re_free(*) | ty::re_static | ty::re_scope(_) | - ty::re_infer(_) => { - isr - } - ty::re_bound(br) => { - match isr.find(br) { - Some(_) => isr, - None => @Cons((br, to_r(br)), isr) + debug!("replace_bound_regions_in_fn_sig(self_ty={}, fn_sig={})", + opt_self_ty.repr(tcx), + fn_sig.repr(tcx)); + + let mut map = HashMap::new(); + let (fn_sig, opt_self_ty) = { + let mut f = ty_fold::RegionFolder::regions(tcx, |r| { + debug!("region r={}", r.to_str()); + match r { + ty::ReLateBound(s, br) if s == fn_sig.binder_id => { + *map.find_or_insert_with(br, |_| mapf(br)) } - } - } - } - - // For each type `ty` in `tys`... - do tys.iter().fold(isr) |isr, ty| { - let mut isr = isr; - - // Using fold_regions is inefficient, because it - // constructs new types, but it avoids code duplication in - // terms of locating all the regions within the various - // kinds of types. This had already caused me several - // bugs so I decided to switch over. - do ty::fold_regions(tcx, *ty) |r, in_fn| { - if !in_fn { isr = append_isr(isr, |br| to_r(br), r); } - r - }; - - isr - } - } - - // Takes `isr`, a mapping from in-scope region names ("isr"s) to - // their corresponding regions; and `ty`, a type. Returns an - // updated version of `ty`, in which bound regions in `ty` have - // been replaced with the corresponding bindings in `isr`. - fn replace_bound_regions( - tcx: ty::ctxt, - isr: isr_alist, - ty: ty::t) -> ty::t { - - do ty::fold_regions(tcx, ty) |r, in_fn| { - let r1 = match r { - // As long as we are not within a fn() type, `&T` is - // mapped to the free region anon_r. But within a fn - // type, it remains bound. - ty::re_bound(ty::br_anon(_)) if in_fn => r, - - ty::re_bound(br) => { - match isr.find(br) { - // In most cases, all named, bound regions will be - // mapped to some free region. - Some(fr) => fr, - - // But in the case of a fn() type, there may be - // named regions within that remain bound: - None if in_fn => r, - None => { - tcx.sess.bug( - format!("Bound region not found in \ - in_scope_regions list: {}", - region_to_str(tcx, "", false, r))); - } - } - } - - // Free regions like these just stay the same: - ty::re_empty | - ty::re_static | - ty::re_scope(_) | - ty::re_free(*) | - ty::re_infer(_) => r - }; - r1 - } - } + _ => r + }}); + (ty_fold::super_fold_sig(&mut f, fn_sig), + ty_fold::fold_opt_ty(&mut f, opt_self_ty)) + }; + debug!("resulting map: {}", map.to_str()); + (map, opt_self_ty, fn_sig) } pub fn relate_nested_regions( @@ -168,7 +54,6 @@ pub fn relate_nested_regions( relate_op: &fn(ty::Region, ty::Region)) { /*! - * * This rather specialized function walks each region `r` that appear * in `ty` and invokes `relate_op(r_encl, r)` for each one. `r_encl` * here is the region of any enclosing `&'r T` pointer. If there is @@ -194,41 +79,60 @@ pub fn relate_nested_regions( * Hence, in the second example above, `'r2` must be a subregion of `'r3`. */ - let mut the_stack = ~[]; - for &r in opt_region.iter() { the_stack.push(r); } - walk_ty(tcx, &mut the_stack, ty, relate_op); + let mut rr = RegionRelator { tcx: tcx, + stack: ~[], + relate_op: relate_op }; + match opt_region { + Some(o_r) => { rr.stack.push(o_r); } + None => {} + } + rr.fold_ty(ty); - fn walk_ty(tcx: ty::ctxt, - the_stack: &mut ~[ty::Region], - ty: ty::t, - relate_op: &fn(ty::Region, ty::Region)) - { - match ty::get(ty).sty { - ty::ty_rptr(r, ref mt) | - ty::ty_evec(ref mt, ty::vstore_slice(r)) => { - relate(*the_stack, r, |x,y| relate_op(x,y)); - the_stack.push(r); - walk_ty(tcx, the_stack, mt.ty, |x,y| relate_op(x,y)); - the_stack.pop(); - } - _ => { - ty::fold_regions_and_ty( - tcx, - ty, - |r| { relate( *the_stack, r, |x,y| relate_op(x,y)); r }, - |t| { walk_ty(tcx, the_stack, t, |x,y| relate_op(x,y)); t }, - |t| { walk_ty(tcx, the_stack, t, |x,y| relate_op(x,y)); t }); + struct RegionRelator<'self> { + tcx: ty::ctxt, + stack: ~[ty::Region], + relate_op: &'self fn(ty::Region, ty::Region), + } + + // FIXME(#10151) -- Define more precisely when a region is + // considered "nested". Consider taking variance into account as + // well. + + impl<'self> TypeFolder for RegionRelator<'self> { + fn tcx(&self) -> ty::ctxt { + self.tcx + } + + fn fold_ty(&mut self, ty: ty::t) -> ty::t { + match ty::get(ty).sty { + ty::ty_rptr(r, ref mt) | + ty::ty_evec(ref mt, ty::vstore_slice(r)) => { + self.relate(r); + self.stack.push(r); + ty_fold::super_fold_ty(self, mt.ty); + self.stack.pop(); + } + + _ => { + ty_fold::super_fold_ty(self, ty); + } } + + ty + } + + fn fold_region(&mut self, r: ty::Region) -> ty::Region { + self.relate(r); + r } } - fn relate(the_stack: &[ty::Region], - r_sub: ty::Region, - relate_op: &fn(ty::Region, ty::Region)) - { - for &r in the_stack.iter() { - if !r.is_bound() && !r_sub.is_bound() { - relate_op(r, r_sub); + impl<'self> RegionRelator<'self> { + fn relate(&mut self, r_sub: ty::Region) { + for &r in self.stack.iter() { + if !r.is_bound() && !r_sub.is_bound() { + (self.relate_op)(r, r_sub); + } } } } @@ -265,7 +169,7 @@ pub fn relate_free_regions( debug!("relate_free_regions(t={})", ppaux::ty_to_str(tcx, t)); relate_nested_regions(tcx, None, t, |a, b| { match (&a, &b) { - (&ty::re_free(free_a), &ty::re_free(free_b)) => { + (&ty::ReFree(free_a), &ty::ReFree(free_b)) => { tcx.region_maps.relate_free_regions(free_a, free_b); } _ => {} diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 3c4ff35b768db..996157df2e299 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -11,6 +11,7 @@ use middle::ty::param_ty; use middle::ty; +use middle::ty_fold::TypeFolder; use middle::typeck::check::{FnCtxt, impl_self_ty}; use middle::typeck::check::{structurally_resolved_type}; use middle::typeck::infer::fixup_err_to_str; @@ -68,13 +69,13 @@ pub struct LocationInfo { /// A vtable context includes an inference context, a crate context, and a /// callback function to call in case of type error. -pub struct VtableContext { - ccx: @mut CrateCtxt, - infcx: @mut infer::InferCtxt +pub struct VtableContext<'self> { + infcx: @mut infer::InferCtxt, + param_env: &'self ty::ParameterEnvironment, } -impl VtableContext { - pub fn tcx(&self) -> ty::ctxt { self.ccx.tcx } +impl<'self> VtableContext<'self> { + pub fn tcx(&self) -> ty::ctxt { self.infcx.tcx } } fn has_trait_bounds(type_param_defs: &[ty::TypeParameterDef]) -> bool { @@ -95,7 +96,6 @@ fn lookup_vtables(vcx: &VtableContext, substs.repr(vcx.tcx())); let _i = indenter(); - // We do this backwards for reasons discussed above. assert_eq!(substs.tps.len(), type_param_defs.len()); let mut result = @@ -233,8 +233,6 @@ fn lookup_vtable(vcx: &VtableContext, vcx.infcx.trait_ref_to_str(trait_ref)); let _i = indenter(); - let tcx = vcx.tcx(); - let ty = match fixup_ty(vcx, location_info, ty, is_early) { Some(ty) => ty, None => { @@ -250,18 +248,21 @@ fn lookup_vtable(vcx: &VtableContext, // If the type is self or a param, we look at the trait/supertrait // bounds to see if they include the trait we are looking for. let vtable_opt = match ty::get(ty).sty { - ty::ty_param(param_ty {idx: n, def_id: did}) => { - let type_param_def = tcx.ty_param_defs.get(&did.node); - lookup_vtable_from_bounds(vcx, location_info, - type_param_def.bounds.trait_bounds, + ty::ty_param(param_ty {idx: n, _}) => { + let type_param_bounds: &[@ty::TraitRef] = + vcx.param_env.type_param_bounds[n].trait_bounds; + lookup_vtable_from_bounds(vcx, + location_info, + type_param_bounds, param_numbered(n), trait_ref) } - ty::ty_self(trait_id) => { - let self_trait_ref = ty::lookup_trait_def(tcx, trait_id).trait_ref; - lookup_vtable_from_bounds(vcx, location_info, - &[self_trait_ref], + ty::ty_self(_) => { + let self_param_bound = vcx.param_env.self_param_bound.unwrap(); + lookup_vtable_from_bounds(vcx, + location_info, + [self_param_bound], param_self, trait_ref) } @@ -285,7 +286,7 @@ fn lookup_vtable_from_bounds(vcx: &VtableContext, bounds: &[@ty::TraitRef], param: param_index, trait_ref: @ty::TraitRef) - -> Option { + -> Option { let tcx = vcx.tcx(); let mut n_bound = 0; @@ -317,8 +318,7 @@ fn search_for_vtable(vcx: &VtableContext, ty: ty::t, trait_ref: @ty::TraitRef, is_early: bool) - -> Option -{ + -> Option { let tcx = vcx.tcx(); let mut found = ~[]; @@ -480,7 +480,7 @@ fn fixup_substs(vcx: &VtableContext, // use a dummy type just to package up the substs that need fixing up let t = ty::mk_trait(tcx, id, substs, - ty::RegionTraitStore(ty::re_static), + ty::RegionTraitStore(ty::ReStatic), ast::MutImmutable, ty::EmptyBuiltinBounds()); do fixup_ty(vcx, location_info, t, is_early).map |t_f| { @@ -494,7 +494,8 @@ fn fixup_substs(vcx: &VtableContext, fn fixup_ty(vcx: &VtableContext, location_info: &LocationInfo, ty: ty::t, - is_early: bool) -> Option { + is_early: bool) + -> Option { let tcx = vcx.tcx(); match resolve_type(vcx.infcx, ty, resolve_and_force_all_but_regions) { Ok(new_type) => Some(new_type), @@ -515,8 +516,7 @@ fn connect_trait_tps(vcx: &VtableContext, location_info: &LocationInfo, impl_substs: &ty::substs, trait_ref: @ty::TraitRef, - impl_did: ast::DefId) -{ + impl_did: ast::DefId) { let tcx = vcx.tcx(); let impl_trait_ref = match ty::impl_trait_ref(tcx, impl_did) { @@ -571,7 +571,7 @@ pub fn early_resolve_expr(ex: @ast::Expr, if has_trait_bounds(*item_ty.generics.type_param_defs) { debug!("early_resolve_expr: looking up vtables for type params {}", item_ty.generics.type_param_defs.repr(fcx.tcx())); - let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() }; + let vcx = fcx.vtable_context(); let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), *item_ty.generics.type_param_defs, substs, is_early); @@ -599,7 +599,7 @@ pub fn early_resolve_expr(ex: @ast::Expr, ex.repr(fcx.tcx())); if has_trait_bounds(*type_param_defs) { let substs = fcx.node_ty_substs(callee_id); - let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() }; + let vcx = fcx.vtable_context(); let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), *type_param_defs, &substs, is_early); if !is_early { @@ -642,10 +642,7 @@ pub fn early_resolve_expr(ex: @ast::Expr, (&ty::ty_rptr(_, mt), ty::RegionTraitStore(*)) => { let location_info = &location_info_for_expr(ex); - let vcx = VtableContext { - ccx: fcx.ccx, - infcx: fcx.infcx() - }; + let vcx = fcx.vtable_context(); let target_trait_ref = @ty::TraitRef { def_id: target_def_id, substs: ty::substs { @@ -726,48 +723,58 @@ fn resolve_expr(fcx: @mut FnCtxt, visit::walk_expr(&mut fcx, ex, ()); } -pub fn resolve_impl(ccx: @mut CrateCtxt, impl_item: @ast::item) { - let def_id = ast_util::local_def(impl_item.id); - match ty::impl_trait_ref(ccx.tcx, def_id) { - None => {}, - Some(trait_ref) => { - let infcx = infer::new_infer_ctxt(ccx.tcx); - let vcx = VtableContext { ccx: ccx, infcx: infcx }; - let loc_info = location_info_for_item(impl_item); - - // First, check that the impl implements any trait bounds - // on the trait. - let trait_def = ty::lookup_trait_def(ccx.tcx, trait_ref.def_id); - let vtbls = lookup_vtables(&vcx, - &loc_info, - *trait_def.generics.type_param_defs, - &trait_ref.substs, - false); - - // Now, locate the vtable for the impl itself. The real - // purpose of this is to check for supertrait impls, - // but that falls out of doing this. - let param_bounds = ty::ParamBounds { - builtin_bounds: ty::EmptyBuiltinBounds(), - trait_bounds: ~[trait_ref] - }; - let t = ty::node_id_to_type(ccx.tcx, impl_item.id); - debug!("=== Doing a self lookup now."); - // Right now, we don't have any place to store this. - // We will need to make one so we can use this information - // for compiling default methods that refer to supertraits. - let self_vtable_res = - lookup_vtables_for_param(&vcx, &loc_info, None, - ¶m_bounds, t, false); - - - let res = impl_res { - trait_vtables: vtbls, - self_vtables: self_vtable_res - }; - ccx.tcx.impl_vtables.insert(def_id, res); - } - } +pub fn resolve_impl(ccx: @mut CrateCtxt, + impl_item: @ast::item, + impl_generics: &ty::Generics, + impl_trait_ref: &ty::TraitRef) { + let param_env = ty::construct_parameter_environment( + ccx.tcx, + None, + *impl_generics.type_param_defs, + [], + impl_generics.region_param_defs, + impl_item.id); + + let impl_trait_ref = @impl_trait_ref.subst(ccx.tcx, ¶m_env.free_substs); + + let infcx = infer::new_infer_ctxt(ccx.tcx); + let vcx = VtableContext { infcx: infcx, param_env: ¶m_env }; + let loc_info = location_info_for_item(impl_item); + + // First, check that the impl implements any trait bounds + // on the trait. + let trait_def = ty::lookup_trait_def(ccx.tcx, impl_trait_ref.def_id); + let vtbls = lookup_vtables(&vcx, + &loc_info, + *trait_def.generics.type_param_defs, + &impl_trait_ref.substs, + false); + + // Now, locate the vtable for the impl itself. The real + // purpose of this is to check for supertrait impls, + // but that falls out of doing this. + let param_bounds = ty::ParamBounds { + builtin_bounds: ty::EmptyBuiltinBounds(), + trait_bounds: ~[impl_trait_ref] + }; + let t = ty::node_id_to_type(ccx.tcx, impl_item.id); + let t = t.subst(ccx.tcx, ¶m_env.free_substs); + debug!("=== Doing a self lookup now."); + + // Right now, we don't have any place to store this. + // We will need to make one so we can use this information + // for compiling default methods that refer to supertraits. + let self_vtable_res = + lookup_vtables_for_param(&vcx, &loc_info, None, + ¶m_bounds, t, false); + + + let res = impl_res { + trait_vtables: vtbls, + self_vtables: self_vtable_res + }; + let impl_def_id = ast_util::local_def(impl_item.id); + ccx.tcx.impl_vtables.insert(impl_def_id, res); } impl visit::Visitor<()> for @mut FnCtxt { diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 795074fa61904..bf00bee270943 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -357,8 +357,8 @@ impl CoherenceChecker { @vec::append( (*impl_poly_type.generics.type_param_defs).clone(), *new_method_ty.generics.type_param_defs), - region_param: - impl_poly_type.generics.region_param + region_param_defs: + impl_poly_type.generics.region_param_defs }; let new_polytype = ty::ty_param_bounds_and_ty { generics: new_generics, @@ -482,20 +482,17 @@ impl CoherenceChecker { pub fn universally_quantify_polytype(&self, polytype: ty_param_bounds_and_ty) -> UniversalQuantificationResult { - let regions = match polytype.generics.region_param { - None => opt_vec::Empty, - Some(_) => { - opt_vec::with( - self.inference_context.next_region_var( - infer::BoundRegionInCoherence)) - } - }; + let region_parameter_count = polytype.generics.region_param_defs.len(); + let region_parameters = + self.inference_context.next_region_vars( + infer::BoundRegionInCoherence, + region_parameter_count); let bounds_count = polytype.generics.type_param_defs.len(); let type_parameters = self.inference_context.next_ty_vars(bounds_count); let substitutions = substs { - regions: ty::NonerasedRegions(regions), + regions: ty::NonerasedRegions(opt_vec::from(region_parameters)), self_ty: None, tps: type_parameters }; @@ -539,33 +536,6 @@ impl CoherenceChecker { return trait_id; } - // This check doesn't really have anything to do with coherence. It's - // here for historical reasons - pub fn check_trait_methods_are_implemented( - &self, - all_methods: &mut ~[@Method], - trait_did: DefId, - trait_ref_span: Span) { - - let tcx = self.crate_context.tcx; - - let mut provided_names = HashSet::new(); - // Implemented methods - for elt in all_methods.iter() { - provided_names.insert(elt.ident.name); - } - - let r = ty::trait_methods(tcx, trait_did); - for method in r.iter() { - debug!("checking for {}", method.ident.repr(tcx)); - if provided_names.contains(&method.ident.name) { continue; } - - tcx.sess.span_err(trait_ref_span, - format!("missing method `{}`", - tcx.sess.str_of(method.ident))); - } - } - /// For coherence, when we have `impl Type`, we need to guarantee that /// `Type` is "local" to the crate. For our purposes, this means that it /// must precisely name some nominal type defined in this crate. @@ -620,17 +590,10 @@ impl CoherenceChecker { let ty_trait_ref = ty::node_id_to_trait_ref( self.crate_context.tcx, trait_ref.ref_id); - let trait_did = ty_trait_ref.def_id; self.instantiate_default_methods(local_def(item.id), ty_trait_ref, &mut methods); - - // Check that we have implementations of every trait method - self.check_trait_methods_are_implemented( - &mut methods, - trait_did, - trait_ref.path.span); } return @Impl { diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 353639051097a..36ed9f94fb71a 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -39,15 +39,10 @@ use middle::subst::Subst; use middle::typeck::astconv::{AstConv, ty_of_arg}; use middle::typeck::astconv::{ast_ty_to_ty}; use middle::typeck::astconv; -use middle::typeck::infer; use middle::typeck::rscope::*; -use middle::typeck::rscope; use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; -use util::common::pluralize; use util::ppaux; -use util::ppaux::UserString; -use std::result; use std::vec; use syntax::abi::AbiSet; use syntax::ast::{RegionTyParamBound, TraitTyParamBound}; @@ -56,10 +51,9 @@ use syntax::ast_map; use syntax::ast_util::{local_def, split_trait_methods}; use syntax::codemap::Span; use syntax::codemap; -use syntax::print::pprust::{path_to_str, explicit_self_to_str}; +use syntax::print::pprust::{path_to_str}; use syntax::visit; use syntax::opt_vec::OptVec; -use syntax::opt_vec; use syntax::parse::token::special_idents; struct CollectItemTypesVisitor { @@ -97,19 +91,11 @@ pub fn collect_item_types(ccx: @mut CrateCtxt, crate: &ast::Crate) { } pub trait ToTy { - fn to_ty( - &self, - rs: &RS, - ast_ty: &ast::Ty) - -> ty::t; + fn to_ty(&self, rs: &RS, ast_ty: &ast::Ty) -> ty::t; } impl ToTy for CrateCtxt { - fn to_ty( - &self, - rs: &RS, - ast_ty: &ast::Ty) - -> ty::t { + fn to_ty(&self, rs: &RS, ast_ty: &ast::Ty) -> ty::t { ast_ty_to_ty(self, rs, ast_ty) } } @@ -149,59 +135,45 @@ impl AstConv for CrateCtxt { pub fn get_enum_variant_types(ccx: &CrateCtxt, enum_ty: ty::t, variants: &[ast::variant], - generics: &ast::Generics, - rp: Option) { + generics: &ast::Generics) { let tcx = ccx.tcx; // Create a set of parameter types shared among all the variants. for variant in variants.iter() { - let region_parameterization = - RegionParameterization::from_variance_and_generics(rp, generics); - // Nullary enum constructors get turned into constants; n-ary enum // constructors get turned into functions. - let result_ty; - match variant.node.kind { + let scope = variant.node.id; + let result_ty = match variant.node.kind { ast::tuple_variant_kind(ref args) if args.len() > 0 => { - let rs = TypeRscope(region_parameterization); + let rs = ExplicitRscope; let input_tys = args.map(|va| ccx.to_ty(&rs, &va.ty)); - result_ty = Some(ty::mk_ctor_fn(tcx, input_tys, enum_ty)); + ty::mk_ctor_fn(tcx, scope, input_tys, enum_ty) } ast::tuple_variant_kind(_) => { - result_ty = Some(enum_ty); + enum_ty } ast::struct_variant_kind(struct_def) => { let tpt = ty_param_bounds_and_ty { - generics: ty_generics(ccx, rp, generics, 0), + generics: ty_generics(ccx, generics, 0), ty: enum_ty }; - convert_struct(ccx, - rp, - struct_def, - generics, - tpt, - variant.node.id); + convert_struct(ccx, struct_def, tpt, variant.node.id); let input_tys = struct_def.fields.map( |f| ty::node_id_to_type(ccx.tcx, f.node.id)); - result_ty = Some(ty::mk_ctor_fn(tcx, input_tys, enum_ty)); + ty::mk_ctor_fn(tcx, scope, input_tys, enum_ty) } }; - match result_ty { - None => {} - Some(result_ty) => { - let tpt = ty_param_bounds_and_ty { - generics: ty_generics(ccx, rp, generics, 0), - ty: result_ty - }; - tcx.tcache.insert(local_def(variant.node.id), tpt); - write_ty_to_tcx(tcx, variant.node.id, result_ty); - } - } + let tpt = ty_param_bounds_and_ty { + generics: ty_generics(ccx, generics, 0), + ty: result_ty + }; + tcx.tcache.insert(local_def(variant.node.id), tpt); + write_ty_to_tcx(tcx, variant.node.id, result_ty); } } @@ -209,13 +181,13 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { let tcx = ccx.tcx; - let region_paramd = tcx.region_paramd_items.find(&trait_id).map(|x| *x); match tcx.items.get_copy(&trait_id) { ast_map::node_item(@ast::item { node: ast::item_trait(ref generics, _, ref ms), _ }, _) => { - let trait_ty_generics = ty_generics(ccx, region_paramd, generics, 0); + let trait_ty_generics = + ty_generics(ccx, generics, 0); // For each method, construct a suitable ty::Method and // store it into the `tcx.methods` table: @@ -223,14 +195,14 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, let ty_method = @match m { &ast::required(ref m) => { ty_method_of_trait_method( - ccx, trait_id, region_paramd, generics, + ccx, trait_id, &trait_ty_generics, &m.id, &m.ident, &m.explicit_self, &m.generics, &m.purity, &m.decl) } &ast::provided(ref m) => { ty_method_of_trait_method( - ccx, trait_id, region_paramd, generics, + ccx, trait_id, &trait_ty_generics, &m.id, &m.ident, &m.explicit_self, &m.generics, &m.purity, &m.decl) } @@ -264,13 +236,13 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_ty_generics: &ty::Generics) { // If declaration is // - // trait { - // fn foo(...) -> Self; + // trait<'a,'b,'c,A,B,C> { + // fn foo<'d,'e,'f,D,E,F>(...) -> Self; // } // // and we will create a function like // - // fn foo(...) -> D' {} + // fn foo<'a,'b,'c,'d,'e,'f,A',B',C',D',E',F',G'>(...) -> D' {} // // Note that `Self` is replaced with an explicit type // parameter D' that is sandwiched in between the trait params @@ -307,12 +279,19 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, m.generics.type_param_defs[i].def_id) }; + // Convert the regions 'a, 'b, 'c defined on the trait into + // bound regions on the fn. + let rps_from_trait = trait_ty_generics.region_param_defs.iter().map(|d| { + ty::ReLateBound(m.fty.sig.binder_id, + ty::BrNamed(d.def_id, d.ident)) + }).collect(); + // build up the substitution from // A,B,C => A',B',C' // Self => D' // D,E,F => E',F',G' let substs = substs { - regions: ty::NonerasedRegions(opt_vec::Empty), + regions: ty::NonerasedRegions(rps_from_trait), self_ty: Some(self_param), tps: non_shifted_trait_tps + shifted_method_tps }; @@ -357,7 +336,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, ty_param_bounds_and_ty { generics: ty::Generics { type_param_defs: @new_type_param_defs, - region_param: trait_ty_generics.region_param + region_param_defs: @[], // fn items }, ty: ty }); @@ -365,8 +344,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, fn ty_method_of_trait_method(this: &CrateCtxt, trait_id: ast::NodeId, - trait_rp: Option, - trait_generics: &ast::Generics, + trait_generics: &ty::Generics, m_id: &ast::NodeId, m_ident: &ast::Ident, m_explicit_self: &ast::explicit_self, @@ -375,14 +353,14 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, m_decl: &ast::fn_decl) -> ty::Method { let trait_self_ty = ty::mk_self(this.tcx, local_def(trait_id)); - let rscope = MethodRscope::new(m_explicit_self.node, trait_rp, trait_generics); let (transformed_self_ty, fty) = - astconv::ty_of_method(this, &rscope, *m_purity, &m_generics.lifetimes, + astconv::ty_of_method(this, *m_id, *m_purity, trait_self_ty, *m_explicit_self, m_decl); - let num_trait_type_params = trait_generics.ty_params.len(); + let num_trait_type_params = trait_generics.type_param_defs.len(); ty::Method::new( *m_ident, - ty_generics(this, None, m_generics, num_trait_type_params), + // FIXME(#5121) -- distinguish early vs late lifetime params + ty_generics(this, m_generics, num_trait_type_params), transformed_self_ty, fty, m_explicit_self.node, @@ -398,9 +376,8 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, pub fn ensure_supertraits(ccx: &CrateCtxt, id: ast::NodeId, sp: codemap::Span, - rp: Option, - ast_trait_refs: &[ast::trait_ref], - generics: &ast::Generics) -> ty::BuiltinBounds + ast_trait_refs: &[ast::trait_ref]) + -> ty::BuiltinBounds { let tcx = ccx.tcx; @@ -416,8 +393,7 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, // FIXME(#8559): Need to instantiate the trait_ref whether or not it's a // builtin trait, so that the trait's node id appears in the tcx trait_ref // map. This is only needed for metadata; see the similar fixme in encoder.rs. - let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, rp, - generics, self_ty); + let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, self_ty); if !ty::try_add_builtin_trait(ccx.tcx, trait_def_id, &mut bounds) { // FIXME(#5527) Could have same trait multiple times @@ -435,376 +411,69 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, bounds } -/** - * Checks that a method from an impl/class conforms to the signature of - * the same method as declared in the trait. - * - * # Parameters - * - * - impl_tps: the type params declared on the impl itself (not the method!) - * - cm: info about the method we are checking - * - trait_m: the method in the trait - * - trait_substs: the substitutions used on the type of the trait - * - self_ty: the self type of the impl - */ -pub fn compare_impl_method(tcx: ty::ctxt, - impl_tps: uint, - cm: &ConvertedMethod, - trait_m: &ty::Method, - trait_substs: &ty::substs, - self_ty: ty::t) { - debug!("compare_impl_method()"); - let infcx = infer::new_infer_ctxt(tcx); - - let impl_m = &cm.mty; - - // Try to give more informative error messages about self typing - // mismatches. Note that any mismatch will also be detected - // below, where we construct a canonical function type that - // includes the self parameter as a normal parameter. It's just - // that the error messages you get out of this code are a bit more - // inscrutable, particularly for cases where one method has no - // self. - match (&trait_m.explicit_self, &impl_m.explicit_self) { - (&ast::sty_static, &ast::sty_static) => {} - (&ast::sty_static, _) => { - tcx.sess.span_err( - cm.span, - format!("method `{}` has a `{}` declaration in the impl, \ - but not in the trait", - tcx.sess.str_of(trait_m.ident), - explicit_self_to_str(&impl_m.explicit_self, tcx.sess.intr()))); - return; - } - (_, &ast::sty_static) => { - tcx.sess.span_err( - cm.span, - format!("method `{}` has a `{}` declaration in the trait, \ - but not in the impl", - tcx.sess.str_of(trait_m.ident), - explicit_self_to_str(&trait_m.explicit_self, tcx.sess.intr()))); - return; - } - _ => { - // Let the type checker catch other errors below - } - } - - let num_impl_m_type_params = impl_m.generics.type_param_defs.len(); - let num_trait_m_type_params = trait_m.generics.type_param_defs.len(); - if num_impl_m_type_params != num_trait_m_type_params { - tcx.sess.span_err( - cm.span, - format!("method `{}` has {} type {}, but its trait \ - declaration has {} type {}", - tcx.sess.str_of(trait_m.ident), - num_impl_m_type_params, - pluralize(num_impl_m_type_params, ~"parameter"), - num_trait_m_type_params, - pluralize(num_trait_m_type_params, ~"parameter"))); - return; - } - - if impl_m.fty.sig.inputs.len() != trait_m.fty.sig.inputs.len() { - tcx.sess.span_err( - cm.span, - format!("method `{}` has {} parameter{} \ - but the trait has {}", - tcx.sess.str_of(trait_m.ident), - impl_m.fty.sig.inputs.len(), - if impl_m.fty.sig.inputs.len() == 1 { "" } else { "s" }, - trait_m.fty.sig.inputs.len())); - return; - } - - for (i, trait_param_def) in trait_m.generics.type_param_defs.iter().enumerate() { - // For each of the corresponding impl ty param's bounds... - let impl_param_def = &impl_m.generics.type_param_defs[i]; - - // Check that the impl does not require any builtin-bounds - // that the trait does not guarantee: - let extra_bounds = - impl_param_def.bounds.builtin_bounds - - trait_param_def.bounds.builtin_bounds; - if !extra_bounds.is_empty() { - tcx.sess.span_err( - cm.span, - format!("in method `{}`, \ - type parameter {} requires `{}`, \ - which is not required by \ - the corresponding type parameter \ - in the trait declaration", - tcx.sess.str_of(trait_m.ident), - i, - extra_bounds.user_string(tcx))); - return; - } - - // FIXME(#2687)---we should be checking that the bounds of the - // trait imply the bounds of the subtype, but it appears we - // are...not checking this. - if impl_param_def.bounds.trait_bounds.len() != - trait_param_def.bounds.trait_bounds.len() - { - tcx.sess.span_err( - cm.span, - format!("in method `{}`, \ - type parameter {} has {} trait {}, but the \ - corresponding type parameter in \ - the trait declaration has {} trait {}", - tcx.sess.str_of(trait_m.ident), - i, impl_param_def.bounds.trait_bounds.len(), - pluralize(impl_param_def.bounds.trait_bounds.len(), - ~"bound"), - trait_param_def.bounds.trait_bounds.len(), - pluralize(trait_param_def.bounds.trait_bounds.len(), - ~"bound"))); - return; - } - } - - // Replace any references to the self region in the self type with - // a free region. So, for example, if the impl type is - // "&'self str", then this would replace the self type with a free - // region `self`. - let dummy_self_r = ty::re_free(ty::FreeRegion {scope_id: cm.body_id, - bound_region: ty::br_self}); - let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r); - - // We are going to create a synthetic fn type that includes - // both the method's self argument and its normal arguments. - // So a method like `fn(&self, a: uint)` would be converted - // into a function `fn(self: &T, a: uint)`. - let mut trait_fn_args = ~[]; - let mut impl_fn_args = ~[]; - - // For both the trait and the impl, create an argument to - // represent the self argument (unless this is a static method). - // This argument will have the *transformed* self type. - for &t in trait_m.transformed_self_ty.iter() { - trait_fn_args.push(t); - } - for &t in impl_m.transformed_self_ty.iter() { - impl_fn_args.push(t); - } - - // Add in the normal arguments. - trait_fn_args.push_all(trait_m.fty.sig.inputs); - impl_fn_args.push_all(impl_m.fty.sig.inputs); - - // Create a bare fn type for trait/impl that includes self argument - let trait_fty = - ty::mk_bare_fn(tcx, - ty::BareFnTy { - purity: trait_m.fty.purity, - abis: trait_m.fty.abis, - sig: ty::FnSig { - bound_lifetime_names: - trait_m.fty - .sig - .bound_lifetime_names - .clone(), - inputs: trait_fn_args, - output: trait_m.fty.sig.output, - variadic: false - } - }); - let impl_fty = - ty::mk_bare_fn(tcx, - ty::BareFnTy { - purity: impl_m.fty.purity, - abis: impl_m.fty.abis, - sig: ty::FnSig { - bound_lifetime_names: - impl_m.fty - .sig - .bound_lifetime_names - .clone(), - inputs: impl_fn_args, - output: impl_m.fty.sig.output, - variadic: false - } - }); - - // Perform substitutions so that the trait/impl methods are expressed - // in terms of the same set of type/region parameters: - // - replace trait type parameters with those from `trait_substs`, - // except with any reference to bound self replaced with `dummy_self_r` - // - replace method parameters on the trait with fresh, dummy parameters - // that correspond to the parameters we will find on the impl - // - replace self region with a fresh, dummy region - let impl_fty = { - debug!("impl_fty (pre-subst): {}", ppaux::ty_to_str(tcx, impl_fty)); - replace_bound_self(tcx, impl_fty, dummy_self_r) - }; - debug!("impl_fty (post-subst): {}", ppaux::ty_to_str(tcx, impl_fty)); - let trait_fty = { - let num_trait_m_type_params = trait_m.generics.type_param_defs.len(); - let dummy_tps = do vec::from_fn(num_trait_m_type_params) |i| { - ty::mk_param(tcx, i + impl_tps, - impl_m.generics.type_param_defs[i].def_id) - }; - let trait_tps = trait_substs.tps.map( - |t| replace_bound_self(tcx, *t, dummy_self_r)); - let substs = substs { - regions: ty::NonerasedRegions(opt_vec::with(dummy_self_r)), - self_ty: Some(self_ty), - tps: vec::append(trait_tps, dummy_tps) - }; - debug!("trait_fty (pre-subst): {} substs={}", - trait_fty.repr(tcx), substs.repr(tcx)); - ty::subst(tcx, &substs, trait_fty) - }; - debug!("trait_fty (post-subst): {}", trait_fty.repr(tcx)); - - match infer::mk_subty(infcx, false, infer::MethodCompatCheck(cm.span), - impl_fty, trait_fty) { - result::Ok(()) => {} - result::Err(ref terr) => { - tcx.sess.span_err( - cm.span, - format!("method `{}` has an incompatible type: {}", - tcx.sess.str_of(trait_m.ident), - ty::type_err_to_str(tcx, terr))); - ty::note_and_explain_type_err(tcx, terr); - } - } - return; - - // Replaces bound references to the self region with `with_r`. - fn replace_bound_self(tcx: ty::ctxt, ty: ty::t, - with_r: ty::Region) -> ty::t { - do ty::fold_regions(tcx, ty) |r, _in_fn| { - if r == ty::re_bound(ty::br_self) {with_r} else {r} - } - } -} - -pub fn check_methods_against_trait(ccx: &CrateCtxt, - generics: &ast::Generics, - rp: Option, - selfty: ty::t, - a_trait_ty: &ast::trait_ref, - impl_ms: &[ConvertedMethod]) -{ - let tcx = ccx.tcx; - let trait_ref = instantiate_trait_ref(ccx, a_trait_ty, rp, - generics, selfty); - - if trait_ref.def_id.crate == ast::LOCAL_CRATE { - ensure_trait_methods(ccx, trait_ref.def_id.node); - } - - // Check that each method we impl is a method on the trait - // Trait methods we don't implement must be default methods, but if not - // we'll catch it in coherence - let trait_ms = ty::trait_methods(tcx, trait_ref.def_id); - for impl_m in impl_ms.iter() { - match trait_ms.iter().find(|trait_m| trait_m.ident.name == impl_m.mty.ident.name) { - Some(trait_m) => { - let num_impl_tps = generics.ty_params.len(); - compare_impl_method( - ccx.tcx, num_impl_tps, impl_m, *trait_m, - &trait_ref.substs, selfty); - } - None => { - // This method is not part of the trait - tcx.sess.span_err( - impl_m.span, - format!("method `{}` is not a member of trait `{}`", - tcx.sess.str_of(impl_m.mty.ident), - path_to_str(&a_trait_ty.path, tcx.sess.intr()))); - } - } - } -} // fn - pub fn convert_field(ccx: &CrateCtxt, - rp: Option, - type_param_defs: @~[ty::TypeParameterDef], - v: &ast::struct_field, - generics: &ast::Generics) { - let region_parameterization = - RegionParameterization::from_variance_and_generics(rp, generics); - let tt = ccx.to_ty(&TypeRscope(region_parameterization), &v.node.ty); + struct_generics: &ty::Generics, + v: &ast::struct_field) { + let tt = ccx.to_ty(&ExplicitRscope, &v.node.ty); write_ty_to_tcx(ccx.tcx, v.node.id, tt); /* add the field to the tcache */ ccx.tcx.tcache.insert(local_def(v.node.id), ty::ty_param_bounds_and_ty { - generics: ty::Generics { - type_param_defs: type_param_defs, - region_param: rp - }, + generics: struct_generics.clone(), ty: tt }); } -pub struct ConvertedMethod { - mty: @ty::Method, - id: ast::NodeId, - span: Span, - body_id: ast::NodeId -} - -pub fn convert_methods(ccx: &CrateCtxt, - container: MethodContainer, - ms: &[@ast::method], - untransformed_rcvr_ty: ty::t, - rcvr_ty_generics: &ty::Generics, - rcvr_ast_generics: &ast::Generics, - rcvr_visibility: ast::visibility) - -> ~[ConvertedMethod] +fn convert_methods(ccx: &CrateCtxt, + container: MethodContainer, + ms: &[@ast::method], + untransformed_rcvr_ty: ty::t, + rcvr_ty_generics: &ty::Generics, + rcvr_ast_generics: &ast::Generics, + rcvr_visibility: ast::visibility) { let tcx = ccx.tcx; - return ms.iter().map(|m| { + for m in ms.iter() { let num_rcvr_ty_params = rcvr_ty_generics.type_param_defs.len(); - let m_ty_generics = - ty_generics(ccx, rcvr_ty_generics.region_param, &m.generics, - num_rcvr_ty_params); + let m_ty_generics = ty_generics(ccx, &m.generics, num_rcvr_ty_params); let mty = @ty_of_method(ccx, container, *m, - rcvr_ty_generics.region_param, untransformed_rcvr_ty, rcvr_ast_generics, - rcvr_visibility, - &m.generics); + rcvr_visibility); let fty = ty::mk_bare_fn(tcx, mty.fty.clone()); + debug!("method {} (id {}) has type {}", + m.ident.repr(ccx.tcx), + m.id, + fty.repr(ccx.tcx)); tcx.tcache.insert( local_def(m.id), // n.b.: the type of a method is parameterized by both - // the tps on the receiver and those on the method itself + // the parameters on the receiver and those on the method itself ty_param_bounds_and_ty { generics: ty::Generics { type_param_defs: @vec::append( (*rcvr_ty_generics.type_param_defs).clone(), *m_ty_generics.type_param_defs), - region_param: rcvr_ty_generics.region_param + region_param_defs: rcvr_ty_generics.region_param_defs, }, ty: fty }); write_ty_to_tcx(tcx, m.id, fty); tcx.methods.insert(mty.def_id, mty); - ConvertedMethod {mty: mty, id: m.id, - span: m.span, body_id: m.body.id} - }).collect(); + } fn ty_of_method(ccx: &CrateCtxt, container: MethodContainer, m: &ast::method, - rp: Option, untransformed_rcvr_ty: ty::t, rcvr_generics: &ast::Generics, - rcvr_visibility: ast::visibility, - method_generics: &ast::Generics) -> ty::Method + rcvr_visibility: ast::visibility) -> ty::Method { - let rscope = MethodRscope::new(m.explicit_self.node, - rp, - rcvr_generics); let (transformed_self_ty, fty) = - astconv::ty_of_method(ccx, &rscope, m.purity, - &method_generics.lifetimes, + astconv::ty_of_method(ccx, m.id, m.purity, untransformed_rcvr_ty, m.explicit_self, &m.decl); @@ -817,7 +486,8 @@ pub fn convert_methods(ccx: &CrateCtxt, let num_rcvr_type_params = rcvr_generics.ty_params.len(); ty::Method::new( m.ident, - ty_generics(ccx, None, &m.generics, num_rcvr_type_params), + // FIXME(#5121) -- distinguish early vs late lifetime params + ty_generics(ccx, &m.generics, num_rcvr_type_params), transformed_self_ty, fty, m.explicit_self.node, @@ -845,27 +515,22 @@ pub fn ensure_no_ty_param_bounds(ccx: &CrateCtxt, pub fn convert(ccx: &CrateCtxt, it: &ast::item) { let tcx = ccx.tcx; - let rp = tcx.region_paramd_items.find(&it.id).map(|x| *x); - debug!("convert: item {} with id {} rp {:?}", - tcx.sess.str_of(it.ident), it.id, rp); + debug!("convert: item {} with id {}", tcx.sess.str_of(it.ident), it.id); match it.node { // These don't define types. ast::item_foreign_mod(_) | ast::item_mod(_) => {} ast::item_enum(ref enum_definition, ref generics) => { - ensure_no_ty_param_bounds(ccx, it.span, generics, "enumeration"); - let tpt = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, tpt.ty); - get_enum_variant_types(ccx, - tpt.ty, - enum_definition.variants, - generics, - rp); + ensure_no_ty_param_bounds(ccx, it.span, generics, "enumeration"); + let tpt = ty_of_item(ccx, it); + write_ty_to_tcx(tcx, it.id, tpt.ty); + get_enum_variant_types(ccx, + tpt.ty, + enum_definition.variants, + generics); } ast::item_impl(ref generics, ref opt_trait_ref, ref selfty, ref ms) => { - let i_ty_generics = ty_generics(ccx, rp, generics, 0); - let region_parameterization = - RegionParameterization::from_variance_and_generics(rp, generics); - let selfty = ccx.to_ty(&TypeRscope(region_parameterization), selfty); + let i_ty_generics = ty_generics(ccx, generics, 0); + let selfty = ccx.to_ty(&ExplicitRscope, selfty); write_ty_to_tcx(tcx, it.id, selfty); tcx.tcache.insert(local_def(it.id), ty_param_bounds_and_ty { @@ -883,41 +548,39 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { it.vis }; - let cms = convert_methods(ccx, - ImplContainer(local_def(it.id)), - *ms, - selfty, - &i_ty_generics, - generics, - parent_visibility); - for t in opt_trait_ref.iter() { + convert_methods(ccx, + ImplContainer(local_def(it.id)), + *ms, + selfty, + &i_ty_generics, + generics, + parent_visibility); + + for trait_ref in opt_trait_ref.iter() { + let trait_ref = instantiate_trait_ref(ccx, trait_ref, selfty); + // Prevent the builtin kind traits from being manually implemented. - let trait_def_id = ty::trait_ref_to_def_id(tcx, t); - if tcx.lang_items.to_builtin_kind(trait_def_id).is_some() { + if tcx.lang_items.to_builtin_kind(trait_ref.def_id).is_some() { tcx.sess.span_err(it.span, "cannot provide an explicit implementation \ for a builtin kind"); } - - check_methods_against_trait(ccx, generics, rp, selfty, t, cms); } } ast::item_trait(ref generics, _, ref trait_methods) => { - let _trait_def = trait_def_of_item(ccx, it); + let trait_def = trait_def_of_item(ccx, it); // Run convert_methods on the provided methods. let (_, provided_methods) = split_trait_methods(*trait_methods); let untransformed_rcvr_ty = ty::mk_self(tcx, local_def(it.id)); - let (ty_generics, _) = mk_item_substs(ccx, generics, rp, - Some(untransformed_rcvr_ty)); - let _ = convert_methods(ccx, - TraitContainer(local_def(it.id)), - provided_methods, - untransformed_rcvr_ty, - &ty_generics, - generics, - it.vis); + convert_methods(ccx, + TraitContainer(local_def(it.id)), + provided_methods, + untransformed_rcvr_ty, + &trait_def.generics, + generics, + it.vis); // We need to do this *after* converting methods, since // convert_methods produces a tcache entry that is wrong for @@ -932,7 +595,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { write_ty_to_tcx(tcx, it.id, tpt.ty); tcx.tcache.insert(local_def(it.id), tpt); - convert_struct(ccx, rp, struct_def, generics, tpt, it.id); + convert_struct(ccx, struct_def, tpt, it.id); } ast::item_ty(_, ref generics) => { ensure_no_ty_param_bounds(ccx, it.span, generics, "type"); @@ -950,18 +613,16 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { } pub fn convert_struct(ccx: &CrateCtxt, - rp: Option, struct_def: &ast::struct_def, - generics: &ast::Generics, tpt: ty::ty_param_bounds_and_ty, id: ast::NodeId) { let tcx = ccx.tcx; // Write the type of each of the members for f in struct_def.fields.iter() { - convert_field(ccx, rp, tpt.generics.type_param_defs, *f, generics); + convert_field(ccx, &tpt.generics, *f); } - let (_, substs) = mk_item_substs(ccx, generics, rp, None); + let substs = mk_item_substs(ccx, &tpt.generics, None); let selfty = ty::mk_struct(tcx, local_def(id), substs); // If this struct is enum-like or tuple-like, create the type of its @@ -979,7 +640,7 @@ pub fn convert_struct(ccx: &CrateCtxt, struct_def.fields.map( |field| ccx.tcx.tcache.get( &local_def(field.node.id)).ty); - let ctor_fn_ty = ty::mk_ctor_fn(tcx, inputs, selfty); + let ctor_fn_ty = ty::mk_ctor_fn(tcx, ctor_id, inputs, selfty); write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty); tcx.tcache.insert(local_def(ctor_id), ty_param_bounds_and_ty { generics: tpt.generics, @@ -1014,8 +675,6 @@ pub fn convert_foreign(ccx: &CrateCtxt, i: &ast::foreign_item) { pub fn instantiate_trait_ref(ccx: &CrateCtxt, ast_trait_ref: &ast::trait_ref, - rp: Option, - generics: &ast::Generics, self_ty: ty::t) -> @ty::TraitRef { /*! @@ -1024,9 +683,8 @@ pub fn instantiate_trait_ref(ccx: &CrateCtxt, * trait. Fails if the type is a type other than an trait type. */ - let rp = RegionParameterization::from_variance_and_generics(rp, generics); - - let rscope = TypeRscope(rp); + // FIXME(#5121) -- distinguish early vs late lifetime params + let rscope = ExplicitRscope; match lookup_def_tcx(ccx.tcx, ast_trait_ref.path.span, ast_trait_ref.ref_id) { ast::DefTrait(trait_did) => { @@ -1066,14 +724,12 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::item) -> @ty::TraitDef { Some(&def) => return def, _ => {} } - let rp = tcx.region_paramd_items.find(&it.id).map(|x| *x); match it.node { ast::item_trait(ref generics, ref supertraits, _) => { let self_ty = ty::mk_self(tcx, def_id); - let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, - Some(self_ty)); - let bounds = ensure_supertraits(ccx, it.id, it.span, rp, - *supertraits, generics); + let ty_generics = ty_generics(ccx, generics, 0); + let substs = mk_item_substs(ccx, &ty_generics, Some(self_ty)); + let bounds = ensure_supertraits(ccx, it.id, it.span, *supertraits); let trait_ref = @ty::TraitRef {def_id: def_id, substs: substs}; let trait_def = @ty::TraitDef {generics: ty_generics, @@ -1091,93 +747,89 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::item) -> @ty::TraitDef { } pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) - -> ty::ty_param_bounds_and_ty { + -> ty::ty_param_bounds_and_ty { let def_id = local_def(it.id); let tcx = ccx.tcx; match tcx.tcache.find(&def_id) { - Some(&tpt) => return tpt, - _ => {} + Some(&tpt) => return tpt, + _ => {} } - let rp = tcx.region_paramd_items.find(&it.id).map(|x| *x); match it.node { - ast::item_static(ref t, _, _) => { - let typ = ccx.to_ty(&EmptyRscope, t); - let tpt = no_params(typ); - tcx.tcache.insert(local_def(it.id), tpt); - return tpt; - } - ast::item_fn(ref decl, purity, abi, ref generics, _) => { - assert!(rp.is_none()); - let ty_generics = ty_generics(ccx, None, generics, 0); - let tofd = astconv::ty_of_bare_fn(ccx, - &EmptyRscope, - purity, - abi, - &generics.lifetimes, - decl); - let tpt = ty_param_bounds_and_ty { - generics: ty::Generics { - type_param_defs: ty_generics.type_param_defs, - region_param: None - }, - ty: ty::mk_bare_fn(ccx.tcx, tofd) - }; - debug!("type of {} (id {}) is {}", - tcx.sess.str_of(it.ident), - it.id, - ppaux::ty_to_str(tcx, tpt.ty)); - ccx.tcx.tcache.insert(local_def(it.id), tpt); - return tpt; - } - ast::item_ty(ref t, ref generics) => { - match tcx.tcache.find(&local_def(it.id)) { - Some(&tpt) => return tpt, - None => { } + ast::item_static(ref t, _, _) => { + let typ = ccx.to_ty(&ExplicitRscope, t); + let tpt = no_params(typ); + tcx.tcache.insert(local_def(it.id), tpt); + return tpt; } - - let rp = tcx.region_paramd_items.find(&it.id).map(|x| *x); - let region_parameterization = - RegionParameterization::from_variance_and_generics(rp, generics); - let tpt = { - let ty = ccx.to_ty(&TypeRscope(region_parameterization), t); - ty_param_bounds_and_ty { - generics: ty_generics(ccx, rp, generics, 0), - ty: ty + ast::item_fn(ref decl, purity, abi, ref generics, _) => { + let ty_generics = ty_generics(ccx, generics, 0); + let tofd = astconv::ty_of_bare_fn(ccx, + it.id, + purity, + abi, + decl); + let tpt = ty_param_bounds_and_ty { + generics: ty::Generics { + type_param_defs: ty_generics.type_param_defs, + region_param_defs: @[], + }, + ty: ty::mk_bare_fn(ccx.tcx, tofd) + }; + debug!("type of {} (id {}) is {}", + tcx.sess.str_of(it.ident), + it.id, + ppaux::ty_to_str(tcx, tpt.ty)); + ccx.tcx.tcache.insert(local_def(it.id), tpt); + return tpt; + } + ast::item_ty(ref t, ref generics) => { + match tcx.tcache.find(&local_def(it.id)) { + Some(&tpt) => return tpt, + None => { } } - }; - tcx.tcache.insert(local_def(it.id), tpt); - return tpt; - } - ast::item_enum(_, ref generics) => { - // Create a new generic polytype. - let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, None); - let t = ty::mk_enum(tcx, local_def(it.id), substs); - let tpt = ty_param_bounds_and_ty { - generics: ty_generics, - ty: t - }; - tcx.tcache.insert(local_def(it.id), tpt); - return tpt; - } - ast::item_trait(*) => { - tcx.sess.span_bug( - it.span, - format!("Invoked ty_of_item on trait")); - } - ast::item_struct(_, ref generics) => { - let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, None); - let t = ty::mk_struct(tcx, local_def(it.id), substs); - let tpt = ty_param_bounds_and_ty { - generics: ty_generics, - ty: t - }; - tcx.tcache.insert(local_def(it.id), tpt); - return tpt; - } - ast::item_impl(*) | ast::item_mod(_) | - ast::item_foreign_mod(_) => fail!(), - ast::item_mac(*) => fail!("item macros unimplemented") + let tpt = { + let ty = ccx.to_ty(&ExplicitRscope, t); + ty_param_bounds_and_ty { + generics: ty_generics(ccx, generics, 0), + ty: ty + } + }; + + tcx.tcache.insert(local_def(it.id), tpt); + return tpt; + } + ast::item_enum(_, ref generics) => { + // Create a new generic polytype. + let ty_generics = ty_generics(ccx, generics, 0); + let substs = mk_item_substs(ccx, &ty_generics, None); + let t = ty::mk_enum(tcx, local_def(it.id), substs); + let tpt = ty_param_bounds_and_ty { + generics: ty_generics, + ty: t + }; + tcx.tcache.insert(local_def(it.id), tpt); + return tpt; + } + ast::item_trait(*) => { + tcx.sess.span_bug( + it.span, + format!("Invoked ty_of_item on trait")); + } + ast::item_struct(_, ref generics) => { + let ty_generics = ty_generics(ccx, generics, 0); + let substs = mk_item_substs(ccx, &ty_generics, None); + let t = ty::mk_struct(tcx, local_def(it.id), substs); + let tpt = ty_param_bounds_and_ty { + generics: ty_generics, + ty: t + }; + tcx.tcache.insert(local_def(it.id), tpt); + return tpt; + } + ast::item_impl(*) | ast::item_mod(_) | + ast::item_foreign_mod(_) => fail!(), + ast::item_mac(*) => fail!("item macros unimplemented") } } @@ -1197,28 +849,29 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt, ty::ty_param_bounds_and_ty { generics: ty::Generics { type_param_defs: @~[], - region_param: None, + region_param_defs: @[], }, - ty: ast_ty_to_ty(ccx, &EmptyRscope, t) + ty: ast_ty_to_ty(ccx, &ExplicitRscope, t) } } } } pub fn ty_generics(ccx: &CrateCtxt, - rp: Option, generics: &ast::Generics, base_index: uint) -> ty::Generics { return ty::Generics { - region_param: rp, + region_param_defs: generics.lifetimes.iter().map(|l| { + ty::RegionParameterDef { ident: l.ident, + def_id: local_def(l.id) } + }).collect(), type_param_defs: @generics.ty_params.mapi_to_vec(|offset, param| { match ccx.tcx.ty_param_defs.find(¶m.id) { Some(&def) => def, None => { let param_ty = ty::param_ty {idx: base_index + offset, def_id: local_def(param.id)}; - let bounds = @compute_bounds(ccx, rp, generics, - param_ty, ¶m.bounds); + let bounds = @compute_bounds(ccx, param_ty, ¶m.bounds); let def = ty::TypeParameterDef { ident: param.ident, def_id: local_def(param.id), @@ -1234,13 +887,10 @@ pub fn ty_generics(ccx: &CrateCtxt, fn compute_bounds( ccx: &CrateCtxt, - rp: Option, - generics: &ast::Generics, param_ty: ty::param_ty, ast_bounds: &OptVec) -> ty::ParamBounds { /*! - * * Translate the AST's notion of ty param bounds (which are an * enum consisting of a newtyped Ty or a region) to ty's * notion of ty param bounds, which can either be user-defined @@ -1256,7 +906,7 @@ pub fn ty_generics(ccx: &CrateCtxt, match *ast_bound { TraitTyParamBound(ref b) => { let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id); - let trait_ref = instantiate_trait_ref(ccx, b, rp, generics, ty); + let trait_ref = instantiate_trait_ref(ccx, b, ty); if !ty::try_add_builtin_trait( ccx.tcx, trait_ref.def_id, &mut param_bounds.builtin_bounds) @@ -1282,9 +932,8 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, ast_generics: &ast::Generics, abis: AbiSet) -> ty::ty_param_bounds_and_ty { - let ty_generics = ty_generics(ccx, None, ast_generics, 0); - let region_param_names = RegionParamNames::from_generics(ast_generics); - let rb = in_binding_rscope(&EmptyRscope, region_param_names); + let ty_generics = ty_generics(ccx, ast_generics, 0); + let rb = BindingRscope::new(def_id.node); let input_tys = decl.inputs.map(|a| ty_of_arg(ccx, &rb, a, None) ); let output_ty = ast_ty_to_ty(ccx, &rb, &decl.output); @@ -1293,12 +942,10 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, ty::BareFnTy { abis: abis, purity: ast::unsafe_fn, - sig: ty::FnSig { - bound_lifetime_names: opt_vec::Empty, - inputs: input_tys, - output: output_ty, - variadic: decl.variadic - } + sig: ty::FnSig {binder_id: def_id.node, + inputs: input_tys, + output: output_ty, + variadic: decl.variadic} }); let tpt = ty_param_bounds_and_ty { generics: ty_generics, @@ -1309,19 +956,18 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, } pub fn mk_item_substs(ccx: &CrateCtxt, - ast_generics: &ast::Generics, - rp: Option, - self_ty: Option) -> (ty::Generics, ty::substs) + ty_generics: &ty::Generics, + self_ty: Option) -> ty::substs { - let mut i = 0; - let ty_generics = ty_generics(ccx, rp, ast_generics, 0); - let params = ast_generics.ty_params.map_to_vec(|atp| { - let t = ty::mk_param(ccx.tcx, i, local_def(atp.id)); - i += 1u; - t - }); - let regions = rscope::bound_self_region(rp); - (ty_generics, substs {regions: ty::NonerasedRegions(regions), - self_ty: self_ty, - tps: params}) + let params: ~[ty::t] = + ty_generics.type_param_defs.iter().enumerate().map( + |(i, t)| ty::mk_param(ccx.tcx, i, t.def_id)).collect(); + + let regions: OptVec = + ty_generics.region_param_defs.iter().enumerate().map( + |(i, l)| ty::ReEarlyBound(l.def_id.node, i, l.ident)).collect(); + + substs {regions: ty::NonerasedRegions(regions), + self_ty: self_ty, + tps: params} } diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 5d0f44ae7e356..c42f74864d249 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -123,76 +123,70 @@ pub trait Combine { } } - fn substs(&self, generics: &ty::Generics, as_: &ty::substs, + fn substs(&self, + item_def_id: ast::DefId, + as_: &ty::substs, bs: &ty::substs) -> cres { - fn relate_region_params( - this: &C, - generics: &ty::Generics, + fn relate_region_params(this: &C, + item_def_id: ast::DefId, a: &ty::RegionSubsts, b: &ty::RegionSubsts) - -> cres - { + -> cres { + let tcx = this.infcx().tcx; match (a, b) { - (&ty::ErasedRegions, _) | - (_, &ty::ErasedRegions) => { + (&ty::ErasedRegions, _) | (_, &ty::ErasedRegions) => { Ok(ty::ErasedRegions) } (&ty::NonerasedRegions(ref a_rs), &ty::NonerasedRegions(ref b_rs)) => { - match generics.region_param { - None => { - assert!(a_rs.is_empty()); - assert!(b_rs.is_empty()); - Ok(ty::NonerasedRegions(opt_vec::Empty)) - } - - Some(variance) => { - assert_eq!(a_rs.len(), 1); - assert_eq!(b_rs.len(), 1); - let a_r = *a_rs.get(0); - let b_r = *b_rs.get(0); - - match variance { - ty::rv_invariant => { - do eq_regions(this, a_r, b_r).then { - Ok(ty::NonerasedRegions(opt_vec::with(a_r))) - } - } - - ty::rv_covariant => { - do this.regions(a_r, b_r).and_then |r| { - Ok(ty::NonerasedRegions(opt_vec::with(r))) - } - } - - ty::rv_contravariant => { - do this.contraregions(a_r, b_r).and_then |r| { - Ok(ty::NonerasedRegions(opt_vec::with(r))) - } - } + let variances = ty::item_variances(tcx, item_def_id); + let region_params = &variances.region_params; + let num_region_params = region_params.len(); + + debug!("relate_region_params(\ + item_def_id={}, \ + a_rs={}, \ + b_rs={}, + region_params={})", + item_def_id.repr(tcx), + a_rs.repr(tcx), + b_rs.repr(tcx), + region_params.repr(tcx)); + + assert_eq!(num_region_params, a_rs.len()); + assert_eq!(num_region_params, b_rs.len()); + let mut rs = opt_vec::Empty; + for i in range(0, num_region_params) { + let a_r = *a_rs.get(i); + let b_r = *b_rs.get(i); + let variance = *region_params.get(i); + let r = match variance { + ty::Invariant => { + eq_regions(this, a_r, b_r) + .and_then(|()| Ok(a_r)) } - } + ty::Covariant => this.regions(a_r, b_r), + ty::Contravariant => this.contraregions(a_r, b_r), + ty::Bivariant => Ok(a_r), + }; + rs.push(if_ok!(r)); } + Ok(ty::NonerasedRegions(rs)) } } } - do self.tps(as_.tps, bs.tps).and_then |tps| { - do self.self_tys(as_.self_ty, bs.self_ty).and_then |self_ty| { - do relate_region_params(self, - generics, - &as_.regions, - &bs.regions).and_then |regions| { - Ok(substs { - regions: regions, - self_ty: self_ty, - tps: tps.clone() - }) - } - } - } + let tps = if_ok!(self.tps(as_.tps, bs.tps)); + let self_ty = if_ok!(self.self_tys(as_.self_ty, bs.self_ty)); + let regions = if_ok!(relate_region_params(self, + item_def_id, + &as_.regions, + &bs.regions)); + Ok(substs { regions: regions, + self_ty: self_ty, + tps: tps.clone() }) } fn bare_fn_tys(&self, a: &ty::BareFnTy, @@ -267,9 +261,11 @@ pub trait Combine { -> cres; fn regions(&self, a: ty::Region, b: ty::Region) -> cres; - fn vstores(&self, vk: ty::terr_vstore_kind, - a: ty::vstore, b: ty::vstore) -> cres { - + fn vstores(&self, + vk: ty::terr_vstore_kind, + a: ty::vstore, + b: ty::vstore) + -> cres { debug!("{}.vstores(a={:?}, b={:?})", self.tag(), a, b); match (a, b) { @@ -293,8 +289,7 @@ pub trait Combine { vk: ty::terr_vstore_kind, a: ty::TraitStore, b: ty::TraitStore) - -> cres { - + -> cres { debug!("{}.trait_stores(a={:?}, b={:?})", self.tag(), a, b); match (a, b) { @@ -317,7 +312,8 @@ pub trait Combine { fn trait_refs(&self, a: &ty::TraitRef, - b: &ty::TraitRef) -> cres { + b: &ty::TraitRef) + -> cres { // Different traits cannot be related // - NOTE in the future, expand out subtraits! @@ -326,15 +322,9 @@ pub trait Combine { Err(ty::terr_traits( expected_found(self, a.def_id, b.def_id))) } else { - let tcx = self.infcx().tcx; - let trait_def = ty::lookup_trait_def(tcx, a.def_id); - let substs = if_ok!(self.substs(&trait_def.generics, - &a.substs, - &b.substs)); - Ok(ty::TraitRef { - def_id: a.def_id, - substs: substs - }) + let substs = if_ok!(self.substs(a.def_id, &a.substs, &b.substs)); + Ok(ty::TraitRef { def_id: a.def_id, + substs: substs }) } } } @@ -366,8 +356,8 @@ pub fn eq_tys(this: &C, a: ty::t, b: ty::t) -> ures { pub fn eq_regions(this: &C, a: ty::Region, b: ty::Region) -> ures { debug!("eq_regions({}, {})", - a.inf_str(this.infcx()), - b.inf_str(this.infcx())); + a.repr(this.infcx().tcx), + b.repr(this.infcx().tcx)); let sub = this.sub(); do indent { this.infcx().try(|| { @@ -429,23 +419,20 @@ pub fn super_fn_sigs(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres< return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic))); } - do argvecs(this, a.inputs, b.inputs) - .and_then |inputs| { - do this.tys(a.output, b.output).and_then |output| { - Ok(FnSig { - bound_lifetime_names: opt_vec::Empty, // FIXME(#4846) - inputs: inputs.clone(), - output: output, - variadic: a.variadic - }) - } - } + let inputs = if_ok!(argvecs(this, a.inputs, b.inputs)); + let output = if_ok!(this.tys(a.output, b.output)); + Ok(FnSig {binder_id: a.binder_id, + inputs: inputs, + output: output, + variadic: a.variadic}) } -pub fn super_tys( - this: &C, a: ty::t, b: ty::t) -> cres { +pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { let tcx = this.infcx().tcx; - return match (&ty::get(a).sty, &ty::get(b).sty) { + let a_sty = &ty::get(a).sty; + let b_sty = &ty::get(b).sty; + debug!("super_tys: a_sty={:?} b_sty={:?}", a_sty, b_sty); + return match (a_sty, b_sty) { // The "subtype" ought to be handling cases involving bot or var: (&ty::ty_bot, _) | (_, &ty::ty_bot) | @@ -494,6 +481,7 @@ pub fn super_tys( unify_float_variable(this, !this.a_is_expected(), v_id, v) } + (&ty::ty_char, _) | (&ty::ty_nil, _) | (&ty::ty_bool, _) | (&ty::ty_int(_), _) | @@ -513,36 +501,30 @@ pub fn super_tys( (&ty::ty_enum(a_id, ref a_substs), &ty::ty_enum(b_id, ref b_substs)) if a_id == b_id => { - let type_def = ty::lookup_item_type(tcx, a_id); - do this.substs(&type_def.generics, a_substs, b_substs).and_then |substs| { - Ok(ty::mk_enum(tcx, a_id, substs)) - } + let substs = if_ok!(this.substs(a_id, + a_substs, + b_substs)); + Ok(ty::mk_enum(tcx, a_id, substs)) } (&ty::ty_trait(a_id, ref a_substs, a_store, a_mutbl, a_bounds), &ty::ty_trait(b_id, ref b_substs, b_store, b_mutbl, b_bounds)) if a_id == b_id && a_mutbl == b_mutbl => { - let trait_def = ty::lookup_trait_def(tcx, a_id); - do this.substs(&trait_def.generics, a_substs, b_substs).and_then |substs| { - do this.trait_stores(ty::terr_trait, a_store, b_store).and_then |s| { - do this.bounds(a_bounds, b_bounds).and_then |bounds| { - Ok(ty::mk_trait(tcx, - a_id, - substs.clone(), - s, - a_mutbl, - bounds)) - } - } - } + let substs = if_ok!(this.substs(a_id, a_substs, b_substs)); + let s = if_ok!(this.trait_stores(ty::terr_trait, a_store, b_store)); + let bounds = if_ok!(this.bounds(a_bounds, b_bounds)); + Ok(ty::mk_trait(tcx, + a_id, + substs.clone(), + s, + a_mutbl, + bounds)) } (&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs)) if a_id == b_id => { - let type_def = ty::lookup_item_type(tcx, a_id); - do this.substs(&type_def.generics, a_substs, b_substs).and_then |substs| { - Ok(ty::mk_struct(tcx, a_id, substs)) - } + let substs = if_ok!(this.substs(a_id, a_substs, b_substs)); + Ok(ty::mk_struct(tcx, a_id, substs)) } (&ty::ty_box(ref a_mt), &ty::ty_box(ref b_mt)) => { @@ -578,9 +560,8 @@ pub fn super_tys( } (&ty::ty_estr(vs_a), &ty::ty_estr(vs_b)) => { - do this.vstores(ty::terr_str, vs_a, vs_b).and_then |vs| { - Ok(ty::mk_estr(tcx,vs)) - } + let vs = if_ok!(this.vstores(ty::terr_str, vs_a, vs_b)); + Ok(ty::mk_estr(tcx,vs)) } (&ty::ty_tup(ref as_), &ty::ty_tup(ref bs)) => { diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 3f38850c8ffda..635de28194df9 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -73,6 +73,7 @@ use middle::typeck::infer::region_inference::SubSupConflict; use middle::typeck::infer::region_inference::SupSupConflict; use syntax::opt_vec::OptVec; use util::ppaux::UserString; +use util::ppaux::bound_region_to_str; use util::ppaux::note_and_explain_region; pub trait ErrorReporting { @@ -110,6 +111,13 @@ pub trait ErrorReporting { region2: Region); } +trait ErrorReportingHelpers { + fn report_inference_failure(@mut self, + var_origin: RegionVariableOrigin); + + fn note_region_origin(@mut self, + origin: SubregionOrigin); +} impl ErrorReporting for InferCtxt { fn report_region_errors(@mut self, @@ -398,10 +406,7 @@ impl ErrorReporting for InferCtxt { sub_region: Region, sup_origin: SubregionOrigin, sup_region: Region) { - self.tcx.sess.span_err( - var_origin.span(), - format!("cannot infer an appropriate lifetime \ - due to conflicting requirements")); + self.report_inference_failure(var_origin); note_and_explain_region( self.tcx, @@ -409,9 +414,7 @@ impl ErrorReporting for InferCtxt { sup_region, "..."); - self.tcx.sess.span_note( - sup_origin.span(), - format!("...due to the following expression")); + self.note_region_origin(sup_origin); note_and_explain_region( self.tcx, @@ -419,9 +422,7 @@ impl ErrorReporting for InferCtxt { sub_region, "..."); - self.tcx.sess.span_note( - sub_origin.span(), - format!("...due to the following expression")); + self.note_region_origin(sub_origin); } fn report_sup_sup_conflict(@mut self, @@ -430,10 +431,7 @@ impl ErrorReporting for InferCtxt { region1: Region, origin2: SubregionOrigin, region2: Region) { - self.tcx.sess.span_err( - var_origin.span(), - format!("cannot infer an appropriate lifetime \ - due to conflicting requirements")); + self.report_inference_failure(var_origin); note_and_explain_region( self.tcx, @@ -441,9 +439,7 @@ impl ErrorReporting for InferCtxt { region1, "..."); - self.tcx.sess.span_note( - origin1.span(), - format!("...due to the following expression")); + self.note_region_origin(origin1); note_and_explain_region( self.tcx, @@ -451,9 +447,167 @@ impl ErrorReporting for InferCtxt { region2, "..."); - self.tcx.sess.span_note( - origin2.span(), - format!("...due to the following expression")); + self.note_region_origin(origin2); + } +} + +impl ErrorReportingHelpers for InferCtxt { + fn report_inference_failure(@mut self, + var_origin: RegionVariableOrigin) { + let var_description = match var_origin { + infer::MiscVariable(_) => ~"", + infer::PatternRegion(_) => ~" for pattern", + infer::AddrOfRegion(_) => ~" for borrow expression", + infer::AddrOfSlice(_) => ~" for slice expression", + infer::Autoref(_) => ~" for autoref", + infer::Coercion(_) => ~" for automatic coercion", + infer::BoundRegionInFnCall(_, br) => { + format!(" for {}in function call", + bound_region_to_str(self.tcx, "region ", true, br)) + } + infer::BoundRegionInFnType(_, br) => { + format!(" for {}in function type", + bound_region_to_str(self.tcx, "region ", true, br)) + } + infer::BoundRegionInTypeOrImpl(_) => { + format!(" for region in type/impl") + } + infer::BoundRegionInCoherence(*) => { + format!(" for coherence check") + } + }; + + self.tcx.sess.span_err( + var_origin.span(), + format!("cannot infer an appropriate lifetime{} \ + due to conflicting requirements", + var_description)); + } + + fn note_region_origin(@mut self, + origin: SubregionOrigin) { + match origin { + infer::Subtype(ref trace) => { + let desc = match trace.origin { + infer::Misc(_) => { + format!("types are compatible") + } + infer::MethodCompatCheck(_) => { + format!("method type is compatible with trait") + } + infer::ExprAssignable(_) => { + format!("expression is assignable") + } + infer::RelateTraitRefs(_) => { + format!("traits are compatible") + } + infer::RelateSelfType(_) => { + format!("type matches impl") + } + infer::MatchExpression(_) => { + format!("match arms have compatible types") + } + infer::IfExpression(_) => { + format!("if and else have compatible types") + } + }; + + match self.values_str(&trace.values) { + Some(values_str) => { + self.tcx.sess.span_note( + trace.origin.span(), + format!("...so that {} ({})", + desc, values_str)); + } + None => { + // Really should avoid printing this error at + // all, since it is derived, but that would + // require more refactoring than I feel like + // doing right now. - nmatsakis + self.tcx.sess.span_note( + trace.origin.span(), + format!("...so that {}", desc)); + } + } + } + infer::Reborrow(span) => { + self.tcx.sess.span_note( + span, + "...so that borrowed pointer does not outlive \ + borrowed content"); + } + infer::InfStackClosure(span) => { + self.tcx.sess.span_note( + span, + "...so that closure does not outlive its stack frame"); + } + infer::InvokeClosure(span) => { + self.tcx.sess.span_note( + span, + "...so that closure is not invoked outside its lifetime"); + } + infer::DerefPointer(span) => { + self.tcx.sess.span_note( + span, + "...so that pointer is not dereferenced \ + outside its lifetime"); + } + infer::FreeVariable(span) => { + self.tcx.sess.span_note( + span, + "...so that captured variable does not outlive the \ + enclosing closure"); + } + infer::IndexSlice(span) => { + self.tcx.sess.span_note( + span, + "...so that slice is not indexed outside the lifetime"); + } + infer::RelateObjectBound(span) => { + self.tcx.sess.span_note( + span, + "...so that source pointer does not outlive \ + lifetime bound of the object type"); + } + infer::CallRcvr(span) => { + self.tcx.sess.span_note( + span, + "...so that method receiver is valid for the method call"); + } + infer::CallArg(span) => { + self.tcx.sess.span_note( + span, + "...so that argument is valid for the call"); + } + infer::CallReturn(span) => { + self.tcx.sess.span_note( + span, + "...so that return value is valid for the call"); + } + infer::AddrOf(span) => { + self.tcx.sess.span_note( + span, + "...so that borrowed pointer is valid \ + at the time of borrow"); + } + infer::AutoBorrow(span) => { + self.tcx.sess.span_note( + span, + "...so that automatically borrowed pointer is valid \ + at the time of borrow"); + } + infer::BindingTypeIsNotValidAtDecl(span) => { + self.tcx.sess.span_note( + span, + "...so that variable is valid at time of its declaration"); + } + infer::ReferenceOutlivesReferent(_, span) => { + self.tcx.sess.span_note( + span, + "...so that the pointer does not outlive the \ + data it points at"); + } + } } } diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 87c7373b005d5..9febef1c7c399 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -20,15 +20,13 @@ use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::{cres, InferCtxt}; use middle::typeck::infer::{TypeTrace, Subtype}; use middle::typeck::infer::fold_regions_in_sig; -use middle::typeck::isr_alist; use syntax::ast::{Many, Once, extern_fn, impure_fn, MutImmutable, MutMutable}; -use syntax::ast::{unsafe_fn}; +use syntax::ast::{unsafe_fn, NodeId}; use syntax::ast::{Onceness, purity}; +use std::hashmap::HashMap; use util::common::{indenter}; use util::ppaux::mt_to_str; -use extra::list; - pub struct Glb(CombineFields); // "greatest lower bound" (common subtype) impl Combine for Glb { @@ -132,14 +130,14 @@ impl Combine for Glb { let snapshot = self.infcx.region_vars.start_snapshot(); // Instantiate each bound region with a fresh region variable. - let (a_with_fresh, a_isr) = + let (a_with_fresh, a_map) = self.infcx.replace_bound_regions_with_fresh_regions( self.trace, a); - let a_vars = var_ids(self, a_isr); - let (b_with_fresh, b_isr) = + let a_vars = var_ids(self, &a_map); + let (b_with_fresh, b_map) = self.infcx.replace_bound_regions_with_fresh_regions( self.trace, b); - let b_vars = var_ids(self, b_isr); + let b_vars = var_ids(self, &b_map); // Collect constraints. let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); @@ -152,20 +150,23 @@ impl Combine for Glb { fold_regions_in_sig( self.infcx.tcx, &sig0, - |r, _in_fn| generalize_region(self, snapshot, - new_vars, a_isr, a_vars, b_vars, - r)); + |r| generalize_region(self, snapshot, + new_vars, sig0.binder_id, + &a_map, a_vars, b_vars, + r)); debug!("sig1 = {}", sig1.inf_str(self.infcx)); return Ok(sig1); fn generalize_region(this: &Glb, snapshot: uint, new_vars: &[RegionVid], - a_isr: isr_alist, + new_binder_id: NodeId, + a_map: &HashMap, a_vars: &[RegionVid], b_vars: &[RegionVid], r0: ty::Region) -> ty::Region { if !is_var_in_set(new_vars, r0) { + assert!(!r0.is_bound()); return r0; } @@ -177,13 +178,13 @@ impl Combine for Glb { for r in tainted.iter() { if is_var_in_set(a_vars, *r) { if a_r.is_some() { - return fresh_bound_variable(this); + return fresh_bound_variable(this, new_binder_id); } else { a_r = Some(*r); } } else if is_var_in_set(b_vars, *r) { if b_r.is_some() { - return fresh_bound_variable(this); + return fresh_bound_variable(this, new_binder_id); } else { b_r = Some(*r); } @@ -192,57 +193,57 @@ impl Combine for Glb { } } - // NB---I do not believe this algorithm computes - // (necessarily) the GLB. As written it can - // spuriously fail. In particular, if there is a case - // like: &fn(fn(&a)) and fn(fn(&b)), where a and b are - // free, it will return fn(&c) where c = GLB(a,b). If - // however this GLB is not defined, then the result is - // an error, even though something like - // "fn(fn(&X))" where X is bound would be a - // subtype of both of those. - // - // The problem is that if we were to return a bound - // variable, we'd be computing a lower-bound, but not - // necessarily the *greatest* lower-bound. + // NB---I do not believe this algorithm computes + // (necessarily) the GLB. As written it can + // spuriously fail. In particular, if there is a case + // like: &fn(fn(&a)) and fn(fn(&b)), where a and b are + // free, it will return fn(&c) where c = GLB(a,b). If + // however this GLB is not defined, then the result is + // an error, even though something like + // "fn(fn(&X))" where X is bound would be a + // subtype of both of those. + // + // The problem is that if we were to return a bound + // variable, we'd be computing a lower-bound, but not + // necessarily the *greatest* lower-bound. + // + // Unfortunately, this problem is non-trivial to solve, + // because we do not know at the time of computing the GLB + // whether a GLB(a,b) exists or not, because we haven't + // run region inference (or indeed, even fully computed + // the region hierarchy!). The current algorithm seems to + // works ok in practice. if a_r.is_some() && b_r.is_some() && only_new_vars { // Related to exactly one bound variable from each fn: - return rev_lookup(this, a_isr, a_r.unwrap()); + return rev_lookup(this, a_map, new_binder_id, a_r.unwrap()); } else if a_r.is_none() && b_r.is_none() { // Not related to bound variables from either fn: + assert!(!r0.is_bound()); return r0; } else { // Other: - return fresh_bound_variable(this); + return fresh_bound_variable(this, new_binder_id); } } fn rev_lookup(this: &Glb, - a_isr: isr_alist, + a_map: &HashMap, + new_binder_id: NodeId, r: ty::Region) -> ty::Region { - let mut ret = None; - do list::each(a_isr) |pair| { - let (a_br, a_r) = *pair; - if a_r == r { - ret = Some(ty::re_bound(a_br)); - false - } else { - true + for (a_br, a_r) in a_map.iter() { + if *a_r == r { + return ty::ReLateBound(new_binder_id, *a_br); } - }; - - match ret { - Some(x) => x, - None => this.infcx.tcx.sess.span_bug( - this.trace.origin.span(), - format!("could not find original bound region for {:?}", r)) } + this.infcx.tcx.sess.span_bug( + this.trace.origin.span(), + format!("could not find original bound region for {:?}", r)) } - fn fresh_bound_variable(this: &Glb) -> ty::Region { - this.infcx.region_vars.new_bound() + fn fresh_bound_variable(this: &Glb, binder_id: NodeId) -> ty::Region { + this.infcx.region_vars.new_bound(binder_id) } } } diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 8a32a305b3a0e..04bf5fda7258d 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -35,7 +35,6 @@ use middle::ty::{RegionVid, TyVar, Vid}; use middle::ty; -use middle::typeck::isr_alist; use middle::typeck::infer::*; use middle::typeck::infer::combine::*; use middle::typeck::infer::glb::Glb; @@ -43,10 +42,9 @@ use middle::typeck::infer::lub::Lub; use middle::typeck::infer::unify::*; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::to_str::InferStr; +use std::hashmap::HashMap; use util::common::indenter; -use extra::list; - pub trait LatticeValue { fn sub(cf: &CombineFields, a: &Self, b: &Self) -> ures; fn lub(cf: &CombineFields, a: &Self, b: &Self) -> cres; @@ -366,14 +364,13 @@ impl TyLatticeDir for Glb { } } -pub fn super_lattice_tys( - this: &L, - a: ty::t, - b: ty::t) -> cres { +pub fn super_lattice_tys(this: &L, + a: ty::t, + b: ty::t) + -> cres { debug!("{}.lattice_tys({}, {})", this.tag(), a.inf_str(this.infcx()), b.inf_str(this.infcx())); - let _r = indenter(); if a == b { return Ok(a); @@ -524,25 +521,22 @@ pub fn lattice_var_and_t(this: &T, isr: isr_alist) -> ~[RegionVid] { - let mut result = ~[]; - do list::each(isr) |pair| { - match pair.second() { - ty::re_infer(ty::ReVar(r)) => { result.push(r); } +pub fn var_ids(this: &T, + map: &HashMap) + -> ~[RegionVid] { + map.iter().map(|(_, r)| match *r { + ty::ReInfer(ty::ReVar(r)) => { r } r => { this.infcx().tcx.sess.span_bug( this.trace().origin.span(), format!("Found non-region-vid: {:?}", r)); } - } - true - }; - result + }).collect() } pub fn is_var_in_set(new_vars: &[RegionVid], r: ty::Region) -> bool { match r { - ty::re_infer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v), + ty::ReInfer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v), _ => false } } diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 42793d956df06..b826310df7bff 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -20,13 +20,11 @@ use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::{cres, InferCtxt}; use middle::typeck::infer::fold_regions_in_sig; use middle::typeck::infer::{TypeTrace, Subtype}; -use middle::typeck::isr_alist; -use util::ppaux::mt_to_str; - -use extra::list; -use syntax::ast::{Many, Once, extern_fn, impure_fn}; +use std::hashmap::HashMap; +use syntax::ast::{Many, Once, extern_fn, impure_fn, NodeId}; use syntax::ast::{unsafe_fn}; use syntax::ast::{Onceness, purity}; +use util::ppaux::mt_to_str; pub struct Lub(CombineFields); // least-upper-bound: common supertype @@ -125,7 +123,7 @@ impl Combine for Lub { let snapshot = self.infcx.region_vars.start_snapshot(); // Instantiate each bound region with a fresh region variable. - let (a_with_fresh, a_isr) = + let (a_with_fresh, a_map) = self.infcx.replace_bound_regions_with_fresh_regions( self.trace, a); let (b_with_fresh, _) = @@ -143,17 +141,20 @@ impl Combine for Lub { fold_regions_in_sig( self.infcx.tcx, &sig0, - |r, _in_fn| generalize_region(self, snapshot, new_vars, - a_isr, r)); + |r| generalize_region(self, snapshot, new_vars, + sig0.binder_id, &a_map, r)); return Ok(sig1); fn generalize_region(this: &Lub, snapshot: uint, new_vars: &[RegionVid], - a_isr: isr_alist, - r0: ty::Region) -> ty::Region { + new_scope: NodeId, + a_map: &HashMap, + r0: ty::Region) + -> ty::Region { // Regions that pre-dated the LUB computation stay as they are. if !is_var_in_set(new_vars, r0) { + assert!(!r0.is_bound()); debug!("generalize_region(r0={:?}): not new variable", r0); return r0; } @@ -167,6 +168,7 @@ impl Combine for Lub { debug!("generalize_region(r0={:?}): \ non-new-variables found in {:?}", r0, tainted); + assert!(!r0.is_bound()); return r0; } @@ -175,27 +177,19 @@ impl Combine for Lub { // in both A and B. Replace the variable with the "first" // bound region from A that we find it to be associated // with. - let mut ret = None; - do list::each(a_isr) |pair| { - let (a_br, a_r) = *pair; - if tainted.iter().any(|x| x == &a_r) { + for (a_br, a_r) in a_map.iter() { + if tainted.iter().any(|x| x == a_r) { debug!("generalize_region(r0={:?}): \ replacing with {:?}, tainted={:?}", - r0, a_br, tainted); - ret = Some(ty::re_bound(a_br)); - false - } else { - true + r0, *a_br, tainted); + return ty::ReLateBound(new_scope, *a_br); } - }; - - match ret { - Some(x) => x, - None => this.infcx.tcx.sess.span_bug( - this.trace.origin.span(), - format!("Region {:?} is not associated with \ - any bound region from A!", r0)) } + + this.infcx.tcx.sess.span_bug( + this.trace.origin.span(), + format!("Region {:?} is not associated with \ + any bound region from A!", r0)) } } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 487eb4c32890e..eafc7e262f191 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -20,8 +20,11 @@ pub use middle::typeck::infer::resolve::{resolve_ivar, resolve_all}; pub use middle::typeck::infer::resolve::{resolve_nested_tvar}; pub use middle::typeck::infer::resolve::{resolve_rvar}; +use extra::smallintmap::SmallIntMap; use middle::ty::{TyVid, IntVid, FloatVid, RegionVid, Vid}; use middle::ty; +use middle::ty_fold; +use middle::ty_fold::TypeFolder; use middle::typeck::check::regionmanip::{replace_bound_regions_in_fn_sig}; use middle::typeck::infer::coercion::Coerce; use middle::typeck::infer::combine::{Combine, CombineFields, eq_tys}; @@ -32,19 +35,16 @@ use middle::typeck::infer::lub::Lub; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::unify::{ValsAndBindings, Root}; use middle::typeck::infer::error_reporting::ErrorReporting; -use middle::typeck::isr_alist; -use util::common::indent; -use util::ppaux::{bound_region_to_str, ty_to_str, trait_ref_to_str, Repr, - UserString}; - +use std::hashmap::HashMap; use std::result; use std::vec; -use extra::list::Nil; -use extra::smallintmap::SmallIntMap; use syntax::ast::{MutImmutable, MutMutable}; use syntax::ast; use syntax::codemap; use syntax::codemap::Span; +use util::common::indent; +use util::ppaux::{bound_region_to_str, ty_to_str, trait_ref_to_str, Repr, + UserString}; pub mod doc; pub mod macros; @@ -216,17 +216,15 @@ pub enum RegionVariableOrigin { // Region variables created for bound regions // in a function or method that is called - BoundRegionInFnCall(Span, ty::bound_region), + BoundRegionInFnCall(Span, ty::BoundRegion), // Region variables created for bound regions // when doing subtyping/lub/glb computations - BoundRegionInFnType(Span, ty::bound_region), + BoundRegionInFnType(Span, ty::BoundRegion), BoundRegionInTypeOrImpl(Span), BoundRegionInCoherence, - - BoundRegionError(Span), } pub enum fixup_err { @@ -568,15 +566,16 @@ impl InferCtxt { /// Execute `f`, unroll bindings on failure pub fn try(@mut self, f: &fn() -> Result) -> Result { debug!("try()"); - do indent { - let snapshot = self.start_snapshot(); - let r = f(); - match r { - Ok(_) => (), - Err(_) => self.rollback_to(&snapshot) + let snapshot = self.start_snapshot(); + let r = f(); + match r { + Ok(_) => { debug!("success"); } + Err(ref e) => { + debug!("error: {:?}", *e); + self.rollback_to(&snapshot) } - r } + r } /// Execute `f` then unroll any bindings it creates @@ -639,7 +638,18 @@ impl InferCtxt { } pub fn next_region_var(&mut self, origin: RegionVariableOrigin) -> ty::Region { - ty::re_infer(ty::ReVar(self.region_vars.new_region_var(origin))) + ty::ReInfer(ty::ReVar(self.region_vars.new_region_var(origin))) + } + + pub fn next_region_vars(&mut self, + origin: RegionVariableOrigin, + count: uint) + -> ~[ty::Region] { + vec::from_fn(count, |_| self.next_region_var(origin)) + } + + pub fn fresh_bound_region(&mut self, binder_id: ast::NodeId) -> ty::Region { + self.region_vars.new_bound(binder_id) } pub fn resolve_regions(@mut self) { @@ -787,9 +797,11 @@ impl InferCtxt { pub fn replace_bound_regions_with_fresh_regions(&mut self, trace: TypeTrace, fsig: &ty::FnSig) - -> (ty::FnSig, isr_alist) { - let(isr, _, fn_sig) = - replace_bound_regions_in_fn_sig(self.tcx, @Nil, None, fsig, |br| { + -> (ty::FnSig, + HashMap) { + let (map, _, fn_sig) = + replace_bound_regions_in_fn_sig(self.tcx, None, fsig, |br| { let rvar = self.next_region_var( BoundRegionInFnType(trace.origin.span(), br)); debug!("Bound region {} maps to {:?}", @@ -797,18 +809,16 @@ impl InferCtxt { rvar); rvar }); - (fn_sig, isr) + (fn_sig, map) } } pub fn fold_regions_in_sig( tcx: ty::ctxt, fn_sig: &ty::FnSig, - fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig + fldr: &fn(r: ty::Region) -> ty::Region) -> ty::FnSig { - do ty::fold_sig(fn_sig) |t| { - ty::fold_regions(tcx, t, |r, in_fn| fldr(r, in_fn)) - } + ty_fold::RegionFolder::regions(tcx, fldr).fold_sig(fn_sig) } impl TypeTrace { @@ -910,7 +920,6 @@ impl RegionVariableOrigin { BoundRegionInFnType(a, _) => a, BoundRegionInTypeOrImpl(a) => a, BoundRegionInCoherence => codemap::dummy_sp(), - BoundRegionError(a) => a, } } } @@ -924,14 +933,13 @@ impl Repr for RegionVariableOrigin { AddrOfSlice(a) => format!("AddrOfSlice({})", a.repr(tcx)), Autoref(a) => format!("Autoref({})", a.repr(tcx)), Coercion(a) => format!("Coercion({})", a.repr(tcx)), - BoundRegionInFnCall(a, b) => format!("BoundRegionInFnCall({},{})", + BoundRegionInFnCall(a, b) => format!("bound_regionInFnCall({},{})", a.repr(tcx), b.repr(tcx)), - BoundRegionInFnType(a, b) => format!("BoundRegionInFnType({},{})", + BoundRegionInFnType(a, b) => format!("bound_regionInFnType({},{})", a.repr(tcx), b.repr(tcx)), - BoundRegionInTypeOrImpl(a) => format!("BoundRegionInTypeOrImpl({})", + BoundRegionInTypeOrImpl(a) => format!("bound_regionInTypeOrImpl({})", a.repr(tcx)), - BoundRegionInCoherence => format!("BoundRegionInCoherence"), - BoundRegionError(a) => format!("BoundRegionError({})", a.repr(tcx)), + BoundRegionInCoherence => format!("bound_regionInCoherence"), } } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index 68c5ec3b7d66d..e613aa4ba28de 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -13,8 +13,9 @@ use middle::ty; use middle::ty::{FreeRegion, Region, RegionVid}; -use middle::ty::{re_empty, re_static, re_infer, re_free, re_bound}; -use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh}; +use middle::ty::{ReEmpty, ReStatic, ReInfer, ReFree, ReEarlyBound, + ReLateBound}; +use middle::ty::{ReScope, ReVar, ReSkolemized, BrFresh}; use middle::typeck::infer::cres; use middle::typeck::infer::{RegionVariableOrigin, SubregionOrigin}; use middle::typeck::infer; @@ -186,30 +187,39 @@ impl RegionVarBindings { return vid; } - pub fn new_skolemized(&mut self, br: ty::bound_region) -> Region { + pub fn new_skolemized(&mut self, br: ty::BoundRegion) -> Region { let sc = self.skolemization_count; self.skolemization_count += 1; - re_infer(ReSkolemized(sc, br)) + ReInfer(ReSkolemized(sc, br)) } - pub fn new_bound(&mut self) -> Region { + pub fn new_bound(&mut self, binder_id: ast::NodeId) -> Region { // Creates a fresh bound variable for use in GLB computations. // See discussion of GLB computation in the large comment at // the top of this file for more details. // - // This computation is mildly wrong in the face of rollover. - // It's conceivable, if unlikely, that one might wind up with - // accidental capture for nested functions in that case, if - // the outer function had bound regions created a very long - // time before and the inner function somehow wound up rolling - // over such that supposedly fresh identifiers were in fact - // shadowed. We should convert our bound_region - // representation to use deBruijn indices or something like - // that to eliminate that possibility. + // This computation is potentially wrong in the face of + // rollover. It's conceivable, if unlikely, that one might + // wind up with accidental capture for nested functions in + // that case, if the outer function had bound regions created + // a very long time before and the inner function somehow + // wound up rolling over such that supposedly fresh + // identifiers were in fact shadowed. For now, we just assert + // that there is no rollover -- eventually we should try to be + // robust against this possibility, either by checking the set + // of bound identifiers that appear in a given expression and + // ensure that we generate one that is distinct, or by + // changing the representation of bound regions in a fn + // declaration let sc = self.bound_count; self.bound_count += 1; - re_bound(br_fresh(sc)) + + if sc >= self.bound_count { + self.tcx.sess.bug("Rollover in RegionInference new_bound()"); + } + + ReLateBound(binder_id, BrFresh(sc)) } pub fn add_constraint(&mut self, @@ -236,25 +246,25 @@ impl RegionVarBindings { debug!("RegionVarBindings: make_subregion({:?}, {:?})", sub, sup); match (sub, sup) { - (re_infer(ReVar(sub_id)), re_infer(ReVar(sup_id))) => { + (ReEarlyBound(*), _) | + (ReLateBound(*), _) | + (_, ReEarlyBound(*)) | + (_, ReLateBound(*)) => { + self.tcx.sess.span_bug( + origin.span(), + format!("Cannot relate bound region: {} <= {}", + sub.repr(self.tcx), + sup.repr(self.tcx))); + } + (ReInfer(ReVar(sub_id)), ReInfer(ReVar(sup_id))) => { self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin); } - (r, re_infer(ReVar(sup_id))) => { + (r, ReInfer(ReVar(sup_id))) => { self.add_constraint(ConstrainRegSubVar(r, sup_id), origin); } - (re_infer(ReVar(sub_id)), r) => { + (ReInfer(ReVar(sub_id)), r) => { self.add_constraint(ConstrainVarSubReg(sub_id, r), origin); } - (re_bound(br), _) => { - self.tcx.sess.span_bug( - origin.span(), - format!("Cannot relate bound region as subregion: {:?}", br)); - } - (_, re_bound(br)) => { - self.tcx.sess.span_bug( - origin.span(), - format!("Cannot relate bound region as superregion: {:?}", br)); - } _ => { self.add_constraint(ConstrainRegSubReg(sub, sup), origin); } @@ -271,8 +281,8 @@ impl RegionVarBindings { debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); match (a, b) { - (re_static, _) | (_, re_static) => { - re_static // nothing lives longer than static + (ReStatic, _) | (_, ReStatic) => { + ReStatic // nothing lives longer than static } _ => { @@ -294,7 +304,7 @@ impl RegionVarBindings { debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); match (a, b) { - (re_static, r) | (r, re_static) => { + (ReStatic, r) | (r, ReStatic) => { // static lives longer than everything else r } @@ -323,13 +333,13 @@ impl RegionVarBindings { Value(r) => r, NoValue => { - // No constraints, return ty::re_empty - re_empty + // No constraints, return ty::ReEmpty + ReEmpty } ErrorValue => { // An error that has previously been reported. - re_static + ReStatic } } } @@ -356,7 +366,7 @@ impl RegionVarBindings { let vars = TwoRegions { a: a, b: b }; match self.combine_map(t).find(&vars) { Some(&c) => { - return re_infer(ReVar(c)); + return ReInfer(ReVar(c)); } None => {} } @@ -365,10 +375,10 @@ impl RegionVarBindings { if self.in_snapshot() { self.undo_log.push(AddCombination(t, vars)); } - relate(self, a, re_infer(ReVar(c))); - relate(self, b, re_infer(ReVar(c))); + relate(self, a, ReInfer(ReVar(c))); + relate(self, b, ReInfer(ReVar(c))); debug!("combine_vars() c={:?}", c); - re_infer(ReVar(c)) + ReInfer(ReVar(c)) } pub fn vars_created_since_snapshot(&mut self, snapshot: uint) @@ -411,14 +421,14 @@ impl RegionVarBindings { // nb: can't use uint::range() here as we move result_set let regs = match self.undo_log[undo_index] { AddConstraint(ConstrainVarSubVar(ref a, ref b)) => { - Some((re_infer(ReVar(*a)), - re_infer(ReVar(*b)))) + Some((ReInfer(ReVar(*a)), + ReInfer(ReVar(*b)))) } AddConstraint(ConstrainRegSubVar(ref a, ref b)) => { - Some((*a, re_infer(ReVar(*b)))) + Some((*a, ReInfer(ReVar(*b)))) } AddConstraint(ConstrainVarSubReg(ref a, ref b)) => { - Some((re_infer(ReVar(*a)), *b)) + Some((ReInfer(ReVar(*a)), *b)) } AddConstraint(ConstrainRegSubReg(a, b)) => { Some((a, b)) @@ -485,23 +495,33 @@ impl RegionVarBindings { fn lub_concrete_regions(&self, a: Region, b: Region) -> Region { match (a, b) { - (re_static, _) | (_, re_static) => { - re_static // nothing lives longer than static + (ReLateBound(*), _) | + (_, ReLateBound(*)) | + (ReEarlyBound(*), _) | + (_, ReEarlyBound(*)) => { + self.tcx.sess.bug( + format!("Cannot relate bound region: LUB({}, {})", + a.repr(self.tcx), + b.repr(self.tcx))); } - (re_empty, r) | (r, re_empty) => { + (ReStatic, _) | (_, ReStatic) => { + ReStatic // nothing lives longer than static + } + + (ReEmpty, r) | (r, ReEmpty) => { r // everything lives longer than empty } - (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => { + (ReInfer(ReVar(v_id)), _) | (_, ReInfer(ReVar(v_id))) => { self.tcx.sess.span_bug( self.var_origins[v_id.to_uint()].span(), format!("lub_concrete_regions invoked with \ non-concrete regions: {:?}, {:?}", a, b)); } - (f @ re_free(ref fr), re_scope(s_id)) | - (re_scope(s_id), f @ re_free(ref fr)) => { + (f @ ReFree(ref fr), ReScope(s_id)) | + (ReScope(s_id), f @ ReFree(ref fr)) => { // A "free" region can be interpreted as "some region // at least as big as the block fr.scope_id". So, we can // reasonably compare free regions and scopes: @@ -514,35 +534,30 @@ impl RegionVarBindings { // otherwise, we don't know what the free region is, // so we must conservatively say the LUB is static: - _ => re_static + _ => ReStatic } } - (re_scope(a_id), re_scope(b_id)) => { + (ReScope(a_id), ReScope(b_id)) => { // The region corresponding to an outer block is a // subtype of the region corresponding to an inner // block. let rm = self.tcx.region_maps; match rm.nearest_common_ancestor(a_id, b_id) { - Some(r_id) => re_scope(r_id), - _ => re_static + Some(r_id) => ReScope(r_id), + _ => ReStatic } } - (re_free(ref a_fr), re_free(ref b_fr)) => { + (ReFree(ref a_fr), ReFree(ref b_fr)) => { self.lub_free_regions(a_fr, b_fr) } // For these types, we cannot define any additional // relationship: - (re_infer(ReSkolemized(*)), _) | - (_, re_infer(ReSkolemized(*))) | - (re_bound(_), re_bound(_)) | - (re_bound(_), re_free(_)) | - (re_bound(_), re_scope(_)) | - (re_free(_), re_bound(_)) | - (re_scope(_), re_bound(_)) => { - if a == b {a} else {re_static} + (ReInfer(ReSkolemized(*)), _) | + (_, ReInfer(ReSkolemized(*))) => { + if a == b {a} else {ReStatic} } } } @@ -560,7 +575,7 @@ impl RegionVarBindings { return match a.cmp(b) { Less => helper(self, a, b), Greater => helper(self, b, a), - Equal => ty::re_free(*a) + Equal => ty::ReFree(*a) }; fn helper(this: &RegionVarBindings, @@ -569,11 +584,11 @@ impl RegionVarBindings { { let rm = this.tcx.region_maps; if rm.sub_free_region(*a, *b) { - ty::re_free(*b) + ty::ReFree(*b) } else if rm.sub_free_region(*b, *a) { - ty::re_free(*a) + ty::ReFree(*a) } else { - ty::re_static + ty::ReStatic } } } @@ -584,26 +599,36 @@ impl RegionVarBindings { -> cres { debug!("glb_concrete_regions({:?}, {:?})", a, b); match (a, b) { - (re_static, r) | (r, re_static) => { + (ReLateBound(*), _) | + (_, ReLateBound(*)) | + (ReEarlyBound(*), _) | + (_, ReEarlyBound(*)) => { + self.tcx.sess.bug( + format!("Cannot relate bound region: GLB({}, {})", + a.repr(self.tcx), + b.repr(self.tcx))); + } + + (ReStatic, r) | (r, ReStatic) => { // static lives longer than everything else Ok(r) } - (re_empty, _) | (_, re_empty) => { + (ReEmpty, _) | (_, ReEmpty) => { // nothing lives shorter than everything else - Ok(re_empty) + Ok(ReEmpty) } - (re_infer(ReVar(v_id)), _) | - (_, re_infer(ReVar(v_id))) => { + (ReInfer(ReVar(v_id)), _) | + (_, ReInfer(ReVar(v_id))) => { self.tcx.sess.span_bug( self.var_origins[v_id.to_uint()].span(), format!("glb_concrete_regions invoked with \ non-concrete regions: {:?}, {:?}", a, b)); } - (re_free(ref fr), s @ re_scope(s_id)) | - (s @ re_scope(s_id), re_free(ref fr)) => { + (ReFree(ref fr), s @ ReScope(s_id)) | + (s @ ReScope(s_id), ReFree(ref fr)) => { // Free region is something "at least as big as // `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger // than the scope `s_id`, then we can say that the GLB @@ -616,23 +641,18 @@ impl RegionVarBindings { } } - (re_scope(a_id), re_scope(b_id)) => { + (ReScope(a_id), ReScope(b_id)) => { self.intersect_scopes(a, b, a_id, b_id) } - (re_free(ref a_fr), re_free(ref b_fr)) => { + (ReFree(ref a_fr), ReFree(ref b_fr)) => { self.glb_free_regions(a_fr, b_fr) } // For these types, we cannot define any additional // relationship: - (re_infer(ReSkolemized(*)), _) | - (_, re_infer(ReSkolemized(*))) | - (re_bound(_), re_bound(_)) | - (re_bound(_), re_free(_)) | - (re_bound(_), re_scope(_)) | - (re_free(_), re_bound(_)) | - (re_scope(_), re_bound(_)) => { + (ReInfer(ReSkolemized(*)), _) | + (_, ReInfer(ReSkolemized(*))) => { if a == b { Ok(a) } else { @@ -655,7 +675,7 @@ impl RegionVarBindings { return match a.cmp(b) { Less => helper(self, a, b), Greater => helper(self, b, a), - Equal => Ok(ty::re_free(*a)) + Equal => Ok(ty::ReFree(*a)) }; fn helper(this: &RegionVarBindings, @@ -664,11 +684,11 @@ impl RegionVarBindings { { let rm = this.tcx.region_maps; if rm.sub_free_region(*a, *b) { - Ok(ty::re_free(*a)) + Ok(ty::ReFree(*a)) } else if rm.sub_free_region(*b, *a) { - Ok(ty::re_free(*b)) + Ok(ty::ReFree(*b)) } else { - this.intersect_scopes(ty::re_free(*a), ty::re_free(*b), + this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b), a.scope_id, b.scope_id) } } @@ -695,8 +715,8 @@ impl RegionVarBindings { scope_a, scope_b, region_a, region_b); let rm = self.tcx.region_maps; match rm.nearest_common_ancestor(scope_a, scope_b) { - Some(r_id) if scope_a == r_id => Ok(re_scope(scope_b)), - Some(r_id) if scope_b == r_id => Ok(re_scope(scope_a)), + Some(r_id) if scope_a == r_id => Ok(ReScope(scope_b)), + Some(r_id) if scope_b == r_id => Ok(ReScope(scope_a)), _ => Err(ty::terr_regions_no_overlap(region_a, region_b)) } } diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index 564fcb76dc73b..168d8a57c7fec 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -50,6 +50,7 @@ use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid}; use middle::ty::{type_is_bot, IntType, UintType}; use middle::ty; +use middle::ty_fold; use middle::typeck::infer::{Bounds, cyclic_ty, fixup_err, fres, InferCtxt}; use middle::typeck::infer::{region_var_bound_by_region_var, unresolved_ty}; use middle::typeck::infer::to_str::InferStr; @@ -96,6 +97,20 @@ pub fn resolver(infcx: @mut InferCtxt, modes: uint) -> ResolveState { } } +impl ty_fold::TypeFolder for ResolveState { + fn tcx(&self) -> ty::ctxt { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: ty::t) -> ty::t { + self.resolve_type(t) + } + + fn fold_region(&mut self, r: ty::Region) -> ty::Region { + self.resolve_region(r) + } +} + impl ResolveState { pub fn should(&mut self, mode: uint) -> bool { (self.modes & mode) == mode @@ -166,11 +181,7 @@ impl ResolveState { typ } else { self.type_depth += 1; - let result = ty::fold_regions_and_ty( - self.infcx.tcx, typ, - |r| self.resolve_region(r), - |t| self.resolve_type(t), - |t| self.resolve_type(t)); + let result = ty_fold::super_fold_ty(self, typ); self.type_depth -= 1; result } @@ -181,21 +192,21 @@ impl ResolveState { pub fn resolve_region(&mut self, orig: ty::Region) -> ty::Region { debug!("Resolve_region({})", orig.inf_str(self.infcx)); match orig { - ty::re_infer(ty::ReVar(rid)) => self.resolve_region_var(rid), + ty::ReInfer(ty::ReVar(rid)) => self.resolve_region_var(rid), _ => orig } } pub fn resolve_region_var(&mut self, rid: RegionVid) -> ty::Region { if !self.should(resolve_rvar) { - return ty::re_infer(ty::ReVar(rid)); + return ty::ReInfer(ty::ReVar(rid)); } self.infcx.region_vars.resolve_var(rid) } pub fn assert_not_rvar(&mut self, rid: RegionVid, r: ty::Region) { match r { - ty::re_infer(ty::ReVar(rid2)) => { + ty::ReInfer(ty::ReVar(rid2)) => { self.err = Some(region_var_bound_by_region_var(rid, rid2)); } _ => { } diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index e5afefe0c7164..117b100005a01 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -24,8 +24,6 @@ use middle::typeck::infer::{TypeTrace, Subtype}; use util::common::{indenter}; use util::ppaux::bound_region_to_str; -use extra::list::Nil; -use extra::list; use syntax::ast::{Onceness, purity}; pub struct Sub(CombineFields); // "subtype", "subregion" etc @@ -168,9 +166,8 @@ impl Combine for Sub { // Second, we instantiate each bound region in the supertype with a // fresh concrete region. - let (skol_isr, _, b_sig) = { - do replace_bound_regions_in_fn_sig(self.infcx.tcx, @Nil, - None, b) |br| { + let (skol_map, _, b_sig) = { + do replace_bound_regions_in_fn_sig(self.infcx.tcx, None, b) |br| { let skol = self.infcx.region_vars.new_skolemized(br); debug!("Bound region {} skolemized to {:?}", bound_region_to_str(self.infcx.tcx, "", false, br), @@ -189,16 +186,13 @@ impl Combine for Sub { // that the skolemized regions do not "leak". let new_vars = self.infcx.region_vars.vars_created_since_snapshot(snapshot); - - let mut ret = Ok(sig); - do list::each(skol_isr) |pair| { - let (skol_br, skol) = *pair; + for (&skol_br, &skol) in skol_map.iter() { let tainted = self.infcx.region_vars.tainted(snapshot, skol); for tainted_region in tainted.iter() { // Each skolemized should only be relatable to itself // or new variables: match *tainted_region { - ty::re_infer(ty::ReVar(ref vid)) => { + ty::ReInfer(ty::ReVar(ref vid)) => { if new_vars.iter().any(|x| x == vid) { continue; } } _ => { @@ -208,19 +202,16 @@ impl Combine for Sub { // A is not as polymorphic as B: if self.a_is_expected { - ret = Err(ty::terr_regions_insufficiently_polymorphic( - skol_br, *tainted_region)); - break + return Err(ty::terr_regions_insufficiently_polymorphic( + skol_br, *tainted_region)); } else { - ret = Err(ty::terr_regions_overly_polymorphic( - skol_br, *tainted_region)); - break + return Err(ty::terr_regions_overly_polymorphic( + skol_br, *tainted_region)); } } - ret.is_ok() - }; + } - ret + return Ok(sig); } } diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index d7a00ebf0e940..7c56594d26755 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -188,7 +188,7 @@ impl Env { meta: FnMeta {purity: ast::impure_fn, proto: ast::ProtoBare, onceness: ast::Many, - region: ty::re_static, + region: ty::ReStatic, bounds: @~[]}, sig: FnSig { inputs: inputs, @@ -203,22 +203,22 @@ impl Env { } pub fn t_rptr_bound(&self, id: uint) -> ty::t { - ty::mk_imm_rptr(self.tcx, ty::re_bound(ty::br_anon(id)), self.t_int()) + ty::mk_imm_rptr(self.tcx, ty::re_bound(ty::BrAnon(id)), self.t_int()) } pub fn t_rptr_scope(&self, id: ast::node_id) -> ty::t { - ty::mk_imm_rptr(self.tcx, ty::re_scope(id), self.t_int()) + ty::mk_imm_rptr(self.tcx, ty::ReScope(id), self.t_int()) } pub fn t_rptr_free(&self, nid: ast::node_id, id: uint) -> ty::t { ty::mk_imm_rptr(self.tcx, - ty::re_free(ty::FreeRegion {scope_id: nid, - bound_region: ty::br_anon(id)}), + ty::ReFree(ty::FreeRegion {scope_id: nid, + bound_region: ty::BrAnon(id)}), self.t_int()) } pub fn t_rptr_static(&self) -> ty::t { - ty::mk_imm_rptr(self.tcx, ty::re_static, self.t_int()) + ty::mk_imm_rptr(self.tcx, ty::ReStatic, self.t_int()) } pub fn lub() -> Lub { Lub(self.infcx.combine_fields(true, dummy_sp())) } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 75c7adfb03e40..63ddc9addc55e 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -19,16 +19,23 @@ The type checker is responsible for: 3. Guaranteeing that most type rules are met ("most?", you say, "why most?" Well, dear reader, read on) -The main entry point is `check_crate()`. Type checking operates in two major -phases: collect and check. The collect phase passes over all items and -determines their type, without examining their "innards". The check phase -then checks function bodies and so forth. +The main entry point is `check_crate()`. Type checking operates in +several major phases: -Within the check phase, we check each function body one at a time (bodies of -function expressions are checked as part of the containing function). -Inference is used to supply types wherever they are unknown. The actual -checking of a function itself has several phases (check, regionck, writeback), -as discussed in the documentation for the `check` module. +1. The collect phase first passes over all items and determines their + type, without examining their "innards". + +2. Variance inference then runs to compute the variance of each parameter + +3. Coherence checks for overlapping or orphaned impls + +4. Finally, the check phase then checks function bodies and so forth. + Within the check phase, we check each function body one at a time + (bodies of function expressions are checked as part of the + containing function). Inference is used to supply types wherever + they are unknown. The actual checking of a function itself has + several phases (check, regionck, writeback), as discussed in the + documentation for the `check` module. The type checker is defined into various submodules which are documented independently: @@ -39,6 +46,10 @@ independently: - collect: computes the types of each top-level item and enters them into the `cx.tcache` table for later use +- coherence: enforces coherence rules, builds some tables + +- variance: variance inference + - check: walks over function bodies and type checks them, inferring types for local variables, type parameters, etc as necessary. @@ -64,7 +75,6 @@ use extra::list; use syntax::codemap::Span; use syntax::print::pprust::*; use syntax::{ast, ast_map, abi}; -use syntax::opt_vec; pub mod check; pub mod rscope; @@ -72,6 +82,7 @@ pub mod astconv; pub mod infer; pub mod collect; pub mod coherence; +pub mod variance; #[deriving(Clone, Encodable, Decodable, Eq, Ord)] pub enum param_index { @@ -266,7 +277,7 @@ pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId) pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty { ty::ty_param_bounds_and_ty { generics: ty::Generics {type_param_defs: @~[], - region_param: None}, + region_param_defs: @[]}, ty: t } } @@ -306,19 +317,19 @@ pub fn require_same_types( // a list of mapping from in-scope-region-names ("isr") to the // corresponding ty::Region -pub type isr_alist = @List<(ty::bound_region, ty::Region)>; +pub type isr_alist = @List<(ty::BoundRegion, ty::Region)>; trait get_and_find_region { - fn get(&self, br: ty::bound_region) -> ty::Region; - fn find(&self, br: ty::bound_region) -> Option; + fn get(&self, br: ty::BoundRegion) -> ty::Region; + fn find(&self, br: ty::BoundRegion) -> Option; } impl get_and_find_region for isr_alist { - fn get(&self, br: ty::bound_region) -> ty::Region { + fn get(&self, br: ty::BoundRegion) -> ty::Region { self.find(br).unwrap() } - fn find(&self, br: ty::bound_region) -> Option { + fn find(&self, br: ty::BoundRegion) -> Option { let mut ret = None; do list::each(*self) |isr| { let (isr_br, isr_r) = *isr; @@ -354,7 +365,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt, purity: ast::impure_fn, abis: abi::AbiSet::Rust(), sig: ty::FnSig { - bound_lifetime_names: opt_vec::Empty, + binder_id: main_id, inputs: ~[], output: ty::mk_nil(), variadic: false @@ -400,7 +411,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt, purity: ast::impure_fn, abis: abi::AbiSet::Rust(), sig: ty::FnSig { - bound_lifetime_names: opt_vec::Empty, + binder_id: start_id, inputs: ~[ ty::mk_int(), ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8())) @@ -456,6 +467,9 @@ pub fn check_crate(tcx: ty::ctxt, // have valid types and not error tcx.sess.abort_if_errors(); + time(time_passes, "variance inference", (), |_| + variance::infer_variance(tcx, crate)); + time(time_passes, "coherence checking", (), |_| coherence::check_coherence(ccx, crate)); diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index 1967122745dad..9a32eafa8e4de 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -11,312 +11,71 @@ use middle::ty; -use std::result; +use std::vec; use syntax::ast; use syntax::codemap::Span; use syntax::opt_vec::OptVec; -use syntax::opt_vec; -use syntax::parse::token::special_idents; - -#[deriving(ToStr)] -pub struct RegionError { - msg: ~str, - replacement: ty::Region -} +/// Defines strategies for handling regions that are omitted. For +/// example, if one writes the type `&Foo`, then the lifetime of of +/// this borrowed pointer has been omitted. When converting this +/// type, the generic functions in astconv will invoke `anon_regions` +/// on the provided region-scope to decide how to translate this +/// omitted region. +/// +/// It is not always legal to omit regions, therefore `anon_regions` +/// can return `Err(())` to indicate that this is not a scope in which +/// regions can legally be omitted. pub trait RegionScope { - fn anon_region(&self, span: Span) -> Result; - fn self_region(&self, span: Span) -> Result; - fn named_region(&self, span: Span, id: ast::Ident) - -> Result; -} - -#[deriving(Clone)] -pub struct EmptyRscope; -impl RegionScope for EmptyRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"only 'static is allowed here", - replacement: ty::re_static - }) - } - fn self_region(&self, _span: Span) -> Result { - self.anon_region(_span) - } - fn named_region(&self, _span: Span, _id: ast::Ident) - -> Result - { - self.anon_region(_span) - } -} - -#[deriving(Clone)] -pub struct RegionParamNames(OptVec); - -impl RegionParamNames { - fn has_self(&self) -> bool { - self.has_ident(special_idents::self_) - } - - fn has_ident(&self, ident: ast::Ident) -> bool { - for region_param_name in self.iter() { - if *region_param_name == ident { - return true; - } - } - false - } - - pub fn add_generics(&mut self, generics: &ast::Generics) { - match generics.lifetimes { - opt_vec::Empty => {} - opt_vec::Vec(ref new_lifetimes) => { - match **self { - opt_vec::Empty => { - *self = RegionParamNames( - opt_vec::Vec(new_lifetimes.map(|lt| lt.ident))); - } - opt_vec::Vec(ref mut existing_lifetimes) => { - for new_lifetime in new_lifetimes.iter() { - existing_lifetimes.push(new_lifetime.ident); - } - } - } - } - } - } - - // Convenience function to produce the error for an unresolved name. The - // optional argument specifies a custom replacement. - pub fn undeclared_name(custom_replacement: Option) - -> Result { - let replacement = match custom_replacement { - None => ty::re_bound(ty::br_self), - Some(custom_replacement) => custom_replacement - }; - Err(RegionError { - msg: ~"this lifetime must be declared", - replacement: replacement - }) - } - - pub fn from_generics(generics: &ast::Generics) -> RegionParamNames { - match generics.lifetimes { - opt_vec::Empty => RegionParamNames(opt_vec::Empty), - opt_vec::Vec(ref lifetimes) => { - RegionParamNames(opt_vec::Vec(lifetimes.map(|lt| lt.ident))) - } - } - } - - pub fn from_lifetimes(lifetimes: &opt_vec::OptVec) - -> RegionParamNames { - match *lifetimes { - opt_vec::Empty => RegionParamNames::new(), - opt_vec::Vec(ref v) => { - RegionParamNames(opt_vec::Vec(v.map(|lt| lt.ident))) - } - } - } - - fn new() -> RegionParamNames { - RegionParamNames(opt_vec::Empty) - } -} - -#[deriving(Clone)] -struct RegionParameterization { - variance: ty::region_variance, - region_param_names: RegionParamNames, -} - -impl RegionParameterization { - pub fn from_variance_and_generics(variance: Option, - generics: &ast::Generics) - -> Option { - match variance { - None => None, - Some(variance) => { - Some(RegionParameterization { - variance: variance, - region_param_names: - RegionParamNames::from_generics(generics), - }) - } - } - } -} - -#[deriving(Clone)] -pub struct MethodRscope { - explicit_self: ast::explicit_self_, - variance: Option, - region_param_names: RegionParamNames, -} - -impl MethodRscope { - // `generics` here refers to the generics of the outer item (impl or - // trait). - pub fn new(explicit_self: ast::explicit_self_, - variance: Option, - rcvr_generics: &ast::Generics) - -> MethodRscope { - let region_param_names = - RegionParamNames::from_generics(rcvr_generics); - MethodRscope { - explicit_self: explicit_self, - variance: variance, - region_param_names: region_param_names - } - } - - pub fn region_param_names(&self) -> RegionParamNames { - self.region_param_names.clone() - } -} - -impl RegionScope for MethodRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"anonymous lifetimes are not permitted here", - replacement: ty::re_bound(ty::br_self) - }) - } - fn self_region(&self, _span: Span) -> Result { - assert!(self.variance.is_some()); - match self.variance { - None => {} // must be borrowed self, so this is OK - Some(_) => { - if !self.region_param_names.has_self() { - return Err(RegionError { - msg: ~"the `self` lifetime must be declared", - replacement: ty::re_bound(ty::br_self) - }) - } - } - } - result::Ok(ty::re_bound(ty::br_self)) - } - fn named_region(&self, span: Span, id: ast::Ident) - -> Result { - if !self.region_param_names.has_ident(id) { - return RegionParamNames::undeclared_name(None); - } - do EmptyRscope.named_region(span, id).or_else |_e| { - result::Err(RegionError { - msg: ~"lifetime is not in scope", - replacement: ty::re_bound(ty::br_self) - }) - } - } + fn anon_regions(&self, + span: Span, + count: uint) + -> Result<~[ty::Region], ()>; } -#[deriving(Clone)] -pub struct TypeRscope(Option); - -impl TypeRscope { - fn replacement(&self) -> ty::Region { - if self.is_some() { - ty::re_bound(ty::br_self) - } else { - ty::re_static - } - } -} -impl RegionScope for TypeRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"anonymous lifetimes are not permitted here", - replacement: self.replacement() - }) - } - fn self_region(&self, _span: Span) -> Result { - match **self { - None => { - // if the self region is used, region parameterization should - // have inferred that this type is RP - fail!("region parameterization should have inferred that \ - this type is RP"); - } - Some(ref region_parameterization) => { - if !region_parameterization.region_param_names.has_self() { - return Err(RegionError { - msg: ~"the `self` lifetime must be declared", - replacement: ty::re_bound(ty::br_self) - }) - } - } - } - result::Ok(ty::re_bound(ty::br_self)) - } - fn named_region(&self, span: Span, id: ast::Ident) - -> Result { - do EmptyRscope.named_region(span, id).or_else |_e| { - result::Err(RegionError { - msg: ~"only 'self is allowed as part of a type declaration", - replacement: self.replacement() - }) - } - } -} +// A scope in which all regions must be explicitly named +pub struct ExplicitRscope; -pub fn bound_self_region(rp: Option) - -> OptVec { - match rp { - Some(_) => opt_vec::with(ty::re_bound(ty::br_self)), - None => opt_vec::Empty +impl RegionScope for ExplicitRscope { + fn anon_regions(&self, + _span: Span, + _count: uint) + -> Result<~[ty::Region], ()> { + Err(()) } } +/// A scope in which we generate anonymous, late-bound regions for +/// omitted regions. This occurs in function signatures. pub struct BindingRscope { - base: @RegionScope, - anon_bindings: @mut uint, - region_param_names: RegionParamNames, + binder_id: ast::NodeId, + anon_bindings: @mut uint } -impl Clone for BindingRscope { - fn clone(&self) -> BindingRscope { +impl BindingRscope { + pub fn new(binder_id: ast::NodeId) -> BindingRscope { BindingRscope { - base: self.base, - anon_bindings: self.anon_bindings, - region_param_names: self.region_param_names.clone(), + binder_id: binder_id, + anon_bindings: @mut 0 } } } -pub fn in_binding_rscope( - this: &RS, - region_param_names: RegionParamNames) - -> BindingRscope { - let base = @(*this).clone(); - let base = base as @RegionScope; - BindingRscope { - base: base, - anon_bindings: @mut 0, - region_param_names: region_param_names, - } -} - impl RegionScope for BindingRscope { - fn anon_region(&self, _span: Span) -> Result { + fn anon_regions(&self, + _: Span, + count: uint) + -> Result<~[ty::Region], ()> { let idx = *self.anon_bindings; - *self.anon_bindings += 1; - result::Ok(ty::re_bound(ty::br_anon(idx))) - } - fn self_region(&self, span: Span) -> Result { - self.base.self_region(span) - } - fn named_region(&self, - span: Span, - id: ast::Ident) -> Result - { - do self.base.named_region(span, id).or_else |_e| { - let result = ty::re_bound(ty::br_named(id)); - if self.region_param_names.has_ident(id) { - result::Ok(result) - } else { - RegionParamNames::undeclared_name(Some(result)) - } - } + *self.anon_bindings += count; + Ok(vec::from_fn(count, |i| ty::ReLateBound(self.binder_id, + ty::BrAnon(idx + i)))) } } + +pub fn bound_type_regions(defs: &[ty::RegionParameterDef]) + -> OptVec { + assert!(defs.iter().all(|def| def.def_id.crate == ast::LOCAL_CRATE)); + defs.iter().enumerate().map( + |(i, def)| ty::ReEarlyBound(def.def_id.node, i, def.ident)).collect() +} diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs new file mode 100644 index 0000000000000..1b435d11404b1 --- /dev/null +++ b/src/librustc/middle/typeck/variance.rs @@ -0,0 +1,1000 @@ +// Copyright 2013 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. + +/*! + +This file infers the variance of type and lifetime parameters. The +algorithm is taken from Section 4 of the paper "Taming the Wildcards: +Combining Definition- and Use-Site Variance" published in PLDI'11 and +written by Altidor et al., and hereafter referred to as The Paper. + +This inference is explicitly designed *not* to consider the uses of +types within code. To determine the variance of type parameters +defined on type `X`, we only consider the definition of the type `X` +and the definitions of any types it references. + +We only infer variance for type parameters found on *types*: structs, +enums, and traits. We do not infer variance for type parameters found +on fns or impls. This is because those things are not type definitions +and variance doesn't really make sense in that context. + +It is worth covering what variance means in each case. For structs and +enums, I think it is fairly straightforward. The variance of the type +or lifetime parameters defines whether `T` is a subtype of `T` +(resp. `T<'a>` and `T<'b>`) based on the relationship of `A` and `B` +(resp. `'a` and `'b`). (FIXME #3598 -- we do not currently make use of +the variances we compute for type parameters.) + +### Variance on traits + +The meaning of variance for trait parameters is more subtle and worth +expanding upon. There are in fact two uses of the variance values we +compute. + +#### Trait variance and object types + +The first is for object types. Just as with structs and enums, we can +decide the subtyping relationship between two object types `&Trait` +and `&Trait` based on the relationship of `A` and `B`. Note that +for object types we ignore the `Self` type parameter -- it is unknown, +and the nature of dynamic dispatch ensures that we will always call a +function that is expected the appropriate `Self` type. However, we +must be careful with the other type parameters, or else we could end +up calling a function that is expecting one type but provided another. + +To see what I mean, consider a trait like so: + + trait ConvertTo { + fn convertTo(&self) -> A; + } + +Intuitively, If we had one object `O=&ConvertTo` and another +`S=&ConvertTo`, then `S <: O` because `String <: Object` +(presuming Java-like "string" and "object" types, my go to examples +for subtyping). The actual algorithm would be to compare the +(explicit) type parameters pairwise respecting their variance: here, +the type parameter A is covariant (it appears only in a return +position), and hence we require that `String <: Object`. + +You'll note though that we did not consider the binding for the +(implicit) `Self` type parameter: in fact, it is unknown, so that's +good. The reason we can ignore that parameter is precisely because we +don't need to know its value until a call occurs, and at that time (as +you said) the dynamic nature of virtual dispatch means the code we run +will be correct for whatever value `Self` happens to be bound to for +the particular object whose method we called. `Self` is thus different +from `A`, because the caller requires that `A` be known in order to +know the return type of the method `convertTo()`. (As an aside, we +have rules preventing methods where `Self` appears outside of the +receiver position from being called via an object.) + +#### Trait variance and vtable resolution + +But traits aren't only used with objects. They're also used when +deciding whether a given impl satisfies a given trait bound (or should +be -- FIXME #5781). To set the scene here, imagine I had a function: + + fn convertAll>(v: &[T]) { + ... + } + +Now imagine that I have an implementation of `ConvertTo` for `Object`: + + impl ConvertTo for Object { ... } + +And I want to call `convertAll` on an array of strings. Suppose +further that for whatever reason I specifically supply the value of +`String` for the type parameter `T`: + + let mut vector = ~["string", ...]; + convertAll::(v); + +Is this legal? To put another way, can we apply the `impl` for +`Object` to the type `String`? The answer is yes, but to see why +we have to expand out what will happen: + +- `convertAll` will create a pointer to one of the entries in the + vector, which will have type `&String` +- It will then call the impl of `convertTo()` that is intended + for use with objects. This has the type: + + fn(self: &Object) -> int + + It is ok to provide a value for `self` of type `&String` because + `&String <: &Object`. + +OK, so intuitively we want this to be legal, so let's bring this back +to variance and see whether we are computing the correct result. We +must first figure out how to phrase the question "is an impl for +`Object,int` usable where an impl for `String,int` is expected?" + +Maybe it's helpful to think of a dictionary-passing implementation of +type classes. In that case, `convertAll()` takes an implicit parameter +representing the impl. In short, we *have* an impl of type: + + V_O = ConvertTo for Object + +and the function prototype expects an impl of type: + + V_S = ConvertTo for String + +As with any argument, this is legal if the type of the value given +(`V_O`) is a subtype of the type expected (`V_S`). So is `V_O <: V_S`? +The answer will depend on the variance of the various parameters. In +this case, because the `Self` parameter is contravariant and `A` is +covariant, it means that: + + V_O <: V_S iff + int <: int + String <: Object + +These conditions are satisfied and so we are happy. + +### The algorithm + +The basic idea is quite straightforward. We iterate over the types +defined and, for each use of a type parameter X, accumulate a +constraint indicating that the variance of X must be valid for the +variance of that use site. We then iteratively refine the variance of +X until all constraints are met. There is *always* a sol'n, because at +the limit we can declare all type parameters to be invariant and all +constraints will be satisfied. + +As a simple example, consider: + + enum Option { Some(A), None } + enum OptionalFn { Some(&fn(B)), None } + enum OptionalMap { Some(&fn(C) -> C), None } + +Here, we will generate the constraints: + + 1. V(A) <= + + 2. V(B) <= - + 3. V(C) <= + + 4. V(C) <= - + +These indicate that (1) the variance of A must be at most covariant; +(2) the variance of B must be at most contravariant; and (3, 4) the +variance of C must be at most covariant *and* contravariant. All of these +results are based on a variance lattice defined as follows: + + * Top (bivariant) + - + + o Bottom (invariant) + +Based on this lattice, the solution V(A)=+, V(B)=-, V(C)=o is the +optimal solution. Note that there is always a naive solution which +just declares all variables to be invariant. + +You may be wondering why fixed-point iteration is required. The reason +is that the variance of a use site may itself be a function of the +variance of other type parameters. In full generality, our constraints +take the form: + + V(X) <= Term + Term := + | - | * | o | V(X) | Term x Term + +Here the notation V(X) indicates the variance of a type/region +parameter `X` with respect to its defining class. `Term x Term` +represents the "variance transform" as defined in the paper: + + If the variance of a type variable `X` in type expression `E` is `V2` + and the definition-site variance of the [corresponding] type parameter + of a class `C` is `V1`, then the variance of `X` in the type expression + `C` is `V3 = V1.xform(V2)`. + +*/ + +use std::hashmap::HashMap; +use extra::arena; +use extra::arena::Arena; +use middle::ty; +use std::vec; +use syntax::ast; +use syntax::ast_map; +use syntax::ast_util; +use syntax::parse::token; +use syntax::opt_vec; +use syntax::visit; +use syntax::visit::Visitor; + +pub fn infer_variance(tcx: ty::ctxt, + crate: &ast::Crate) { + let mut arena = arena::Arena::new(); + let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, crate); + let constraints_cx = add_constraints_from_crate(terms_cx, crate); + solve_constraints(constraints_cx); +} + +/************************************************************************** + * Representing terms + * + * Terms are structured as a straightforward tree. Rather than rely on + * GC, we allocate terms out of a bounded arena (the lifetime of this + * arena is the lifetime 'self that is threaded around). + * + * We assign a unique index to each type/region parameter whose variance + * is to be inferred. We refer to such variables as "inferreds". An + * `InferredIndex` is a newtype'd int representing the index of such + * a variable. + */ + +type VarianceTermPtr<'self> = &'self VarianceTerm<'self>; + +struct InferredIndex(uint); + +enum VarianceTerm<'self> { + ConstantTerm(ty::Variance), + TransformTerm(VarianceTermPtr<'self>, VarianceTermPtr<'self>), + InferredTerm(InferredIndex), +} + +impl<'self> ToStr for VarianceTerm<'self> { + fn to_str(&self) -> ~str { + match *self { + ConstantTerm(c1) => format!("{}", c1.to_str()), + TransformTerm(v1, v2) => format!("({} \u00D7 {})", + v1.to_str(), v2.to_str()), + InferredTerm(id) => format!("[{}]", *id) + } + } +} + +/************************************************************************** + * The first pass over the crate simply builds up the set of inferreds. + */ + +struct TermsContext<'self> { + tcx: ty::ctxt, + arena: &'self Arena, + + empty_variances: @ty::ItemVariances, + + // Maps from the node id of a type/generic parameter to the + // corresponding inferred index. + inferred_map: HashMap, + + // Maps from an InferredIndex to the info for that variable. + inferred_infos: ~[InferredInfo<'self>], +} + +enum ParamKind { TypeParam, RegionParam, SelfParam } + +struct InferredInfo<'self> { + item_id: ast::NodeId, + kind: ParamKind, + index: uint, + param_id: ast::NodeId, + term: VarianceTermPtr<'self>, +} + +fn determine_parameters_to_be_inferred<'a>(tcx: ty::ctxt, + arena: &'a mut Arena, + crate: &ast::Crate) + -> TermsContext<'a> { + let mut terms_cx = TermsContext { + tcx: tcx, + arena: arena, + inferred_map: HashMap::new(), + inferred_infos: ~[], + + // cache and share the variance struct used for items with + // no type/region parameters + empty_variances: @ty::ItemVariances { self_param: None, + type_params: opt_vec::Empty, + region_params: opt_vec::Empty } + }; + + visit::walk_crate(&mut terms_cx, crate, ()); + + terms_cx +} + +impl<'self> TermsContext<'self> { + fn add_inferred(&mut self, + item_id: ast::NodeId, + kind: ParamKind, + index: uint, + param_id: ast::NodeId) { + let inf_index = InferredIndex(self.inferred_infos.len()); + let term = self.arena.alloc(|| InferredTerm(inf_index)); + self.inferred_infos.push(InferredInfo { item_id: item_id, + kind: kind, + index: index, + param_id: param_id, + term: term }); + let newly_added = self.inferred_map.insert(param_id, inf_index); + assert!(newly_added); + + debug!("add_inferred(item_id={}, \ + kind={:?}, \ + index={}, \ + param_id={}, + inf_index={:?})", + item_id, kind, index, param_id, inf_index); + } + + fn num_inferred(&self) -> uint { + self.inferred_infos.len() + } +} + +impl<'self> Visitor<()> for TermsContext<'self> { + fn visit_item(&mut self, + item: @ast::item, + (): ()) { + debug!("add_inferreds for item {}", item.repr(self.tcx)); + + let inferreds_on_entry = self.num_inferred(); + + // NB: In the code below for writing the results back into the + // tcx, we rely on the fact that all inferreds for a particular + // item are assigned continuous indices. + match item.node { + ast::item_trait(*) => { + self.add_inferred(item.id, SelfParam, 0, item.id); + } + _ => { } + } + + match item.node { + ast::item_enum(_, ref generics) | + ast::item_struct(_, ref generics) | + ast::item_trait(ref generics, _, _) => { + for (i, p) in generics.lifetimes.iter().enumerate() { + self.add_inferred(item.id, RegionParam, i, p.id); + } + for (i, p) in generics.ty_params.iter().enumerate() { + self.add_inferred(item.id, TypeParam, i, p.id); + } + + // If this item has no type or lifetime parameters, + // then there are no variances to infer, so just + // insert an empty entry into the variance map. + // Arguably we could just leave the map empty in this + // case but it seems cleaner to be able to distinguish + // "invalid item id" from "item id with no + // parameters". + if self.num_inferred() == inferreds_on_entry { + let newly_added = self.tcx.item_variance_map.insert( + ast_util::local_def(item.id), + self.empty_variances); + assert!(newly_added); + } + + visit::walk_item(self, item, ()); + } + + ast::item_impl(*) | + ast::item_static(*) | + ast::item_fn(*) | + ast::item_mod(*) | + ast::item_foreign_mod(*) | + ast::item_ty(*) | + ast::item_mac(*) => { + visit::walk_item(self, item, ()); + } + } + } +} + +/************************************************************************** + * Constraint construction and representation + * + * The second pass over the AST determines the set of constraints. + * We walk the set of items and, for each member, generate new constraints. + */ + +struct ConstraintContext<'self> { + terms_cx: TermsContext<'self>, + + // These are pointers to common `ConstantTerm` instances + covariant: VarianceTermPtr<'self>, + contravariant: VarianceTermPtr<'self>, + invariant: VarianceTermPtr<'self>, + bivariant: VarianceTermPtr<'self>, + + constraints: ~[Constraint<'self>], +} + +/// Declares that the variable `decl_id` appears in a location with +/// variance `variance`. +struct Constraint<'self> { + inferred: InferredIndex, + variance: &'self VarianceTerm<'self>, +} + +fn add_constraints_from_crate<'a>(terms_cx: TermsContext<'a>, + crate: &ast::Crate) + -> ConstraintContext<'a> { + let covariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Covariant)); + let contravariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Contravariant)); + let invariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Invariant)); + let bivariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Bivariant)); + let mut constraint_cx = ConstraintContext { + terms_cx: terms_cx, + covariant: covariant, + contravariant: contravariant, + invariant: invariant, + bivariant: bivariant, + constraints: ~[], + }; + visit::walk_crate(&mut constraint_cx, crate, ()); + constraint_cx +} + +impl<'self> Visitor<()> for ConstraintContext<'self> { + fn visit_item(&mut self, + item: @ast::item, + (): ()) { + let did = ast_util::local_def(item.id); + let tcx = self.terms_cx.tcx; + + match item.node { + ast::item_enum(ref enum_definition, _) => { + // Hack: If we directly call `ty::enum_variants`, it + // annoyingly takes it upon itself to run off and + // evaluate the discriminants eagerly (*grumpy* that's + // not the typical pattern). This results in double + // error messages because typeck goes off and does + // this at a later time. All we really care about is + // the types of the variant arguments, so we just call + // `ty::VariantInfo::from_ast_variant()` ourselves + // here, mainly so as to mask the differences between + // struct-like enums and so forth. + for ast_variant in enum_definition.variants.iter() { + let variant = + ty::VariantInfo::from_ast_variant(tcx, + ast_variant, + /*discrimant*/ 0); + for &arg_ty in variant.args.iter() { + self.add_constraints_from_ty(arg_ty, self.covariant); + } + } + } + + ast::item_struct(*) => { + let struct_fields = ty::lookup_struct_fields(tcx, did); + for field_info in struct_fields.iter() { + assert_eq!(field_info.id.crate, ast::LOCAL_CRATE); + let field_ty = ty::node_id_to_type(tcx, field_info.id.node); + self.add_constraints_from_ty(field_ty, self.covariant); + } + } + + ast::item_trait(*) => { + let methods = ty::trait_methods(tcx, did); + for method in methods.iter() { + match method.transformed_self_ty { + Some(self_ty) => { + // The implicit self parameter is basically + // equivalent to a normal parameter declared + // like: + // + // self : self_ty + // + // where self_ty is `&Self` or `&mut Self` + // or whatever. + self.add_constraints_from_ty( + self_ty, self.contravariant); + } + None => {} + } + + self.add_constraints_from_sig( + &method.fty.sig, self.covariant); + } + } + + ast::item_static(*) | + ast::item_fn(*) | + ast::item_mod(*) | + ast::item_foreign_mod(*) | + ast::item_ty(*) | + ast::item_impl(*) | + ast::item_mac(*) => { + visit::walk_item(self, item, ()); + } + } + } +} + +impl<'self> ConstraintContext<'self> { + fn tcx(&self) -> ty::ctxt { + self.terms_cx.tcx + } + + fn inferred_index(&self, param_id: ast::NodeId) -> InferredIndex { + match self.terms_cx.inferred_map.find(¶m_id) { + Some(&index) => index, + None => { + self.tcx().sess.bug(format!( + "No inferred index entry for {}", + ast_map::node_id_to_str(self.tcx().items, + param_id, + token::get_ident_interner()))); + } + } + } + + fn declared_variance(&self, + param_def_id: ast::DefId, + item_def_id: ast::DefId, + kind: ParamKind, + index: uint) + -> VarianceTermPtr<'self> { + /*! + * Returns a variance term representing the declared variance of + * the type/region parameter with the given id. + */ + + assert_eq!(param_def_id.crate, item_def_id.crate); + if param_def_id.crate == ast::LOCAL_CRATE { + // Parameter on an item defined within current crate: + // variance not yet inferred, so return a symbolic + // variance. + let index = self.inferred_index(param_def_id.node); + self.terms_cx.inferred_infos[*index].term + } else { + // Parameter on an item defined within another crate: + // variance already inferred, just look it up. + let variances = ty::item_variances(self.tcx(), item_def_id); + let variance = match kind { + SelfParam => variances.self_param.unwrap(), + TypeParam => *variances.type_params.get(index), + RegionParam => *variances.region_params.get(index), + }; + self.constant_term(variance) + } + } + + fn add_constraint(&mut self, + index: InferredIndex, + variance: VarianceTermPtr<'self>) { + debug!("add_constraint(index={}, variance={})", + *index, variance.to_str()); + self.constraints.push(Constraint { inferred: index, + variance: variance }); + } + + fn contravariant(&mut self, + variance: VarianceTermPtr<'self>) + -> VarianceTermPtr<'self> { + self.xform(variance, self.contravariant) + } + + fn invariant(&mut self, + variance: VarianceTermPtr<'self>) + -> VarianceTermPtr<'self> { + self.xform(variance, self.invariant) + } + + fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'self> { + match v { + ty::Covariant => self.covariant, + ty::Invariant => self.invariant, + ty::Contravariant => self.contravariant, + ty::Bivariant => self.bivariant, + } + } + + fn xform(&mut self, + v1: VarianceTermPtr<'self>, + v2: VarianceTermPtr<'self>) + -> VarianceTermPtr<'self> { + match (*v1, *v2) { + (_, ConstantTerm(ty::Covariant)) => { + // Applying a "covariant" transform is always a no-op + v1 + } + + (ConstantTerm(c1), ConstantTerm(c2)) => { + self.constant_term(c1.xform(c2)) + } + + _ => { + self.terms_cx.arena.alloc(|| TransformTerm(v1, v2)) + } + } + } + + /// Adds constraints appropriate for an instance of `ty` appearing + /// in a context with ambient variance `variance` + fn add_constraints_from_ty(&mut self, + ty: ty::t, + variance: VarianceTermPtr<'self>) { + debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx())); + + match ty::get(ty).sty { + ty::ty_nil | ty::ty_bot | ty::ty_bool | + ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) | + ty::ty_float(_) => { + /* leaf type -- noop */ + } + + ty::ty_rptr(region, ref mt) => { + let contra = self.contravariant(variance); + self.add_constraints_from_region(region, contra); + self.add_constraints_from_mt(mt, variance); + } + + ty::ty_estr(vstore) => { + self.add_constraints_from_vstore(vstore, variance); + } + + ty::ty_evec(ref mt, vstore) => { + self.add_constraints_from_vstore(vstore, variance); + self.add_constraints_from_mt(mt, variance); + } + + ty::ty_box(ref mt) | + ty::ty_uniq(ref mt) | + ty::ty_ptr(ref mt) => { + self.add_constraints_from_mt(mt, variance); + } + + ty::ty_tup(ref subtys) => { + for &subty in subtys.iter() { + self.add_constraints_from_ty(subty, variance); + } + } + + ty::ty_enum(def_id, ref substs) | + ty::ty_struct(def_id, ref substs) => { + let item_type = ty::lookup_item_type(self.tcx(), def_id); + self.add_constraints_from_substs(def_id, &item_type.generics, + substs, variance); + } + + ty::ty_trait(def_id, ref substs, _, _, _) => { + let trait_def = ty::lookup_trait_def(self.tcx(), def_id); + self.add_constraints_from_substs(def_id, &trait_def.generics, + substs, variance); + } + + ty::ty_param(ty::param_ty { def_id: ref def_id, _ }) => { + assert_eq!(def_id.crate, ast::LOCAL_CRATE); + match self.terms_cx.inferred_map.find(&def_id.node) { + Some(&index) => { + self.add_constraint(index, variance); + } + None => { + // We do not infer variance for type parameters + // declared on methods. They will not be present + // in the inferred_map. + } + } + } + + ty::ty_self(ref def_id) => { + assert_eq!(def_id.crate, ast::LOCAL_CRATE); + let index = self.inferred_index(def_id.node); + self.add_constraint(index, variance); + } + + ty::ty_bare_fn(ty::BareFnTy { sig: ref sig, _ }) => { + self.add_constraints_from_sig(sig, variance); + } + + ty::ty_closure(ty::ClosureTy { sig: ref sig, region, _ }) => { + let contra = self.contravariant(variance); + self.add_constraints_from_region(region, contra); + self.add_constraints_from_sig(sig, variance); + } + + ty::ty_infer(*) | ty::ty_err | ty::ty_type | + ty::ty_opaque_box | ty::ty_opaque_closure_ptr(*) | + ty::ty_unboxed_vec(*) => { + self.tcx().sess.bug( + format!("Unexpected type encountered in \ + variance inference: {}", + ty.repr(self.tcx()))); + } + } + } + + /// Adds constraints appropriate for a vector with vstore `vstore` + /// appearing in a context with ambient variance `variance` + fn add_constraints_from_vstore(&mut self, + vstore: ty::vstore, + variance: VarianceTermPtr<'self>) { + match vstore { + ty::vstore_slice(r) => { + let contra = self.contravariant(variance); + self.add_constraints_from_region(r, contra); + } + + ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => { + } + } + } + + /// Adds constraints appropriate for a nominal type (enum, struct, + /// object, etc) appearing in a context with ambient variance `variance` + fn add_constraints_from_substs(&mut self, + def_id: ast::DefId, + generics: &ty::Generics, + substs: &ty::substs, + variance: VarianceTermPtr<'self>) { + debug!("add_constraints_from_substs(def_id={:?})", def_id); + + for (i, p) in generics.type_param_defs.iter().enumerate() { + let variance_decl = + self.declared_variance(p.def_id, def_id, TypeParam, i); + let variance_i = self.xform(variance, variance_decl); + self.add_constraints_from_ty(substs.tps[i], variance_i); + } + + match substs.regions { + ty::ErasedRegions => {} + ty::NonerasedRegions(ref rps) => { + for (i, p) in generics.region_param_defs.iter().enumerate() { + let variance_decl = + self.declared_variance(p.def_id, def_id, RegionParam, i); + let variance_i = self.xform(variance, variance_decl); + self.add_constraints_from_region(*rps.get(i), variance_i); + } + } + } + } + + /// Adds constraints appropriate for a function with signature + /// `sig` appearing in a context with ambient variance `variance` + fn add_constraints_from_sig(&mut self, + sig: &ty::FnSig, + variance: VarianceTermPtr<'self>) { + let contra = self.contravariant(variance); + for &input in sig.inputs.iter() { + self.add_constraints_from_ty(input, contra); + } + self.add_constraints_from_ty(sig.output, variance); + } + + /// Adds constraints appropriate for a region appearing in a + /// context with ambient variance `variance` + fn add_constraints_from_region(&mut self, + region: ty::Region, + variance: VarianceTermPtr<'self>) { + match region { + ty::ReEarlyBound(param_id, _, _) => { + let index = self.inferred_index(param_id); + self.add_constraint(index, variance); + } + + ty::ReStatic => { } + + ty::ReLateBound(*) => { + // We do not infer variance for region parameters on + // methods or in fn types. + } + + ty::ReFree(*) | ty::ReScope(*) | ty::ReInfer(*) | + ty::ReEmpty => { + // We don't expect to see anything but 'static or bound + // regions when visiting member types or method types. + self.tcx().sess.bug(format!("Unexpected region encountered in \ + variance inference: {}", + region.repr(self.tcx()))); + } + } + } + + /// Adds constraints appropriate for a mutability-type pair + /// appearing in a context with ambient variance `variance` + fn add_constraints_from_mt(&mut self, + mt: &ty::mt, + variance: VarianceTermPtr<'self>) { + match mt.mutbl { + ast::MutMutable => { + let invar = self.invariant(variance); + self.add_constraints_from_ty(mt.ty, invar); + } + + ast::MutImmutable => { + self.add_constraints_from_ty(mt.ty, variance); + } + } + } +} + +/************************************************************************** + * Constraint solving + * + * The final phase iterates over the constraints, refining the variance + * for each inferred until a fixed point is reached. This will be the + * optimal solution to the constraints. The final variance for each + * inferred is then written into the `variance_map` in the tcx. + */ + +struct SolveContext<'self> { + terms_cx: TermsContext<'self>, + constraints: ~[Constraint<'self>], + + // Maps from an InferredIndex to the inferred value for that variable. + solutions: ~[ty::Variance] +} + +fn solve_constraints(constraints_cx: ConstraintContext) { + let ConstraintContext { terms_cx, constraints, _ } = constraints_cx; + let solutions = vec::from_elem(terms_cx.num_inferred(), ty::Bivariant); + let mut solutions_cx = SolveContext { + terms_cx: terms_cx, + constraints: constraints, + solutions: solutions + }; + solutions_cx.solve(); + solutions_cx.write(); +} + +impl<'self> SolveContext<'self> { + fn solve(&mut self) { + // Propagate constraints until a fixed point is reached. Note + // that the maximum number of iterations is 2C where C is the + // number of constraints (each variable can change values at most + // twice). Since number of constraints is linear in size of the + // input, so is the inference process. + let mut changed = true; + while changed { + changed = false; + + for constraint in self.constraints.iter() { + let Constraint { inferred, variance: term } = *constraint; + let variance = self.evaluate(term); + let old_value = self.solutions[*inferred]; + let new_value = glb(variance, old_value); + if old_value != new_value { + debug!("Updating inferred {} (node {}) \ + from {:?} to {:?} due to {}", + *inferred, + self.terms_cx.inferred_infos[*inferred].param_id, + old_value, + new_value, + term.to_str()); + + self.solutions[*inferred] = new_value; + changed = true; + } + } + } + } + + fn write(&self) { + // Collect all the variances for a particular item and stick + // them into the variance map. We rely on the fact that we + // generate all the inferreds for a particular item + // consecutively (that is, we collect solutions for an item + // until we see a new item id, and we assume (1) the solutions + // are in the same order as the type parameters were declared + // and (2) all solutions or a given item appear before a new + // item id). + + let tcx = self.terms_cx.tcx; + let item_variance_map = tcx.item_variance_map; + let solutions = &self.solutions; + let inferred_infos = &self.terms_cx.inferred_infos; + let mut index = 0; + let num_inferred = self.terms_cx.num_inferred(); + while index < num_inferred { + let item_id = inferred_infos[index].item_id; + let mut item_variances = ty::ItemVariances { + self_param: None, + type_params: opt_vec::Empty, + region_params: opt_vec::Empty + }; + while (index < num_inferred && + inferred_infos[index].item_id == item_id) { + let info = &inferred_infos[index]; + match info.kind { + SelfParam => { + assert!(item_variances.self_param.is_none()); + item_variances.self_param = Some(solutions[index]); + } + TypeParam => { + item_variances.type_params.push(solutions[index]); + } + RegionParam => { + item_variances.region_params.push(solutions[index]); + } + } + index += 1; + } + + debug!("item_id={} item_variances={}", + item_id, + item_variances.repr(tcx)); + + let item_def_id = ast_util::local_def(item_id); + + // For unit testing: check for a special "rustc_variance" + // attribute and report an error with various results if found. + if ty::has_attr(tcx, item_def_id, "rustc_variance") { + let found = item_variances.repr(tcx); + tcx.sess.span_err(ast_map::item_span(tcx.items, item_id), found); + } + + let newly_added = item_variance_map.insert(item_def_id, + @item_variances); + assert!(newly_added); + } + } + + fn evaluate(&self, term: VarianceTermPtr<'self>) -> ty::Variance { + match *term { + ConstantTerm(v) => { + v + } + + TransformTerm(t1, t2) => { + let v1 = self.evaluate(t1); + let v2 = self.evaluate(t2); + v1.xform(v2) + } + + InferredTerm(index) => { + self.solutions[*index] + } + } + } +} + +/************************************************************************** + * Miscellany transformations on variance + */ + +trait Xform { + fn xform(self, v: Self) -> Self; +} + +impl Xform for ty::Variance { + fn xform(self, v: ty::Variance) -> ty::Variance { + // "Variance transformation", Figure 1 of The Paper + match (self, v) { + // Figure 1, column 1. + (ty::Covariant, ty::Covariant) => ty::Covariant, + (ty::Covariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Invariant) => ty::Invariant, + (ty::Covariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 2. + (ty::Contravariant, ty::Covariant) => ty::Contravariant, + (ty::Contravariant, ty::Contravariant) => ty::Covariant, + (ty::Contravariant, ty::Invariant) => ty::Invariant, + (ty::Contravariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 3. + (ty::Invariant, _) => ty::Invariant, + + // Figure 1, column 4. + (ty::Bivariant, _) => ty::Bivariant, + } + } +} + +fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance { + // Greatest lower bound of the variance lattice as + // defined in The Paper: + // + // * + // - + + // o + match (v1, v2) { + (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, + + (ty::Covariant, ty::Contravariant) => ty::Invariant, + (ty::Contravariant, ty::Covariant) => ty::Invariant, + + (ty::Covariant, ty::Covariant) => ty::Covariant, + + (ty::Contravariant, ty::Contravariant) => ty::Contravariant, + + (x, ty::Bivariant) | (ty::Bivariant, x) => x, + } +} + diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 006e14232f368..c5d7db465e6a1 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -11,11 +11,11 @@ use metadata::encoder; use middle::ty::{ReSkolemized, ReVar}; -use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid}; -use middle::ty::{br_fresh, ctxt, field}; +use middle::ty::{BoundRegion, BrAnon, BrNamed}; +use middle::ty::{BrFresh, ctxt, field}; use middle::ty::{mt, t, param_ty}; -use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region, - re_empty}; +use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region, + ReEmpty}; use middle::ty::{ty_bool, ty_char, ty_bot, ty_box, ty_struct, ty_enum}; use middle::ty::{ty_err, ty_estr, ty_evec, ty_float, ty_bare_fn, ty_closure}; use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param}; @@ -71,7 +71,7 @@ pub fn explain_region(cx: ctxt, region: ty::Region) -> ~str { pub fn explain_region_and_span(cx: ctxt, region: ty::Region) -> (~str, Option) { return match region { - re_scope(node_id) => { + ReScope(node_id) => { match cx.items.find(&node_id) { Some(&ast_map::node_block(ref blk)) => { explain_span(cx, "block", blk.span) @@ -104,11 +104,11 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) } } - re_free(ref fr) => { + ReFree(ref fr) => { let prefix = match fr.bound_region { - br_anon(idx) => format!("the anonymous lifetime \\#{} defined on", + BrAnon(idx) => format!("the anonymous lifetime \\#{} defined on", idx + 1), - br_fresh(_) => format!("an anonymous lifetime defined on"), + BrFresh(_) => format!("an anonymous lifetime defined on"), _ => format!("the lifetime {} as defined on", bound_region_ptr_to_str(cx, fr.bound_region)) }; @@ -118,6 +118,11 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) let (msg, opt_span) = explain_span(cx, "block", blk.span); (format!("{} {}", prefix, msg), opt_span) } + Some(&ast_map::node_item(it, _)) if match it.node { + ast::item_impl(*) => true, _ => false} => { + let (msg, opt_span) = explain_span(cx, "impl", it.span); + (format!("{} {}", prefix, msg), opt_span) + } Some(_) | None => { // this really should not happen (format!("{} node {}", prefix, fr.scope_id), None) @@ -125,13 +130,13 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) } } - re_static => { (~"the static lifetime", None) } + ReStatic => { (~"the static lifetime", None) } - re_empty => { (~"the empty lifetime", None) } + ReEmpty => { (~"the empty lifetime", None) } // I believe these cases should not occur (except when debugging, // perhaps) - re_infer(_) | re_bound(_) => { + ty::ReInfer(_) | ty::ReEarlyBound(*) | ty::ReLateBound(*) => { (format!("lifetime {:?}", region), None) } }; @@ -145,27 +150,28 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) } } -pub fn bound_region_ptr_to_str(cx: ctxt, br: bound_region) -> ~str { +pub fn bound_region_ptr_to_str(cx: ctxt, br: BoundRegion) -> ~str { bound_region_to_str(cx, "&", true, br) } pub fn bound_region_to_str(cx: ctxt, prefix: &str, space: bool, - br: bound_region) -> ~str { + br: BoundRegion) -> ~str { let space_str = if space { " " } else { "" }; - if cx.sess.verbose() { return format!("{}{:?}{}", prefix, br, space_str); } + if cx.sess.verbose() { + return format!("{}{}{}", prefix, br.repr(cx), space_str); + } match br { - br_named(id) => format!("{}'{}{}", prefix, cx.sess.str_of(id), space_str), - br_self => format!("{}'self{}", prefix, space_str), - br_anon(_) => prefix.to_str(), - br_fresh(_) => prefix.to_str(), - br_cap_avoid(_, br) => bound_region_to_str(cx, prefix, space, *br) + BrNamed(_, ident) => format!("{}'{}{}", prefix, + cx.sess.str_of(ident), space_str), + BrAnon(_) => prefix.to_str(), + BrFresh(_) => prefix.to_str(), } } -pub fn re_scope_id_to_str(cx: ctxt, node_id: ast::NodeId) -> ~str { +pub fn ReScope_id_to_str(cx: ctxt, node_id: ast::NodeId) -> ~str { match cx.items.find(&node_id) { Some(&ast_map::node_block(ref blk)) => { format!("", @@ -198,7 +204,7 @@ pub fn re_scope_id_to_str(cx: ctxt, node_id: ast::NodeId) -> ~str { format!("", node_id) } _ => { cx.sess.bug( - format!("re_scope refers to {}", + format!("ReScope refers to {}", ast_map::node_id_to_str(cx.items, node_id, token::get_ident_interner()))) } } @@ -215,7 +221,7 @@ pub fn region_to_str(cx: ctxt, prefix: &str, space: bool, region: Region) -> ~st let space_str = if space { " " } else { "" }; if cx.sess.verbose() { - return format!("{}{:?}{}", prefix, region, space_str); + return format!("{}{}{}", prefix, region.repr(cx), space_str); } // These printouts are concise. They do not contain all the information @@ -223,15 +229,16 @@ pub fn region_to_str(cx: ctxt, prefix: &str, space: bool, region: Region) -> ~st // to fit that into a short string. Hence the recommendation to use // `explain_region()` or `note_and_explain_region()`. match region { - re_scope(_) => prefix.to_str(), - re_bound(br) => bound_region_to_str(cx, prefix, space, br), - re_free(ref fr) => bound_region_to_str(cx, prefix, space, fr.bound_region), - re_infer(ReSkolemized(_, br)) => { + ty::ReScope(_) => prefix.to_str(), + ty::ReEarlyBound(_, _, ident) => cx.sess.str_of(ident).to_owned(), + ty::ReLateBound(_, br) => bound_region_to_str(cx, prefix, space, br), + ty::ReFree(ref fr) => bound_region_to_str(cx, prefix, space, fr.bound_region), + ty::ReInfer(ReSkolemized(_, br)) => { bound_region_to_str(cx, prefix, space, br) } - re_infer(ReVar(_)) => prefix.to_str(), - re_static => format!("{}'static{}", prefix, space_str), - re_empty => format!("{}'{}", prefix, space_str) + ty::ReInfer(ReVar(_)) => prefix.to_str(), + ty::ReStatic => format!("{}'static{}", prefix, space_str), + ty::ReEmpty => format!("{}'{}", prefix, space_str) } } @@ -289,9 +296,10 @@ pub fn tys_to_str(cx: ctxt, ts: &[t]) -> ~str { } pub fn fn_sig_to_str(cx: ctxt, typ: &ty::FnSig) -> ~str { - format!("fn{} -> {}", - tys_to_str(cx, typ.inputs.map(|a| *a)), - ty_to_str(cx, typ.output)) + format!("fn{}{} -> {}", + typ.binder_id, + typ.inputs.repr(cx), + typ.output.repr(cx)) } pub fn trait_ref_to_str(cx: ctxt, trait_ref: &ty::TraitRef) -> ~str { @@ -348,8 +356,8 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { }; match (cty.sigil, cty.region) { - (ast::ManagedSigil, ty::re_static) | - (ast::OwnedSigil, ty::re_static) => {} + (ast::ManagedSigil, ty::ReStatic) | + (ast::OwnedSigil, ty::ReStatic) => {} (_, region) => { s.push_str(region_to_str(cx, "", true, region)); @@ -594,8 +602,17 @@ impl Repr for ~[T] { impl Repr for ty::TypeParameterDef { fn repr(&self, tcx: ctxt) -> ~str { - format!("TypeParameterDef \\{{:?}, bounds: {}\\}", - self.def_id, self.bounds.repr(tcx)) + format!("TypeParameterDef({:?}, {})", + self.def_id, + self.bounds.repr(tcx)) + } +} + +impl Repr for ty::RegionParameterDef { + fn repr(&self, tcx: ctxt) -> ~str { + format!("RegionParameterDef({}, {:?})", + tcx.sess.str_of(self.ident), + self.def_id) } } @@ -655,6 +672,15 @@ impl Repr for ast::Expr { } } +impl Repr for ast::item { + fn repr(&self, tcx: ctxt) -> ~str { + format!("item({})", + ast_map::node_id_to_str(tcx.items, + self.id, + token::get_ident_interner())) + } +} + impl Repr for ast::Pat { fn repr(&self, tcx: ctxt) -> ~str { format!("pat({}: {})", @@ -663,15 +689,58 @@ impl Repr for ast::Pat { } } -impl Repr for ty::bound_region { +impl Repr for ty::BoundRegion { fn repr(&self, tcx: ctxt) -> ~str { - bound_region_ptr_to_str(tcx, *self) + match *self { + ty::BrAnon(id) => format!("BrAnon({})", id), + ty::BrNamed(id, ident) => format!("BrNamed({}, {})", + id.repr(tcx), + ident.repr(tcx)), + ty::BrFresh(id) => format!("BrFresh({})", id), + } } } impl Repr for ty::Region { fn repr(&self, tcx: ctxt) -> ~str { - region_to_str(tcx, "", false, *self) + match *self { + ty::ReEarlyBound(id, index, ident) => { + format!("ReEarlyBound({}, {}, {})", + id, index, ident.repr(tcx)) + } + + ty::ReLateBound(binder_id, ref bound_region) => { + format!("ReLateBound({}, {})", + binder_id, bound_region.repr(tcx)) + } + + ty::ReFree(ref fr) => { + format!("ReFree({}, {})", + fr.scope_id, + fr.bound_region.repr(tcx)) + } + + ty::ReScope(id) => { + format!("ReScope({})", id) + } + + ty::ReStatic => { + format!("ReStatic") + } + + ty::ReInfer(ReVar(ref vid)) => { + format!("ReInfer({})", vid.id) + } + + ty::ReInfer(ReSkolemized(id, ref bound_region)) => { + format!("re_skolemized({}, {})", + id, bound_region.repr(tcx)) + } + + ty::ReEmpty => { + format!("ReEmpty") + } + } } } @@ -707,23 +776,38 @@ impl Repr for ty::ty_param_bounds_and_ty { impl Repr for ty::Generics { fn repr(&self, tcx: ctxt) -> ~str { - format!("Generics \\{type_param_defs: {}, region_param: {:?}\\}", - self.type_param_defs.repr(tcx), - self.region_param) + format!("Generics(type_param_defs: {}, region_param_defs: {})", + self.type_param_defs.repr(tcx), + self.region_param_defs.repr(tcx)) + } +} + +impl Repr for ty::ItemVariances { + fn repr(&self, tcx: ctxt) -> ~str { + format!("IterVariances(self_param={}, type_params={}, region_params={})", + self.self_param.repr(tcx), + self.type_params.repr(tcx), + self.region_params.repr(tcx)) + } +} + +impl Repr for ty::Variance { + fn repr(&self, _: ctxt) -> ~str { + self.to_str().to_owned() } } impl Repr for ty::Method { fn repr(&self, tcx: ctxt) -> ~str { - format!("method \\{ident: {}, generics: {}, transformed_self_ty: {}, \ - fty: {}, explicit_self: {}, vis: {}, def_id: {}\\}", - self.ident.repr(tcx), - self.generics.repr(tcx), - self.transformed_self_ty.repr(tcx), - self.fty.repr(tcx), - self.explicit_self.repr(tcx), - self.vis.repr(tcx), - self.def_id.repr(tcx)) + format!("method(ident: {}, generics: {}, transformed_self_ty: {}, \ + fty: {}, explicit_self: {}, vis: {}, def_id: {})", + self.ident.repr(tcx), + self.generics.repr(tcx), + self.transformed_self_ty.repr(tcx), + self.fty.repr(tcx), + self.explicit_self.repr(tcx), + self.vis.repr(tcx), + self.def_id.repr(tcx)) } } diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index dd1ad8263da86..f48ad25712d12 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -835,7 +835,7 @@ impl Clean for ast::Path { #[deriving(Clone, Encodable, Decodable)] pub struct PathSegment { name: ~str, - lifetime: Option, + lifetimes: ~[Lifetime], types: ~[Type], } @@ -843,7 +843,7 @@ impl Clean for ast::PathSegment { fn clean(&self) -> PathSegment { PathSegment { name: self.identifier.clean(), - lifetime: self.lifetime.clean(), + lifetimes: self.lifetimes.clean(), types: self.types.clean() } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 9ff450d4135ff..4c64feee38439 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -92,16 +92,17 @@ impl fmt::Default for clean::Path { if i > 0 { f.buf.write("::".as_bytes()) } f.buf.write(seg.name.as_bytes()); - if seg.lifetime.is_some() || seg.types.len() > 0 { + if seg.lifetimes.len() > 0 || seg.types.len() > 0 { f.buf.write("<".as_bytes()); - match seg.lifetime { - Some(ref lifetime) => write!(f.buf, "{}", *lifetime), - None => {} + let mut comma = false; + for lifetime in seg.lifetimes.iter() { + if comma { f.buf.write(", ".as_bytes()); } + comma = true; + write!(f.buf, "{}", *lifetime); } - for (i, ty) in seg.types.iter().enumerate() { - if i > 0 || seg.lifetime.is_some() { - f.buf.write(", ".as_bytes()); - } + for ty in seg.types.iter() { + if comma { f.buf.write(", ".as_bytes()); } + comma = true; write!(f.buf, "{}", *ty); } f.buf.write(">".as_bytes()); @@ -152,16 +153,17 @@ fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool, // The generics will get written to both the title and link let mut generics = ~""; let last = path.segments.last(); - if last.lifetime.is_some() || last.types.len() > 0 { + if last.lifetimes.len() > 0 || last.types.len() > 0 { + let mut counter = 0; generics.push_str("<"); - match last.lifetime { - Some(ref lifetime) => generics.push_str(format!("{}", *lifetime)), - None => {} + for lifetime in last.lifetimes.iter() { + if counter > 0 { generics.push_str(", "); } + counter += 1; + generics.push_str(format!("{}", *lifetime)); } - for (i, ty) in last.types.iter().enumerate() { - if i > 0 || last.lifetime.is_some() { - generics.push_str(", "); - } + for ty in last.types.iter() { + if counter > 0 { generics.push_str(", "); } + counter += 1; generics.push_str(format!("{}", *ty)); } generics.push_str(">"); @@ -495,7 +497,7 @@ impl fmt::Default for clean::ViewListIdent { global: false, segments: ~[clean::PathSegment { name: v.name.clone(), - lifetime: None, + lifetimes: ~[], types: ~[], }] }; diff --git a/src/libstd/at_vec.rs b/src/libstd/at_vec.rs index 7d10061e812c3..90729966c18a6 100644 --- a/src/libstd/at_vec.rs +++ b/src/libstd/at_vec.rs @@ -12,7 +12,7 @@ use clone::Clone; use container::Container; -use iter::Iterator; +use iter::{Iterator, FromIterator}; use option::{Option, Some, None}; use mem; use unstable::raw::Repr; @@ -134,6 +134,17 @@ impl Clone for @[T] { } } +impl FromIterator for @[A] { + fn from_iterator>(iterator: &mut T) -> @[A] { + let (lower, _) = iterator.size_hint(); + do build(Some(lower)) |push| { + for x in *iterator { + push(x); + } + } + } +} + #[cfg(not(test))] #[allow(missing_doc)] pub mod traits { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2dd2629048bd3..88a8bbf7cf28f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -28,7 +28,7 @@ use extra::serialize::{Encodable, Decodable, Encoder, Decoder}; // table) and a SyntaxContext to track renaming and // macro expansion per Flatt et al., "Macros // That Work Together" -#[deriving(Clone, IterBytes, ToStr)] +#[deriving(Clone, IterBytes, ToStr, TotalEq, TotalOrd)] pub struct Ident { name: Name, ctxt: SyntaxContext } impl Ident { @@ -110,6 +110,7 @@ pub enum SyntaxContext_ { /// A name is a part of an identifier, representing a string or gensym. It's /// the result of interning. pub type Name = uint; + /// A mark represents a unique id associated with a macro expansion pub type Mrk = uint; @@ -156,9 +157,8 @@ pub struct Path { pub struct PathSegment { /// The identifier portion of this path segment. identifier: Ident, - /// The lifetime parameter for this path segment. Currently only one - /// lifetime parameter is allowed. - lifetime: Option, + /// The lifetime parameters for this path segment. + lifetimes: OptVec, /// The type parameters for this path segment, if present. types: OptVec, } @@ -167,7 +167,7 @@ pub type CrateNum = int; pub type NodeId = int; -#[deriving(Clone, Eq, Encodable, Decodable, IterBytes, ToStr)] +#[deriving(Clone, TotalEq, TotalOrd, Eq, Encodable, Decodable, IterBytes, ToStr)] pub struct DefId { crate: CrateNum, node: NodeId, @@ -251,6 +251,14 @@ pub enum Def { DefMethod(DefId /* method */, Option /* trait */), } +#[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] +pub enum DefRegion { + DefStaticRegion, + DefEarlyBoundRegion(/* index */ uint, /* lifetime decl */ NodeId), + DefLateBoundRegion(/* binder_id */ NodeId, /* depth */ uint, /* lifetime decl */ NodeId), + DefFreeRegion(/* block scope */ NodeId, /* lifetime decl */ NodeId), +} + // The set of MetaItems that define the compilation environment of the crate, // used to drive conditional compilation pub type CrateConfig = ~[@MetaItem]; diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index 17613d19c7ef1..f3d7ac1804db7 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -488,3 +488,15 @@ pub fn node_item_query(items: map, id: NodeId, _ => fail!("{}", error_msg) } } + +pub fn item_span(items: map, + id: ast::NodeId) + -> Span { + match items.find(&id) { + Some(&node_item(item, _)) => item.span, + r => { + fail!(format!("item_span: expected item with id {} but found {:?}", + id, r)) + } + } +} diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 3ec87dbdd26fb..ccae25dc012ac 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -225,7 +225,7 @@ pub fn ident_to_path(s: Span, identifier: Ident) -> Path { segments: ~[ ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -948,7 +948,7 @@ pub fn segments_name_eq(a : &[ast::PathSegment], b : &[ast::PathSegment]) -> boo for (idx,seg) in a.iter().enumerate() { if (seg.identifier.name != b[idx].identifier.name) // FIXME #7743: ident -> name problems in lifetime comparison? - || (seg.lifetime != b[idx].lifetime) + || (seg.lifetimes != b[idx].lifetimes) // can types contain idents? || (seg.types != b[idx].types) { return false; @@ -966,7 +966,9 @@ mod test { use std::hashmap::HashMap; fn ident_to_segment(id : &Ident) -> PathSegment { - PathSegment{identifier:id.clone(), lifetime: None, types: opt_vec::Empty} + PathSegment {identifier:id.clone(), + lifetimes: opt_vec::Empty, + types: opt_vec::Empty} } #[test] fn idents_name_eq_test() { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0a5e20fc2b29a..5ae158045e0e7 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -38,7 +38,7 @@ pub trait AstBuilder { fn path_all(&self, sp: Span, global: bool, idents: ~[ast::Ident], - rp: Option, + lifetimes: OptVec, types: ~[ast::Ty]) -> ast::Path; @@ -237,19 +237,19 @@ pub trait AstBuilder { impl AstBuilder for @ExtCtxt { fn path(&self, span: Span, strs: ~[ast::Ident]) -> ast::Path { - self.path_all(span, false, strs, None, ~[]) + self.path_all(span, false, strs, opt_vec::Empty, ~[]) } fn path_ident(&self, span: Span, id: ast::Ident) -> ast::Path { self.path(span, ~[id]) } fn path_global(&self, span: Span, strs: ~[ast::Ident]) -> ast::Path { - self.path_all(span, true, strs, None, ~[]) + self.path_all(span, true, strs, opt_vec::Empty, ~[]) } fn path_all(&self, sp: Span, global: bool, mut idents: ~[ast::Ident], - rp: Option, + lifetimes: OptVec, types: ~[ast::Ty]) -> ast::Path { let last_identifier = idents.pop(); @@ -257,13 +257,13 @@ impl AstBuilder for @ExtCtxt { .map(|ident| { ast::PathSegment { identifier: ident, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect(); segments.push(ast::PathSegment { identifier: last_identifier, - lifetime: rp, + lifetimes: lifetimes, types: opt_vec::from(types), }); ast::Path { @@ -327,7 +327,7 @@ impl AstBuilder for @ExtCtxt { self.ident_of("option"), self.ident_of("Option") ], - None, + opt_vec::Empty, ~[ ty ]), None) } diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index 0ca18f1208d0d..216bc3097ce94 100644 --- a/src/libsyntax/ext/concat_idents.rs +++ b/src/libsyntax/ext/concat_idents.rs @@ -43,7 +43,7 @@ pub fn expand_syntax_ext(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) segments: ~[ ast::PathSegment { identifier: res, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ] diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index cfb6048df18f1..b37757341efd8 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -375,14 +375,10 @@ impl<'self> TraitDef<'self> { cx.ty_ident(trait_span, ty_param.ident) }; - let self_lifetime = if generics.lifetimes.is_empty() { - None - } else { - Some(*generics.lifetimes.get(0)) - }; + let self_lifetimes = generics.lifetimes.clone(); // Create the type of `self`. - let self_type = cx.ty_path(cx.path_all(trait_span, false, ~[ type_ident ], self_lifetime, + let self_type = cx.ty_path(cx.path_all(trait_span, false, ~[ type_ident ], self_lifetimes, opt_vec::take_vec(self_ty_params)), None); let doc_attr = cx.attribute( diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 1e2a6fa2eb51b..d014816c070c1 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -14,6 +14,7 @@ use codemap::Span; use ext::base::ExtCtxt; use ext::build::{AstBuilder}; use ext::deriving::generic::*; +use opt_vec; pub fn expand_deriving_rand(cx: @ExtCtxt, span: Span, @@ -77,7 +78,7 @@ fn rand_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr { let rand_name = cx.path_all(span, true, rand_ident.clone(), - None, + opt_vec::Empty, ~[]); let rand_name = cx.expr_path(rand_name); diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs index c60259304aef2..d1a5af5f7e891 100644 --- a/src/libsyntax/ext/deriving/ty.rs +++ b/src/libsyntax/ext/deriving/ty.rs @@ -19,6 +19,7 @@ use ext::base::ExtCtxt; use ext::build::AstBuilder; use codemap::{Span,respan}; use opt_vec; +use opt_vec::OptVec; /// The types of pointers pub enum PtrTy<'self> { @@ -71,7 +72,7 @@ impl<'self> Path<'self> { self_generics: &Generics) -> ast::Path { let idents = self.path.map(|s| cx.ident_of(*s) ); - let lt = mk_lifetime(cx, span, &self.lifetime); + let lt = mk_lifetimes(cx, span, &self.lifetime); let tys = self.params.map(|t| t.to_ty(cx, span, self_ty, self_generics)); cx.path_all(span, self.global, idents, lt, tys) @@ -116,6 +117,13 @@ fn mk_lifetime(cx: @ExtCtxt, span: Span, lt: &Option<&str>) -> Option) -> OptVec { + match *lt { + Some(ref s) => opt_vec::with(cx.lifetime(span, cx.ident_of(*s))), + None => opt_vec::Empty + } +} + impl<'self> Ty<'self> { pub fn to_ty(&self, cx: @ExtCtxt, @@ -166,13 +174,9 @@ impl<'self> Ty<'self> { let self_params = do self_generics.ty_params.map |ty_param| { cx.ty_ident(span, ty_param.ident) }; - let lifetime = if self_generics.lifetimes.is_empty() { - None - } else { - Some(*self_generics.lifetimes.get(0)) - }; + let lifetimes = self_generics.lifetimes.clone(); - cx.path_all(span, false, ~[self_ty], lifetime, + cx.path_all(span, false, ~[self_ty], lifetimes, opt_vec::take_vec(self_params)) } Literal(ref p) => { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index ba946d5fb1f1d..b74349da2a9fa 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -169,7 +169,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, segments: ~[ ast::PathSegment { identifier: ident, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -628,7 +628,7 @@ impl Visitor<()> for NewNameFinderContext { segments: [ ast::PathSegment { identifier: id, - lifetime: _, + lifetimes: _, types: _ } ] diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 943279d2dc683..00919fce5db6c 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -15,7 +15,7 @@ use ext::base; use ext::build::AstBuilder; use rsparse = parse; use parse::token; - +use opt_vec; use std::fmt::parse; use std::hashmap::{HashMap, HashSet}; use std::vec; @@ -464,7 +464,7 @@ impl Context { sp, true, rtpath("Method"), - Some(life), + opt_vec::with(life), ~[] ), None); let st = ast::item_static(ty, ast::MutImmutable, method); @@ -582,7 +582,8 @@ impl Context { self.ecx.ident_of("rt"), self.ecx.ident_of("Piece"), ], - Some(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))), + opt_vec::with( + self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))), ~[] ), None); let ty = ast::ty_fixed_length_vec( diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 388de29b45680..ea0ab95a45105 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -144,7 +144,7 @@ pub trait ast_fold { ident: self.fold_ident(m.ident), attrs: m.attrs.map(|a| fold_attribute_(*a, self)), generics: fold_generics(&m.generics, self), - explicit_self: m.explicit_self, + explicit_self: self.fold_explicit_self(&m.explicit_self), purity: m.purity, decl: fold_fn_decl(&m.decl, self), body: self.fold_block(&m.body), @@ -245,12 +245,14 @@ pub trait ast_fold { ty_uniq(ref mt) => ty_uniq(fold_mt(mt, self)), ty_vec(ref mt) => ty_vec(fold_mt(mt, self)), ty_ptr(ref mt) => ty_ptr(fold_mt(mt, self)), - ty_rptr(region, ref mt) => ty_rptr(region, fold_mt(mt, self)), + ty_rptr(ref region, ref mt) => { + ty_rptr(fold_opt_lifetime(region, self), fold_mt(mt, self)) + } ty_closure(ref f) => { ty_closure(@TyClosure { sigil: f.sigil, purity: f.purity, - region: f.region, + region: fold_opt_lifetime(&f.region, self), onceness: f.onceness, bounds: fold_opt_bounds(&f.bounds, self), decl: fold_fn_decl(&f.decl, self), @@ -349,7 +351,7 @@ pub trait ast_fold { global: p.global, segments: p.segments.map(|segment| ast::PathSegment { identifier: self.fold_ident(segment.identifier), - lifetime: segment.lifetime, + lifetimes: segment.lifetimes.map(|l| fold_lifetime(l, self)), types: segment.types.map(|typ| self.fold_ty(typ)), }) } @@ -389,6 +391,24 @@ pub trait ast_fold { fn new_span(&self, sp: Span) -> Span { sp } + + fn fold_explicit_self(&self, es: &explicit_self) -> explicit_self { + Spanned { + span: self.new_span(es.span), + node: self.fold_explicit_self_(&es.node) + } + } + + fn fold_explicit_self_(&self, es: &explicit_self_) -> explicit_self_ { + match *es { + sty_static | sty_value(_) | sty_uniq(_) | sty_box(_) => { + *es + } + sty_region(ref lifetime, m) => { + sty_region(fold_opt_lifetime(lifetime, self), m) + } + } + } } /* some little folds that probably aren't useful to have in ast_fold itself*/ @@ -505,6 +525,11 @@ pub fn fold_lifetimes(lts: &OptVec, fld: &T) lts.map(|l| fold_lifetime(l, fld)) } +pub fn fold_opt_lifetime(o_lt: &Option, fld: &T) + -> Option { + o_lt.as_ref().map(|lt| fold_lifetime(lt, fld)) +} + pub fn fold_generics(generics: &Generics, fld: &T) -> Generics { Generics {ty_params: fold_ty_params(&generics.ty_params, fld), lifetimes: fold_lifetimes(&generics.lifetimes, fld)} @@ -675,7 +700,7 @@ pub fn noop_fold_type_method(m: &TypeMethod, fld: &T) purity: m.purity, decl: fold_fn_decl(&m.decl, fld), generics: fold_generics(&m.generics, fld), - explicit_self: m.explicit_self, + explicit_self: fld.fold_explicit_self(&m.explicit_self), id: fld.new_id(m.id), span: fld.new_span(m.span), } diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs index 2000d0b97461f..4d39d4df72f53 100644 --- a/src/libsyntax/opt_vec.rs +++ b/src/libsyntax/opt_vec.rs @@ -165,3 +165,13 @@ impl<'self, T> Iterator<&'self T> for OptVecIterator<'self, T> { } } } + +impl FromIterator for OptVec { + fn from_iterator>(iterator: &mut T) -> OptVec { + let mut r = Empty; + for x in *iterator { + r.push(x); + } + r + } +} diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 6c81784b5dee4..672865aadcc26 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -368,7 +368,7 @@ mod test { segments: ~[ ast::PathSegment { identifier: str_to_ident("a"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -387,12 +387,12 @@ mod test { segments: ~[ ast::PathSegment { identifier: str_to_ident("a"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }, ast::PathSegment { identifier: str_to_ident("b"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ] @@ -592,7 +592,7 @@ mod test { segments: ~[ ast::PathSegment { identifier: str_to_ident("d"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -614,7 +614,7 @@ mod test { segments: ~[ ast::PathSegment { identifier: str_to_ident("b"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -641,7 +641,7 @@ mod test { segments: ~[ ast::PathSegment { identifier: str_to_ident("b"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -669,7 +669,7 @@ mod test { ast::PathSegment { identifier: str_to_ident("int"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -687,7 +687,7 @@ mod test { ast::PathSegment { identifier: str_to_ident("b"), - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } ], @@ -724,8 +724,8 @@ mod test { identifier: str_to_ident( "b"), - lifetime: - None, + lifetimes: + opt_vec::Empty, types: opt_vec::Empty } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7c98d8d1c85b3..cfb4da87720be 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1490,7 +1490,7 @@ impl Parser { segments.push(PathSegmentAndBoundSet { segment: ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, }, bound_set: bound_set @@ -1499,46 +1499,21 @@ impl Parser { } // Parse the `<` before the lifetime and types, if applicable. - let (any_lifetime_or_types, optional_lifetime, types) = - if mode != NoTypesAllowed && self.eat(&token::LT) { - // Parse an optional lifetime. - let optional_lifetime = match *self.token { - token::LIFETIME(*) => Some(self.parse_lifetime()), - _ => None, - }; - - // Parse type parameters. - let mut types = opt_vec::Empty; - let mut need_comma = optional_lifetime.is_some(); - loop { - // We're done if we see a `>`. - match *self.token { - token::GT | token::BINOP(token::SHR) => { - self.expect_gt(); - break - } - _ => {} // Go on. - } - - if need_comma { - self.expect(&token::COMMA) - } else { - need_comma = true - } - - types.push(self.parse_ty(false)) + let (any_lifetime_or_types, lifetimes, types) = { + if mode != NoTypesAllowed && self.eat(&token::LT) { + let (lifetimes, types) = + self.parse_generic_values_after_lt(); + (true, lifetimes, opt_vec::from(types)) + } else { + (false, opt_vec::Empty, opt_vec::Empty) } - - (true, optional_lifetime, types) - } else { - (false, None, opt_vec::Empty) }; // Assemble and push the result. segments.push(PathSegmentAndBoundSet { segment: ast::PathSegment { identifier: identifier, - lifetime: optional_lifetime, + lifetimes: lifetimes, types: types, }, bound_set: bound_set @@ -1609,11 +1584,11 @@ impl Parser { pub fn parse_lifetime(&self) -> ast::Lifetime { match *self.token { token::LIFETIME(i) => { - let span = self.span; + let span = *self.span; self.bump(); return ast::Lifetime { id: ast::DUMMY_NODE_ID, - span: *span, + span: span, ident: i }; } @@ -4856,7 +4831,7 @@ impl Parser { segments: path.move_iter().map(|identifier| { ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect() @@ -4892,7 +4867,7 @@ impl Parser { segments: path.move_iter().map(|identifier| { ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect() @@ -4910,7 +4885,7 @@ impl Parser { segments: path.move_iter().map(|identifier| { ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect() @@ -4932,7 +4907,7 @@ impl Parser { segments: path.move_iter().map(|identifier| { ast::PathSegment { identifier: identifier, - lifetime: None, + lifetimes: opt_vec::Empty, types: opt_vec::Empty, } }).collect() diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 08c2ae88e4fab..ed8eb4b542710 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1566,23 +1566,30 @@ fn print_path_(s: @ps, } } - if segment.lifetime.is_some() || !segment.types.is_empty() { + if !segment.lifetimes.is_empty() || !segment.types.is_empty() { if colons_before_params { word(s.s, "::") } word(s.s, "<"); - for lifetime in segment.lifetime.iter() { - print_lifetime(s, lifetime); - if !segment.types.is_empty() { + let mut comma = false; + for lifetime in segment.lifetimes.iter() { + if comma { word_space(s, ",") } + print_lifetime(s, lifetime); + comma = true; } - commasep(s, - inconsistent, - segment.types.map_to_vec(|t| (*t).clone()), - print_type); + if !segment.types.is_empty() { + if comma { + word_space(s, ",") + } + commasep(s, + inconsistent, + segment.types.map_to_vec(|t| (*t).clone()), + print_type); + } word(s.s, ">") } @@ -1905,7 +1912,8 @@ pub fn print_meta_item(s: @ps, item: &ast::MetaItem) { pub fn print_view_path(s: @ps, vp: &ast::view_path) { match vp.node { ast::view_path_simple(ident, ref path, _) => { - if path.segments.last().identifier != ident { + // FIXME(#6993) can't compare identifiers directly here + if path.segments.last().identifier.name != ident.name { print_ident(s, ident); space(s.s); word_space(s, "="); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 09ee87d170148..60127be87b6ef 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -90,7 +90,32 @@ pub trait Visitor { walk_struct_def(self, s, i, g, n, e) } fn visit_struct_field(&mut self, s:@struct_field, e:E) { walk_struct_field(self, s, e) } - fn visit_mac(&mut self, m:&mac, e:E) { walk_mac(self, m, e); } + fn visit_opt_lifetime_ref(&mut self, + _span: Span, + opt_lifetime: &Option, + env: E) { + /*! + * Visits an optional reference to a lifetime. The `span` is + * the span of some surrounding reference should opt_lifetime + * be None. + */ + match *opt_lifetime { + Some(ref l) => self.visit_lifetime_ref(l, env), + None => () + } + } + fn visit_lifetime_ref(&mut self, _lifetime: &Lifetime, _e: E) { + /*! Visits a reference to a lifetime */ + } + fn visit_lifetime_decl(&mut self, _lifetime: &Lifetime, _e: E) { + /*! Visits a declaration of a lifetime */ + } + fn visit_explicit_self(&mut self, es: &explicit_self, e: E) { + walk_explicit_self(self, es, e) + } + fn visit_mac(&mut self, macro:&mac, e:E) { + walk_mac(self, macro, e) + } } pub fn walk_crate>(visitor: &mut V, crate: &Crate, env: E) { @@ -119,6 +144,18 @@ pub fn walk_local>(visitor: &mut V, local: &Local, env: E) } } +fn walk_explicit_self>(visitor: &mut V, + explicit_self: &explicit_self, + env: E) { + match explicit_self.node { + sty_static | sty_value(_) | sty_box(_) | sty_uniq(_) => { + } + sty_region(ref lifetime, _) => { + visitor.visit_opt_lifetime_ref(explicit_self.span, lifetime, env) + } + } +} + fn walk_trait_ref>(visitor: &mut V, trait_ref: &ast::trait_ref, env: E) { @@ -221,8 +258,11 @@ pub fn skip_ty>(_: &mut V, _: &Ty, _: E) { pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { match typ.node { ty_box(ref mutable_type) | ty_uniq(ref mutable_type) | - ty_vec(ref mutable_type) | ty_ptr(ref mutable_type) | - ty_rptr(_, ref mutable_type) => { + ty_vec(ref mutable_type) | ty_ptr(ref mutable_type) => { + visitor.visit_ty(mutable_type.ty, env) + } + ty_rptr(ref lifetime, ref mutable_type) => { + visitor.visit_opt_lifetime_ref(typ.span, lifetime, env.clone()); visitor.visit_ty(mutable_type.ty, env) } ty_tup(ref tuple_element_types) => { @@ -231,19 +271,27 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { } } ty_closure(ref function_declaration) => { - for argument in function_declaration.decl.inputs.iter() { + for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&argument.ty, env.clone()) - } - visitor.visit_ty(&function_declaration.decl.output, env.clone()); - for bounds in function_declaration.bounds.iter() { + } + visitor.visit_ty(&function_declaration.decl.output, env.clone()); + for bounds in function_declaration.bounds.iter() { walk_ty_param_bounds(visitor, bounds, env.clone()) - } + } + visitor.visit_opt_lifetime_ref( + typ.span, + &function_declaration.region, + env.clone()); + walk_lifetime_decls(visitor, &function_declaration.lifetimes, + env.clone()); } ty_bare_fn(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&argument.ty, env.clone()) } - visitor.visit_ty(&function_declaration.decl.output, env.clone()) + visitor.visit_ty(&function_declaration.decl.output, env.clone()); + walk_lifetime_decls(visitor, &function_declaration.lifetimes, + env.clone()); } ty_path(ref path, ref bounds, _) => { walk_path(visitor, path, env.clone()); @@ -262,10 +310,21 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { } } +fn walk_lifetime_decls>(visitor: &mut V, + lifetimes: &OptVec, + env: E) { + for l in lifetimes.iter() { + visitor.visit_lifetime_decl(l, env.clone()); + } +} + pub fn walk_path>(visitor: &mut V, path: &Path, env: E) { for segment in path.segments.iter() { for typ in segment.types.iter() { - visitor.visit_ty(typ, env.clone()) + visitor.visit_ty(typ, env.clone()); + } + for lifetime in segment.lifetimes.iter() { + visitor.visit_lifetime_ref(lifetime, env.clone()); } } } @@ -354,6 +413,7 @@ pub fn walk_generics>(visitor: &mut V, for type_parameter in generics.ty_params.iter() { walk_ty_param_bounds(visitor, &type_parameter.bounds, env.clone()) } + walk_lifetime_decls(visitor, &generics.lifetimes, env); } pub fn walk_fn_decl>(visitor: &mut V, @@ -385,18 +445,31 @@ pub fn walk_fn>(visitor: &mut V, function_kind: &fn_kind, function_declaration: &fn_decl, function_body: &Block, - _: Span, + _span: Span, _: NodeId, env: E) { walk_fn_decl(visitor, function_declaration, env.clone()); - let generics = generics_of_fn(function_kind); - visitor.visit_generics(&generics, env.clone()); + + match *function_kind { + fk_item_fn(_, generics, _, _) => { + visitor.visit_generics(generics, env.clone()); + } + fk_method(_, generics, method) => { + visitor.visit_generics(generics, env.clone()); + + visitor.visit_explicit_self(&method.explicit_self, env.clone()); + } + fk_anon(*) | fk_fn_block(*) => { + } + } + visitor.visit_block(function_body, env) } pub fn walk_ty_method>(visitor: &mut V, - method_type: &TypeMethod, - env: E) { + method_type: &TypeMethod, + env: E) { + visitor.visit_explicit_self(&method_type.explicit_self, env.clone()); for argument_type in method_type.decl.inputs.iter() { visitor.visit_ty(&argument_type.ty, env.clone()) } diff --git a/src/test/compile-fail/bad-mid-path-type-params.rs b/src/test/compile-fail/bad-mid-path-type-params.rs index f9f4ccf0f0751..90d6147f0eda3 100644 --- a/src/test/compile-fail/bad-mid-path-type-params.rs +++ b/src/test/compile-fail/bad-mid-path-type-params.rs @@ -28,10 +28,11 @@ impl Trait for S2 { } } -fn main() { +fn foo<'a>() { let _ = S::new::(1, 1.0); //~ ERROR the impl referenced by this path has 1 type parameter, but 0 type parameters were supplied - let _ = S::<'self,int>::new::(1, 1.0); //~ ERROR this impl has no lifetime parameter + let _ = S::<'a,int>::new::(1, 1.0); //~ ERROR expected 0 lifetime parameter(s) let _: S2 = Trait::new::(1, 1.0); //~ ERROR the trait referenced by this path has 1 type parameter, but 0 type parameters were supplied - let _: S2 = Trait::<'self,int>::new::(1, 1.0); //~ ERROR this trait has no lifetime parameter + let _: S2 = Trait::<'a,int>::new::(1, 1.0); //~ ERROR expected 0 lifetime parameter(s) } +fn main() {} diff --git a/src/test/compile-fail/class-method-missing.rs b/src/test/compile-fail/class-method-missing.rs index f3c5ab2019dae..ca97a8997689f 100644 --- a/src/test/compile-fail/class-method-missing.rs +++ b/src/test/compile-fail/class-method-missing.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:missing method `eat` trait animal { fn eat(&self); } @@ -18,6 +17,7 @@ struct cat { } impl animal for cat { + //~^ ERROR not all trait methods implemented, missing: `eat` } fn cat(in_x : uint) -> cat { diff --git a/src/test/compile-fail/core-tls-store-pointer.rs b/src/test/compile-fail/core-tls-store-pointer.rs index 4f60391892da3..70573ca4f180c 100644 --- a/src/test/compile-fail/core-tls-store-pointer.rs +++ b/src/test/compile-fail/core-tls-store-pointer.rs @@ -13,6 +13,6 @@ use std::local_data; local_data_key!(key: @&int) -//~^ ERROR only 'static is allowed +//~^ ERROR missing lifetime specifier fn main() {} diff --git a/src/test/compile-fail/issue-3344.rs b/src/test/compile-fail/issue-3344.rs index 9a3fff453f3a0..1a649c8ad2f83 100644 --- a/src/test/compile-fail/issue-3344.rs +++ b/src/test/compile-fail/issue-3344.rs @@ -9,7 +9,7 @@ // except according to those terms. struct thing(uint); -impl Ord for thing { //~ ERROR missing method `lt` +impl Ord for thing { //~ ERROR not all trait methods implemented, missing: `lt` fn le(&self, other: &thing) -> bool { **self < **other } fn ge(&self, other: &thing) -> bool { **self < **other } } diff --git a/src/test/compile-fail/issue-4335.rs b/src/test/compile-fail/issue-4335.rs index 5b6240d8ac83b..032b2564f4fb2 100644 --- a/src/test/compile-fail/issue-4335.rs +++ b/src/test/compile-fail/issue-4335.rs @@ -10,7 +10,7 @@ fn id(t: T) -> T { t } -fn f<'r, T>(v: &'r T) -> &'r fn()->T { id::<&'r fn()->T>(|| *v) } //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements +fn f<'r, T>(v: &'r T) -> &'r fn()->T { id::<&'r fn()->T>(|| *v) } //~ ERROR cannot infer an appropriate lifetime fn main() { let v = &5; diff --git a/src/test/compile-fail/issue-5216.rs b/src/test/compile-fail/issue-5216.rs index 9f88ba72f059a..8205a6dae6470 100644 --- a/src/test/compile-fail/issue-5216.rs +++ b/src/test/compile-fail/issue-5216.rs @@ -9,12 +9,12 @@ // except according to those terms. fn f() { } -struct S(&fn()); //~ ERROR Illegal anonymous lifetime -pub static C: S = S(f); //~ ERROR Illegal anonymous lifetime +struct S(&fn()); //~ ERROR missing lifetime specifier +pub static C: S = S(f); fn g() { } -type T = &fn(); //~ ERROR Illegal anonymous lifetime -pub static D: T = g; //~ ERROR Illegal anonymous lifetime +type T = &fn(); //~ ERROR missing lifetime specifier +pub static D: T = g; fn main() {} diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index fb85edf699a9b..5fe9b13f83bef 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -8,29 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[feature(managed_boxes)]; +trait Repeat { fn get(&self) -> A; } -trait repeat { fn get(&self) -> A; } - -impl repeat for @A { - fn get(&self) -> A { **self } +impl Repeat for A { + fn get(&self) -> A { self.clone() } } -fn repeater(v: @A) -> @repeat { - // Note: owned kind is not necessary as A appears in the trait type - @v as @repeat // No +fn repeater(v: A) -> ~Repeat: { + ~v as ~Repeat: // No } fn main() { // Error results because the type of is inferred to be - // @repeat<&'blk int> where blk is the lifetime of the block below. + // ~Repeat<&'blk int> where blk is the lifetime of the block below. - let y = { //~ ERROR lifetime of variable does not enclose its declaration - let x: &'blk int = &3; - repeater(@x) + let y = { + let tmp0 = 3; + let tmp1 = &tmp0; //~ ERROR borrowed value does not live long enough + repeater(tmp1) }; assert!(3 == *(y.get())); - //~^ ERROR dereference of reference outside its lifetime - //~^^ ERROR automatically borrowed pointer is not valid at the time of borrow - //~^^^ ERROR lifetime of return value does not outlive the function call } diff --git a/src/test/compile-fail/missing-derivable-attr.rs b/src/test/compile-fail/missing-derivable-attr.rs index 70c16e0baefb8..4e45f33fe9b14 100644 --- a/src/test/compile-fail/missing-derivable-attr.rs +++ b/src/test/compile-fail/missing-derivable-attr.rs @@ -20,7 +20,7 @@ impl MyEq for int { fn eq(&self, other: &int) -> bool { *self == *other } } -impl MyEq for A {} //~ ERROR missing method +impl MyEq for A {} //~ ERROR not all trait methods implemented, missing: `eq` fn main() { } diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs index 4fff5a6f87c78..ff13548b4946f 100644 --- a/src/test/compile-fail/regions-addr-of-arg.rs +++ b/src/test/compile-fail/regions-addr-of-arg.rs @@ -8,12 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Check that taking the address of an argument yields a lifetime +// bounded by the current function call. + fn foo(a: int) { let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough } fn bar(a: int) { - let _q: &'blk int = &a; + let _q: &int = &a; +} + +fn zed<'a>(a: int) -> &'a int { + &a //~ ERROR borrowed value does not live long enough } fn main() { diff --git a/src/test/compile-fail/regions-addr-of-self.rs b/src/test/compile-fail/regions-addr-of-self.rs index 3a480a7e9631c..b7be0dd7b9b1e 100644 --- a/src/test/compile-fail/regions-addr-of-self.rs +++ b/src/test/compile-fail/regions-addr-of-self.rs @@ -14,12 +14,12 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { - let p: &'static mut uint = &mut self.cats_chased; //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements + let p: &'static mut uint = &mut self.cats_chased; //~ ERROR cannot infer an appropriate lifetime *p += 1u; } pub fn chase_cat_2(&mut self) { - let p: &'blk mut uint = &mut self.cats_chased; + let p: &mut uint = &mut self.cats_chased; *p += 1u; } } diff --git a/src/test/compile-fail/regions-addr-of-upvar-self.rs b/src/test/compile-fail/regions-addr-of-upvar-self.rs index 9cedf86f35056..2f60898cfeef1 100644 --- a/src/test/compile-fail/regions-addr-of-upvar-self.rs +++ b/src/test/compile-fail/regions-addr-of-upvar-self.rs @@ -17,7 +17,7 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { let _f = || { - let p: &'static mut uint = &mut self.food; //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements + let p: &'static mut uint = &mut self.food; //~ ERROR cannot infer an appropriate lifetime *p = 3u; }; } diff --git a/src/test/compile-fail/regions-fn-subtyping.rs b/src/test/compile-fail/regions-fn-subtyping.rs index 5928d31a66860..cad73daa46b15 100644 --- a/src/test/compile-fail/regions-fn-subtyping.rs +++ b/src/test/compile-fail/regions-fn-subtyping.rs @@ -24,9 +24,9 @@ fn test_fn<'x,'y,'z,T>(_x: &'x T, _y: &'y T, _z: &'z T) { of::<&fn<'b>(&'b T)>()); subtype::<&fn<'b>(&'b T)>( - of::<&fn<'x>(&'x T)>()); + of::<&fn(&'x T)>()); - subtype::<&fn<'x>(&'x T)>( + subtype::<&fn(&'x T)>( of::<&fn<'b>(&'b T)>()); //~ ERROR mismatched types subtype::<&fn<'a,'b>(&'a T, &'b T)>( @@ -36,9 +36,9 @@ fn test_fn<'x,'y,'z,T>(_x: &'x T, _y: &'y T, _z: &'z T) { of::<&fn<'a,'b>(&'a T, &'b T)>()); //~ ERROR mismatched types subtype::<&fn<'a,'b>(&'a T, &'b T)>( - of::<&fn<'x,'y>(&'x T, &'y T)>()); + of::<&fn(&'x T, &'y T)>()); - subtype::<&fn<'x,'y>(&'x T, &'y T)>( + subtype::<&fn(&'x T, &'y T)>( of::<&fn<'a,'b>(&'a T, &'b T)>()); //~ ERROR mismatched types } diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs index 66ab4b7705433..fb31e477ba279 100644 --- a/src/test/compile-fail/regions-free-region-ordering-callee.rs +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -26,7 +26,7 @@ fn ordering2<'a, 'b>(x: &'a &'b uint, y: &'a uint) -> &'b uint { fn ordering3<'a, 'b>(x: &'a uint, y: &'b uint) -> &'a &'b uint { // Do not infer an ordering from the return value. let z: &'b uint = &*x; - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements + //~^ ERROR cannot infer an appropriate lifetime fail!(); } diff --git a/src/test/compile-fail/regions-free-region-ordering-caller.rs b/src/test/compile-fail/regions-free-region-ordering-caller.rs index c9859899ea4f1..58fa437a6a365 100644 --- a/src/test/compile-fail/regions-free-region-ordering-caller.rs +++ b/src/test/compile-fail/regions-free-region-ordering-caller.rs @@ -14,12 +14,6 @@ struct Paramd<'self> { x: &'self uint } -fn call1<'a>(x: &'a uint) { - let y: uint = 3; - let z: &'a &'blk uint = &(&y); - //~^ ERROR pointer has a longer lifetime than the data it references -} - fn call2<'a, 'b>(a: &'a uint, b: &'b uint) { let z: Option<&'b &'a uint> = None; //~^ ERROR pointer has a longer lifetime than the data it references diff --git a/src/test/compile-fail/regions-free-region-ordering-caller1.rs b/src/test/compile-fail/regions-free-region-ordering-caller1.rs new file mode 100644 index 0000000000000..1408f75be896e --- /dev/null +++ b/src/test/compile-fail/regions-free-region-ordering-caller1.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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 various ways to construct a pointer with a longer lifetime +// than the thing it points at and ensure that they result in +// errors. See also regions-free-region-ordering-callee.rs + +fn call1<'a>(x: &'a uint) { + // Test that creating a pointer like + // &'a &'z uint requires that 'a <= 'z: + let y: uint = 3; + let z: &'a & uint = &(&y); + //~^ ERROR borrowed value does not live long enough + //~^^ ERROR borrowed value does not live long enough +} + +fn main() {} diff --git a/src/test/compile-fail/regions-in-consts.rs b/src/test/compile-fail/regions-in-consts.rs index c34e5fb29de5a..9f2facf4e1f9f 100644 --- a/src/test/compile-fail/regions-in-consts.rs +++ b/src/test/compile-fail/regions-in-consts.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -static c_x: &'blk int = &22; //~ ERROR Illegal lifetime 'blk: only 'static is allowed here -static c_y: &int = &22; //~ ERROR Illegal anonymous lifetime: only 'static is allowed here +static c_y: &int = &22; //~ ERROR missing lifetime specifier static c_z: &'static int = &22; fn main() { diff --git a/src/test/compile-fail/regions-in-type-items.rs b/src/test/compile-fail/regions-in-enums-anon.rs similarity index 73% rename from src/test/compile-fail/regions-in-type-items.rs rename to src/test/compile-fail/regions-in-enums-anon.rs index a30a6772bfd21..5c7a37d0359a4 100644 --- a/src/test/compile-fail/regions-in-type-items.rs +++ b/src/test/compile-fail/regions-in-enums-anon.rs @@ -8,16 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct item_ty_yes0<'self> { - x: &'self uint -} - -struct item_ty_yes1<'self> { - x: &'self uint -} +// Test that anonymous lifetimes are not permitted in enum declarations -struct item_ty_yes2 { - x: &'a uint //~ ERROR only 'self is allowed +enum Foo { + Bar(&int) //~ ERROR missing lifetime specifier } fn main() {} diff --git a/src/test/compile-fail/regions-in-enums.rs b/src/test/compile-fail/regions-in-enums.rs index f189deef32e5d..0a221b9a53390 100644 --- a/src/test/compile-fail/regions-in-enums.rs +++ b/src/test/compile-fail/regions-in-enums.rs @@ -8,17 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that lifetimes must be declared for use on enums. +// See also regions-undeclared.rs + enum yes0<'lt> { - // This will eventually be legal (and in fact the only way): - X3(&'lt uint) //~ ERROR Illegal lifetime 'lt: only 'self is allowed + X3(&'lt uint) } enum yes1<'self> { X4(&'self uint) } -enum yes2 { - X5(&'foo uint) //~ ERROR Illegal lifetime 'foo: only 'self is allowed +enum no0 { + X5(&'foo uint) //~ ERROR use of undeclared lifetime name `'foo` +} + +enum no1 { + X6(&'self uint) //~ ERROR use of undeclared lifetime name `'self` } fn main() {} diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs b/src/test/compile-fail/regions-in-structs-anon.rs similarity index 63% rename from src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs rename to src/test/compile-fail/regions-in-structs-anon.rs index f7c9e5bda3b4f..0f2036a56cdd6 100644 --- a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs +++ b/src/test/compile-fail/regions-in-structs-anon.rs @@ -8,19 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[feature(managed_boxes)]; +// Test that anonymous lifetimes are not permitted in struct declarations -struct invariant<'self> { - f: @mut &'self int +struct Foo { + x: &int //~ ERROR missing lifetime specifier } -fn to_same_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'r> = bi; -} - -fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> { - bi //~ ERROR mismatched types -} - -fn main() { -} +fn main() {} diff --git a/src/test/compile-fail/regions-in-structs.rs b/src/test/compile-fail/regions-in-structs.rs index c4f1a8ae465ec..1e74fa4adb9f6 100644 --- a/src/test/compile-fail/regions-in-structs.rs +++ b/src/test/compile-fail/regions-in-structs.rs @@ -8,16 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct yes0<'self> { - x: &uint, //~ ERROR Illegal anonymous lifetime: anonymous lifetimes are not permitted here -} - struct yes1<'self> { x: &'self uint, } -struct yes2<'self> { - x: &'foo uint, //~ ERROR Illegal lifetime 'foo: only 'self is allowed +struct yes2<'a> { + x: &'a uint, } +struct StructDecl { + a: &'a int, //~ ERROR use of undeclared lifetime name `'a` + b: &'self int, //~ ERROR use of undeclared lifetime name `'self` +} + + fn main() {} diff --git a/src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs b/src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs deleted file mode 100644 index 83e39ebd9f4d0..0000000000000 --- a/src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2012 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. - -struct contravariant<'self> { - f: &'self int -} - -fn to_same_lifetime<'r>(bi: contravariant<'r>) { - let bj: contravariant<'r> = bi; -} - -fn to_shorter_lifetime<'r>(bi: contravariant<'r>) { - let bj: contravariant<'blk> = bi; -} - -fn to_longer_lifetime<'r>(bi: contravariant<'r>) -> contravariant<'static> { - bi //~ ERROR mismatched types -} - -fn main() { -} diff --git a/src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs b/src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs deleted file mode 100644 index 3fcc5184b4a7c..0000000000000 --- a/src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2012 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. - -// Contravariant with respect to a region: -// -// You can upcast to a *smaller region* but not a larger one. This is -// the normal case. - -struct contravariant<'self> { - f: &'static fn() -> &'self int -} - -fn to_same_lifetime<'r>(bi: contravariant<'r>) { - let bj: contravariant<'r> = bi; -} - -fn to_shorter_lifetime<'r>(bi: contravariant<'r>) { - let bj: contravariant<'blk> = bi; -} - -fn to_longer_lifetime<'r>(bi: contravariant<'r>) -> contravariant<'static> { - bi //~ ERROR mismatched types -} - -fn main() { -} diff --git a/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs b/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs deleted file mode 100644 index 4b26e6b602163..0000000000000 --- a/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2012 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. - -// Covariant with respect to a region: -// -// You can upcast to a *larger region* but not a smaller one. - -struct covariant<'self> { - f: &'static fn(x: &'self int) -> int -} - -fn to_same_lifetime<'r>(bi: covariant<'r>) { - let bj: covariant<'r> = bi; -} - -fn to_shorter_lifetime<'r>(bi: covariant<'r>) { - let bj: covariant<'blk> = bi; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime -} - -fn to_longer_lifetime<'r>(bi: covariant<'r>) -> covariant<'static> { - bi -} - -fn main() { -} diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs b/src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs deleted file mode 100644 index 0d4d4056a4401..0000000000000 --- a/src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2012 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. - -// Invariance with respect to a region: -// -// You cannot convert between regions. - -struct invariant<'self> { - f: &'self fn(x: &'self int) -> &'self int -} - -fn to_same_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'r> = bi; -} - -fn to_shorter_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'blk> = bi; //~ ERROR mismatched types -} - -fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> { - bi //~ ERROR mismatched types -} - -fn main() { -} diff --git a/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs b/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs index 4c3338d2e1d0c..5c979955ec9ae 100644 --- a/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs +++ b/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs @@ -13,7 +13,7 @@ // contains region pointers struct foo(~fn(x: &int)); -fn take_foo(x: foo<'static>) {} //~ ERROR no region bound is allowed on `foo` +fn take_foo(x: foo<'static>) {} //~ ERROR wrong number of lifetime parameters fn main() { } diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs b/src/test/compile-fail/regions-name-duplicated.rs similarity index 54% rename from src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs rename to src/test/compile-fail/regions-name-duplicated.rs index 522e5675bfe69..518fe0b00b6ce 100644 --- a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs +++ b/src/test/compile-fail/regions-name-duplicated.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,19 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[feature(managed_boxes)]; - -struct invariant<'self> { - f: @mut [&'self int] -} - -fn to_same_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'r> = bi; +struct Foo<'a, 'a> { //~ ERROR lifetime name `'a` declared twice + x: &'a int } -fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> { - bi //~ ERROR mismatched types -} - -fn main() { -} +fn main() {} diff --git a/src/test/compile-fail/regions-blk.rs b/src/test/compile-fail/regions-name-static.rs similarity index 53% rename from src/test/compile-fail/regions-blk.rs rename to src/test/compile-fail/regions-name-static.rs index 893e4c411780b..c1170654dd294 100644 --- a/src/test/compile-fail/regions-blk.rs +++ b/src/test/compile-fail/regions-name-static.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,18 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(cond: bool) { - let x = 5; - let mut y: &'blk int = &x; - - let mut z: &'blk int; - if cond { - z = &x; //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements - } else { - let w: &'blk int = &x; - z = w; - } +struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `static` + x: &'static int } -fn main() { -} +fn main() {} diff --git a/src/test/compile-fail/regions-name-undeclared.rs b/src/test/compile-fail/regions-name-undeclared.rs new file mode 100644 index 0000000000000..abff33e051491 --- /dev/null +++ b/src/test/compile-fail/regions-name-undeclared.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2013 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. + +// Check that lifetime resolver enforces the lifetime name scoping +// rules correctly in various scenarios. + +struct Foo<'a> { + x: &'a int +} + +impl<'a> Foo<'a> { + // &'a is inherited: + fn m1(&self, arg: &'a int) { } + fn m2(&'a self) { } + fn m3(&self, arg: Foo<'a>) { } + + // &'b is not: + fn m4(&self, arg: &'b int) { } //~ ERROR undeclared lifetime + fn m5(&'b self) { } //~ ERROR undeclared lifetime + fn m6(&self, arg: Foo<'b>) { } //~ ERROR undeclared lifetime +} + +fn bar<'a>(x: &'a int) { + // &'a is visible to code: + let y: &'a int = x; + + // &'a is not visible to *items*: + type X = Option<&'a int>; //~ ERROR undeclared lifetime + enum E { + E1(&'a int) //~ ERROR undeclared lifetime + } + struct S { + f: &'a int //~ ERROR undeclared lifetime + } + fn f(a: &'a int) { } //~ ERROR undeclared lifetime + + // &'a CAN be declared on functions and used then: + fn g<'a>(a: &'a int) { } // OK + fn h(a: &fn<'a>(&'a int)) { } // OK +} + +// Test nesting of lifetimes in fn type declarations +fn fn_types(a: &'a int, //~ ERROR undeclared lifetime + b: &fn<'a>(a: &'a int, + b: &'b int, //~ ERROR undeclared lifetime + c: &fn<'b>(a: &'a int, + b: &'b int), + d: &'b int), //~ ERROR undeclared lifetime + c: &'a int) //~ ERROR undeclared lifetime +{ +} + +pub fn main() {} diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index df7831e905056..ee6ed3b0d76f6 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -12,7 +12,7 @@ // some point regions-ret-borrowed reported an error but this file did // not, due to special hardcoding around the anonymous region. -fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R { +fn with(f: &fn<'a>(x: &'a int) -> R) -> R { f(&3) } diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs b/src/test/compile-fail/regions-undeclared.rs similarity index 56% rename from src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs rename to src/test/compile-fail/regions-undeclared.rs index 9853741d77a11..fcf3a73b19f0d 100644 --- a/src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs +++ b/src/test/compile-fail/regions-undeclared.rs @@ -8,23 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[feature(managed_boxes)]; +static c_x: &'blk int = &22; //~ ERROR use of undeclared lifetime name `'blk` -struct invariant<'self> { - f: @mut &'self int +enum EnumDecl { + Foo(&'a int), //~ ERROR use of undeclared lifetime name `'a` + Bar(&'self int), //~ ERROR use of undeclared lifetime name `'self` } -fn to_same_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'r> = bi; -} - -fn to_shorter_lifetime<'r>(bi: invariant<'r>) { - let bj: invariant<'blk> = bi; //~ ERROR mismatched types -} - -fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> { - bi //~ ERROR mismatched types -} +fn fnDecl(x: &'a int, //~ ERROR use of undeclared lifetime name `'a` + y: &'self int) //~ ERROR use of undeclared lifetime name `'self` +{} fn main() { } diff --git a/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs b/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs new file mode 100644 index 0000000000000..77a54fec7bf7c --- /dev/null +++ b/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs @@ -0,0 +1,39 @@ +// Copyright 2012 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 a type which is covariant with respect to its region +// parameter yields an error when used in a contravariant way. +// +// Note: see variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +// `S` is contravariant with respect to both parameters. +struct S<'a, 'b> { + f: &'a int, + g: &'b int, +} + +fn use_<'short,'long>(c: S<'long, 'short>, + s: &'short int, + l: &'long int, + _where:Option<&'short &'long ()>) { + + let _: S<'long, 'short> = c; // OK + let _: S<'short, 'short> = c; // OK + + // Test whether S<_,'short> <: S<_,'long>. Since + // 'short <= 'long, this would be true if the Contravariant type were + // covariant with respect to its parameter 'a. + + let _: S<'long, 'long> = c; //~ ERROR mismatched types + //~^ ERROR cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs b/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs new file mode 100644 index 0000000000000..3f0161d9deb2b --- /dev/null +++ b/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs @@ -0,0 +1,37 @@ +// Copyright 2012 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 a type which is covariant with respect to its region +// parameter yields an error when used in a contravariant way. +// +// Note: see variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +// This is contravariant with respect to 'a, meaning that +// Contravariant<'foo> <: Contravariant<'static> because +// 'foo <= 'static +struct Contravariant<'a> { + f: &'a int +} + +fn use_<'short,'long>(c: Contravariant<'short>, + s: &'short int, + l: &'long int, + _where:Option<&'short &'long ()>) { + + // Test whether Contravariant<'short> <: Contravariant<'long>. Since + // 'short <= 'long, this would be true if the Contravariant type were + // covariant with respect to its parameter 'a. + + let _: Contravariant<'long> = c; //~ ERROR mismatched types + //~^ ERROR cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs new file mode 100644 index 0000000000000..5cc3f1bdc3780 --- /dev/null +++ b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs @@ -0,0 +1,37 @@ +// Copyright 2012 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 a type which is covariant with respect to its region +// parameter yields an error when used in a contravariant way. +// +// Note: see variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +// This is covariant with respect to 'a, meaning that +// Covariant<'foo> <: Covariant<'static> because +// 'foo <= 'static +struct Covariant<'a> { + f: extern "Rust" fn(&'a int) +} + +fn use_<'short,'long>(c: Covariant<'long>, + s: &'short int, + l: &'long int, + _where:Option<&'short &'long ()>) { + + // Test whether Covariant<'long> <: Covariant<'short>. Since + // 'short <= 'long, this would be true if the Covariant type were + // contravariant with respect to its parameter 'a. + + let _: Covariant<'short> = c; //~ ERROR mismatched types + //~^ ERROR cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs b/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs new file mode 100644 index 0000000000000..0790c3f956a11 --- /dev/null +++ b/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs @@ -0,0 +1,33 @@ +// Copyright 2012 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 an invariant region parameter used in a contravariant way +// yields an error. +// +// Note: see variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +struct Invariant<'a> { + f: &'static mut &'a int +} + +fn use_<'short,'long>(c: Invariant<'long>, + s: &'short int, + l: &'long int, + _where:Option<&'short &'long ()>) { + + // Test whether Invariant<'long> <: Invariant<'short>. Since + // 'short <= 'long, this would be true if the Invariant type were + // contravariant with respect to its parameter 'a. + + let _: Invariant<'short> = c; //~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/compile-fail/regions-variance-invariant-use-covariant.rs b/src/test/compile-fail/regions-variance-invariant-use-covariant.rs new file mode 100644 index 0000000000000..9cdd05f8ebe18 --- /dev/null +++ b/src/test/compile-fail/regions-variance-invariant-use-covariant.rs @@ -0,0 +1,30 @@ +// Copyright 2012 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 a type which is invariant with respect to its region +// parameter used in a covariant way yields an error. +// +// Note: see variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +struct Invariant<'a> { + f: &'static mut &'a int +} + +fn use_<'b>(c: Invariant<'b>) { + + // For this assignment to be legal, Invariant<'b> <: Invariant<'static>. + // Since 'b <= 'static, this would be true if Invariant were covariant + // with respect to its parameter 'a. + + let _: Invariant<'static> = c; //~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/compile-fail/trait-impl-different-num-params.rs b/src/test/compile-fail/trait-impl-different-num-params.rs index 7039e05019958..940c1b3a237b8 100644 --- a/src/test/compile-fail/trait-impl-different-num-params.rs +++ b/src/test/compile-fail/trait-impl-different-num-params.rs @@ -13,7 +13,7 @@ trait foo { } impl foo for int { fn bar(&self) -> int { - //~^ ERROR method `bar` has 0 parameters but the trait has 1 + //~^ ERROR method `bar` has 0 parameter(s) but the trait has 1 *self } } diff --git a/src/test/compile-fail/variance-regions-direct.rs b/src/test/compile-fail/variance-regions-direct.rs new file mode 100644 index 0000000000000..ae8444f015e7e --- /dev/null +++ b/src/test/compile-fail/variance-regions-direct.rs @@ -0,0 +1,73 @@ +// Copyright 2012 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 correctly infer variance for region parameters in +// various self-contained types. + +// Regions that just appear in normal spots are contravariant: + +#[rustc_variance] +struct Test2<'a, 'b, 'c> { //~ ERROR region_params=[-, -, -] + x: &'a int, + y: &'b [int], + c: &'c str +} + +// Those same annotations in function arguments become covariant: + +#[rustc_variance] +struct Test3<'a, 'b, 'c> { //~ ERROR region_params=[+, +, +] + x: extern "Rust" fn(&'a int), + y: extern "Rust" fn(&'b [int]), + c: extern "Rust" fn(&'c str), +} + +// Mutability induces invariance: + +#[rustc_variance] +struct Test4<'a, 'b> { //~ ERROR region_params=[-, o] + x: &'a mut &'b int, +} + +// Mutability induces invariance, even when in a +// contravariant context: + +#[rustc_variance] +struct Test5<'a, 'b> { //~ ERROR region_params=[+, o] + x: extern "Rust" fn(&'a mut &'b int), +} + +// Invariance is a trap from which NO ONE CAN ESCAPE. +// In other words, even though the `&'b int` occurs in +// a argument list (which is contravariant), that +// argument list occurs in an invariant context. + +#[rustc_variance] +struct Test6<'a, 'b> { //~ ERROR region_params=[-, o] + x: &'a mut extern "Rust" fn(&'b int), +} + +// No uses at all is bivariant: + +#[rustc_variance] +struct Test7<'a> { //~ ERROR region_params=[*] + x: int +} + +// Try enums too. + +#[rustc_variance] +enum Test8<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o] + Test8A(extern "Rust" fn(&'a int)), + Test8B(&'b [int]), + Test8C(&'b mut &'c str), +} + +fn main() {} diff --git a/src/test/compile-fail/variance-regions-indirect.rs b/src/test/compile-fail/variance-regions-indirect.rs new file mode 100644 index 0000000000000..fa22bb41aa321 --- /dev/null +++ b/src/test/compile-fail/variance-regions-indirect.rs @@ -0,0 +1,42 @@ +// Copyright 2012 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 correctly infer variance for region parameters in +// case that involve multiple intracrate types. +// Try enums too. + +#[rustc_variance] +enum Base<'a, 'b, 'c, 'd> { //~ ERROR region_params=[+, -, o, *] + Test8A(extern "Rust" fn(&'a int)), + Test8B(&'b [int]), + Test8C(&'b mut &'c str), +} + +#[rustc_variance] +struct Derived1<'w, 'x, 'y, 'z> { //~ ERROR region_params=[*, o, -, +] + f: Base<'z, 'y, 'x, 'w> +} + +#[rustc_variance] // Combine - and + to yield o +struct Derived2<'a, 'b, 'c> { //~ ERROR region_params=[o, o, *] + f: Base<'a, 'a, 'b, 'c> +} + +#[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here) +struct Derived3<'a, 'b, 'c> { //~ ERROR region_params=[o, -, *] + f: Base<'a, 'b, 'a, 'c> +} + +#[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here) +struct Derived4<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o] + f: Base<'a, 'b, 'c, 'a> +} + +fn main() {} diff --git a/src/test/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/regions-dependent-addr-of.rs similarity index 100% rename from src/test/run-pass/region-dependent-addr-of.rs rename to src/test/run-pass/regions-dependent-addr-of.rs diff --git a/src/test/run-pass/region-dependent-autofn.rs b/src/test/run-pass/regions-dependent-autofn.rs similarity index 100% rename from src/test/run-pass/region-dependent-autofn.rs rename to src/test/run-pass/regions-dependent-autofn.rs diff --git a/src/test/run-pass/region-dependent-autoslice.rs b/src/test/run-pass/regions-dependent-autoslice.rs similarity index 100% rename from src/test/run-pass/region-dependent-autoslice.rs rename to src/test/run-pass/regions-dependent-autoslice.rs diff --git a/src/test/run-pass/regions-mock-tcx.rs b/src/test/run-pass/regions-mock-tcx.rs new file mode 100644 index 0000000000000..50a71278c0656 --- /dev/null +++ b/src/test/run-pass/regions-mock-tcx.rs @@ -0,0 +1,122 @@ +// Copyright 2012 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. + +// xfail-fast `use` standards don't resolve + +// Test a sample usage pattern for regions. Makes use of the +// following features: +// +// - Multiple lifetime parameters +// - Arenas + +extern mod extra; + +use extra::arena; +use extra::arena::Arena; +use std::hashmap::HashMap; +use std::cast; +use std::libc; +use std::mem; + +type Type<'tcx> = &'tcx TypeStructure<'tcx>; + +#[deriving(Eq)] +enum TypeStructure<'tcx> { + TypeInt, + TypeFunction(Type<'tcx>, Type<'tcx>), +} + +struct TypeContext<'tcx, 'ast> { + ty_arena: &'tcx Arena, + types: ~[Type<'tcx>], + type_table: HashMap>, + + ast_arena: &'ast Arena, + ast_counter: uint, +} + +impl<'tcx,'ast> TypeContext<'tcx, 'ast> { + fn new(ty_arena: &'tcx Arena, ast_arena: &'ast Arena) + -> TypeContext<'tcx, 'ast> { + TypeContext { ty_arena: ty_arena, + types: ~[], + type_table: HashMap::new(), + + ast_arena: ast_arena, + ast_counter: 0 } + } + + fn add_type(&mut self, s: TypeStructure<'tcx>) -> Type<'tcx> { + for &ty in self.types.iter() { + if *ty == s { + return ty; + } + } + + let ty = self.ty_arena.alloc(|| s); + self.types.push(ty); + ty + } + + fn set_type(&mut self, id: NodeId, ty: Type<'tcx>) -> Type<'tcx> { + self.type_table.insert(id, ty); + ty + } + + fn ast(&mut self, a: AstKind<'ast>) -> Ast<'ast> { + let id = self.ast_counter; + self.ast_counter += 1; + self.ast_arena.alloc(|| AstStructure { id: NodeId {id:id}, kind: a }) + } +} + +#[deriving(Eq, IterBytes)] +struct NodeId { + id: uint +} + +type Ast<'ast> = &'ast AstStructure<'ast>; + +struct AstStructure<'ast> { + id: NodeId, + kind: AstKind<'ast> +} + +enum AstKind<'ast> { + ExprInt, + ExprVar(uint), + ExprLambda(Ast<'ast>), +} + +fn compute_types<'tcx,'ast>(tcx: &mut TypeContext<'tcx,'ast>, + ast: Ast<'ast>) -> Type<'tcx> +{ + match ast.kind { + ExprInt | ExprVar(_) => { + let ty = tcx.add_type(TypeInt); + tcx.set_type(ast.id, ty) + } + ExprLambda(ast) => { + let arg_ty = tcx.add_type(TypeInt); + let body_ty = compute_types(tcx, ast); + let lambda_ty = tcx.add_type(TypeFunction(arg_ty, body_ty)); + tcx.set_type(ast.id, lambda_ty) + } + } +} + +pub fn main() { + let ty_arena = arena::Arena::new(); + let ast_arena = arena::Arena::new(); + let mut tcx = TypeContext::new(&ty_arena, &ast_arena); + let ast = tcx.ast(ExprInt); + let ty = compute_types(&mut tcx, ast); + assert_eq!(*ty, TypeInt); +} diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index e66a34b47bce0..6740230728c6c 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -30,7 +30,7 @@ struct Ccx { #[fixed_stack_segment] #[inline(never)] fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { - cast::transmute(libc::malloc(mem::size_of::>() + cast::transmute(libc::malloc(mem::size_of::>() as libc::size_t)) } } diff --git a/src/test/run-pass/region-return-interior-of-option.rs b/src/test/run-pass/regions-return-interior-of-option.rs similarity index 100% rename from src/test/run-pass/region-return-interior-of-option.rs rename to src/test/run-pass/regions-return-interior-of-option.rs diff --git a/src/test/run-pass/regions-variance-contravariant-use-contravariant.rs b/src/test/run-pass/regions-variance-contravariant-use-contravariant.rs new file mode 100644 index 0000000000000..ffc2f07a1530e --- /dev/null +++ b/src/test/run-pass/regions-variance-contravariant-use-contravariant.rs @@ -0,0 +1,32 @@ +// Copyright 2012 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 a type which is contravariant with respect to its region +// parameter compiles successfully when used in a contravariant way. +// +// Note: see compile-fail/variance-regions-*.rs for the tests that check that the +// variance inference works in the first place. + +struct Contravariant<'a> { + f: &'a int +} + +fn use_<'a>(c: Contravariant<'a>) { + let x = 3; + + // 'b winds up being inferred to this call. + // Contravariant<'a> <: Contravariant<'call> is true + // if 'call <= 'a, which is true, so no error. + collapse(&x, c); + + fn collapse<'b>(x: &'b int, c: Contravariant<'b>) { } +} + +pub fn main() {} diff --git a/src/test/run-pass/regions-variance-covariant-use-covariant.rs b/src/test/run-pass/regions-variance-covariant-use-covariant.rs new file mode 100644 index 0000000000000..7e0ca41501833 --- /dev/null +++ b/src/test/run-pass/regions-variance-covariant-use-covariant.rs @@ -0,0 +1,29 @@ +// Copyright 2012 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 a type which is covariant with respect to its region +// parameter is successful when used in a covariant way. +// +// Note: see compile-fail/variance-regions-*.rs for the tests that +// check that the variance inference works in the first place. + +// This is covariant with respect to 'a, meaning that +// Covariant<'foo> <: Covariant<'static> because +// 'foo <= 'static +struct Covariant<'a> { + f: extern "Rust" fn(&'a int) +} + +fn use_<'a>(c: Covariant<'a>) { + // OK Because Covariant<'a> <: Covariant<'static> iff 'a <= 'static + let _: Covariant<'static> = c; +} + +pub fn main() {}