From 511e7626aebe7a8a41c3be1c6fbd16c92e8f9386 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Aug 2012 09:59:50 -0700 Subject: [PATCH] Infer variance of types with respect to the region parameter. A similar approach could be used for type parameters. Fixes #2282. --- src/libcore/task.rs | 1 + src/libsyntax/ast_map.rs | 15 +- src/rustc/metadata/csearch.rs | 6 +- src/rustc/metadata/decoder.rs | 18 +- src/rustc/metadata/encoder.rs | 8 +- src/rustc/middle/astencode.rs | 10 +- src/rustc/middle/region.rs | 253 ++++++++++++++---- src/rustc/middle/trans/base.rs | 2 +- src/rustc/middle/ty.rs | 14 +- src/rustc/middle/typeck.rs | 2 +- src/rustc/middle/typeck/astconv.rs | 14 +- src/rustc/middle/typeck/check.rs | 194 +++++++------- src/rustc/middle/typeck/coherence.rs | 8 +- src/rustc/middle/typeck/collect.rs | 91 ++++--- src/rustc/middle/typeck/infer/combine.rs | 60 ++++- src/rustc/middle/typeck/infer/glb.rs | 6 +- src/rustc/middle/typeck/infer/lub.rs | 6 +- src/rustc/middle/typeck/infer/sub.rs | 6 +- src/rustc/middle/typeck/rscope.rs | 18 +- .../compile-fail/regions-creating-enums3.rs | 1 - .../compile-fail/regions-creating-enums4.rs | 1 - ...nfer-contravariance-due-to-immutability.rs | 18 ++ ...regions-infer-contravariance-due-to-ret.rs | 23 ++ .../regions-infer-covariance-due-to-arg.rs | 22 ++ ...ons-infer-invariance-due-to-arg-and-ret.rs | 22 ++ ...ns-infer-invariance-due-to-mutability-1.rs | 18 ++ ...ns-infer-invariance-due-to-mutability-2.rs | 18 ++ ...ns-infer-invariance-due-to-mutability-3.rs | 18 ++ ...ns-infer-invariance-due-to-mutability-4.rs | 18 ++ ...ions-infer-invariance-due-to-mutability.rs | 18 ++ ...regions-infer-contravariance-due-to-ret.rs | 18 ++ .../run-pass/regions-infer-contravariance.rs | 21 ++ src/test/run-pass/regions-trait.rs | 4 +- 33 files changed, 720 insertions(+), 232 deletions(-) create mode 100644 src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs create mode 100644 src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs create mode 100644 src/test/compile-fail/regions-infer-covariance-due-to-arg.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-mutability-3.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs create mode 100644 src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs create mode 100644 src/test/run-pass/regions-infer-contravariance-due-to-ret.rs create mode 100644 src/test/run-pass/regions-infer-contravariance.rs diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 85d1ee3da94c..8d6453f00597 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -770,6 +770,7 @@ enum AncestorList = option>; fn access_group(x: &TaskGroupArc, blk: fn(TaskGroupInner) -> U) -> U { unsafe { x.with(blk) } } + #[inline(always)] fn access_ancestors(x: &unsafe::Exclusive, blk: fn(x: &mut AncestorNode) -> U) -> U { diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index 2cc8cb23f2e4..9c90a422c53a 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -299,7 +299,20 @@ fn node_id_to_str(map: map, id: node_id, itr: ident_interner) -> ~str { fmt!{"unknown node (id=%d)", id} } some(node_item(item, path)) => { - fmt!{"item %s (id=%?)", path_ident_to_str(*path, item.ident, itr), id} + let path_str = path_ident_to_str(*path, item.ident, itr); + let item_str = match item.node { + item_const(*) => ~"const", + item_fn(*) => ~"fn", + item_mod(*) => ~"mod", + item_foreign_mod(*) => ~"foreign mod", + item_ty(*) => ~"ty", + item_enum(*) => ~"enum", + item_class(*) => ~"class", + item_trait(*) => ~"trait", + item_impl(*) => ~"impl", + item_mac(*) => ~"macro" + }; + fmt!("%s %s (id=%?)", item_str, path_str, id) } some(node_foreign_item(item, abi, path)) => { fmt!{"foreign item %s with abi %? (id=%?)", diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 52e6841b2ed8..09de007f0a56 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -127,7 +127,7 @@ fn get_type(tcx: ty::ctxt, def: ast::def_id) -> ty::ty_param_bounds_and_ty { } fn get_region_param(cstore: metadata::cstore::cstore, - def: ast::def_id) -> bool { + def: ast::def_id) -> option { let cdata = cstore::get_crate_data(cstore, def.crate); return decoder::get_region_param(cdata, def.node); } @@ -149,7 +149,9 @@ fn get_field_type(tcx: ty::ctxt, class_id: ast::def_id, class_id, def} ); debug!{"got field data %?", the_field}; let ty = decoder::item_type(def, the_field, tcx, cdata); - return {bounds: @~[], rp: false, ty: ty}; + return {bounds: @~[], + region_param: none, + ty: ty}; } // Given a def_id for an impl or class, return the traits it implements, diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index a1a2b7245eb9..b78be76dc690 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -237,11 +237,11 @@ fn item_ty_param_bounds(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) @bounds } -fn item_ty_region_param(item: ebml::doc) -> bool { - match ebml::maybe_get_doc(item, tag_region_param) { - some(_) => true, - none => false - } +fn item_ty_region_param(item: ebml::doc) -> option { + ebml::maybe_get_doc(item, tag_region_param).map(|doc| { + let d = ebml::ebml_deserializer(doc); + ty::deserialize_region_variance(d) + }) } fn item_ty_param_count(item: ebml::doc) -> uint { @@ -340,10 +340,14 @@ fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) item_ty_param_bounds(item, tcx, cdata) } else { @~[] }; let rp = item_ty_region_param(item); - return {bounds: tp_bounds, rp: rp, ty: t}; + return {bounds: tp_bounds, + region_param: rp, + ty: t}; } -fn get_region_param(cdata: cmd, id: ast::node_id) -> bool { +fn get_region_param(cdata: cmd, id: ast::node_id) + -> option { + let item = lookup_item(id, cdata.data); return item_ty_region_param(item); } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 7b2e2b8a2174..f37cdd331cca 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -81,8 +81,12 @@ fn encode_def_id(ebml_w: ebml::writer, id: def_id) { fn encode_region_param(ecx: @encode_ctxt, ebml_w: ebml::writer, it: @ast::item) { - let rp = ecx.tcx.region_paramd_items.contains_key(it.id); - if rp { do ebml_w.wr_tag(tag_region_param) { } } + let opt_rp = ecx.tcx.region_paramd_items.find(it.id); + for opt_rp.each |rp| { + do ebml_w.wr_tag(tag_region_param) { + ty::serialize_region_variance(ebml_w, rp); + } + } } fn encode_mutability(ebml_w: ebml::writer, mt: class_mutability) { diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs index 34ebeb2fe199..f6008ead9395 100644 --- a/src/rustc/middle/astencode.rs +++ b/src/rustc/middle/astencode.rs @@ -601,8 +601,10 @@ impl ebml::writer: ebml_writer_helpers { self.emit_bounds(ecx, bs); } } - do self.emit_rec_field(~"rp", 1u) { - self.emit_bool(tpbt.rp); + do self.emit_rec_field(~"region_param", 1u) { + ty::serialize_opt_region_variance( + self, + tpbt.region_param); } do self.emit_rec_field(~"ty", 2u) { self.emit_ty(ecx, tpbt.ty); @@ -817,8 +819,8 @@ impl ebml::ebml_deserializer: ebml_deserializer_decoder_helpers { bounds: self.read_rec_field(~"bounds", 0u, || { @self.read_to_vec(|| self.read_bounds(xcx) ) }), - rp: self.read_rec_field(~"rp", 1u, || { - self.read_bool() + region_param: self.read_rec_field(~"region_param", 1u, || { + ty::deserialize_opt_region_variance(self) }), ty: self.read_rec_field(~"ty", 2u, || { self.read_ty(xcx) diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index d3c34ceea4d8..aa2dc699940c 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -16,6 +16,7 @@ import syntax::ast_util::new_def_hash; import syntax::ast_map; import dvec::{DVec, dvec}; import metadata::csearch; +import ty::{region_variance, rv_covariant, rv_invariant, rv_contravariant}; import std::list; import std::list::list; @@ -365,8 +366,9 @@ fn resolve_crate(sess: session, def_map: resolve3::DefMap, // a worklist. We can then process the worklist, propagating indirect // dependencies until a fixed point is reached. -type region_paramd_items = hashmap; -type dep_map = hashmap>; +type region_paramd_items = hashmap; +type region_dep = {ambient_variance: region_variance, id: ast::node_id}; +type dep_map = hashmap>; type determine_rp_ctxt_ = { sess: session, @@ -381,42 +383,98 @@ type determine_rp_ctxt_ = { // true when we are within an item but not within a method. // see long discussion on region_is_relevant() - mut anon_implies_rp: bool + mut anon_implies_rp: bool, + + // encodes the context of the current type; invariant if + // mutable, covariant otherwise + mut ambient_variance: region_variance, }; enum determine_rp_ctxt { determine_rp_ctxt_(@determine_rp_ctxt_) } +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`. +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 determine_rp_ctxt { - fn add_rp(id: ast::node_id) { + fn add_variance(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. + fn add_rp(id: ast::node_id, variance: region_variance) { assert id != 0; - if self.region_paramd_items.insert(id, ()) { - debug!{"add region-parameterized item: %d (%s)", id, - ast_map::node_id_to_str(self.ast_map, id, - self.sess.parse_sess.interner)}; + let old_variance = self.region_paramd_items.find(id); + let joined_variance = match old_variance { + none => variance, + some(v) => join_variance(v, variance) + }; + + debug!["add_rp() variance for %s: %? == %? ^ %?", + ast_map::node_id_to_str(self.ast_map, id, + self.sess.parse_sess.interner), + joined_variance, old_variance, variance]; + + if some(joined_variance) != old_variance { + self.region_paramd_items.insert(id, joined_variance); self.worklist.push(id); - } else { - debug!{"item %d already region-parameterized", id}; } } - fn add_dep(from: ast::node_id, to: ast::node_id) { - debug!{"add dependency from %d -> %d (%s -> %s)", - from, to, + /// 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. + fn add_dep(from: ast::node_id) { + debug!["add dependency from %d -> %d (%s -> %s) with variance %?", + from, self.item_id, ast_map::node_id_to_str(self.ast_map, from, self.sess.parse_sess.interner), - ast_map::node_id_to_str(self.ast_map, to, - self.sess.parse_sess.interner)}; + ast_map::node_id_to_str(self.ast_map, self.item_id, + self.sess.parse_sess.interner), + copy self.ambient_variance]; let vec = match self.dep_map.find(from) { - some(vec) => {vec} + some(vec) => vec, none => { let vec = @dvec(); self.dep_map.insert(from, vec); vec } }; - if !vec.contains(to) { vec.push(to); } + let dep = {ambient_variance: self.ambient_variance, id: self.item_id}; + if !vec.contains(dep) { vec.push(dep); } } // Determines whether a reference to a region that appears in the @@ -460,7 +518,9 @@ impl determine_rp_ctxt { } } - fn with(item_id: ast::node_id, anon_implies_rp: bool, f: fn()) { + fn with(item_id: ast::node_id, + 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; @@ -471,6 +531,13 @@ impl determine_rp_ctxt { self.item_id = old_item_id; self.anon_implies_rp = old_anon_implies_rp; } + + fn with_ambient_variance(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(item: @ast::item, @@ -484,12 +551,17 @@ fn determine_rp_in_item(item: @ast::item, fn determine_rp_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk, - sp: span, - id: ast::node_id, + _sp: span, + _id: ast::node_id, &&cx: determine_rp_ctxt, visitor: visit::vt) { do cx.with(cx.item_id, false) { - visit::visit_fn(fk, decl, body, sp, id, cx, visitor); + do cx.with_ambient_variance(rv_contravariant) { + for decl.inputs.each |a| { visitor.visit_ty(a.ty, cx, visitor); } + } + visitor.visit_ty(decl.output, cx, visitor); + visitor.visit_ty_params(visit::tps_of_fn(fk), cx, visitor); + visitor.visit_block(body, cx, visitor); } } @@ -511,16 +583,18 @@ fn determine_rp_in_ty(ty: @ast::ty, // impl etc. So we can ignore it and its components. if cx.item_id == 0 { return; } - // if this type directly references a region, either via a - // region pointer like &r.ty or a region-parameterized path - // like path/r, add to the worklist/set + // 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) match ty.node { - ast::ty_rptr(r, _) | - ast::ty_path(@{rp: some(r), _}, _) => { - debug!{"referenced type with regions %s", - pprust::ty_to_str(ty, cx.sess.intr())}; + ast::ty_rptr(r, _) => { + debug!["referenced rptr type %s", + pprust::ty_to_str(ty, cx.sess.intr())]; + if cx.region_is_relevant(r) { - cx.add_rp(cx.item_id); + cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant)) } } @@ -528,7 +602,7 @@ fn determine_rp_in_ty(ty: @ast::ty, ast::ty_fn(ast::proto_block, _, _) if cx.anon_implies_rp => { debug!("referenced bare fn type with regions %s", pprust::ty_to_str(ty, cx.sess.intr())); - cx.add_rp(cx.item_id); + cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant)); } _ => {} @@ -543,13 +617,16 @@ fn determine_rp_in_ty(ty: @ast::ty, match cx.def_map.get(id) { ast::def_ty(did) | ast::def_class(did, _) => { if did.crate == ast::local_crate { - cx.add_dep(did.node, cx.item_id); + cx.add_dep(did.node); } else { let cstore = cx.sess.cstore; - if csearch::get_region_param(cstore, did) { - debug!{"reference to external, rp'd type %s", - pprust::ty_to_str(ty, cx.sess.intr())}; - cx.add_rp(cx.item_id); + match csearch::get_region_param(cstore, did) { + none => {} + some(variance) => { + debug!["reference to external, rp'd type %s", + pprust::ty_to_str(ty, cx.sess.intr())]; + cx.add_rp(cx.item_id, cx.add_variance(variance)) + } } } } @@ -560,15 +637,73 @@ fn determine_rp_in_ty(ty: @ast::ty, } match ty.node { - ast::ty_fn(*) => { - do cx.with(cx.item_id, false) { - visit::visit_ty(ty, cx, visitor); + ast::ty_box(mt) | ast::ty_uniq(mt) | ast::ty_vec(mt) | + ast::ty_rptr(_, mt) | ast::ty_ptr(mt) => { + visit_mt(mt, cx, visitor); + } + + ast::ty_rec(fields) => { + for fields.each |field| { + visit_mt(field.node.mt, cx, visitor); } } + + ast::ty_path(path, _) => { + // type parameters are---for now, anyway---always invariant + do cx.with_ambient_variance(rv_invariant) { + for path.types.each |tp| { + visitor.visit_ty(tp, cx, visitor); + } + } + } + + ast::ty_fn(_, bounds, 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 decl.inputs.each |a| { + visitor.visit_ty(a.ty, cx, visitor); + } + } + visit::visit_ty_param_bounds(bounds, cx, visitor); + visitor.visit_ty(decl.output, cx, visitor); + } + } + _ => { visit::visit_ty(ty, cx, visitor); } } + + fn visit_mt(mt: ast::mt, &&cx: determine_rp_ctxt, + visitor: visit::vt) { + // mutability is invariant + if mt.mutbl == ast::m_mutbl { + do cx.with_ambient_variance(rv_invariant) { + visitor.visit_ty(mt.ty, cx, visitor); + } + } else { + visitor.visit_ty(mt.ty, cx, visitor); + } + } +} + +fn determine_rp_in_struct_field(cm: @ast::struct_field, + &&cx: determine_rp_ctxt, + visitor: visit::vt) { + match cm.node.kind { + ast::named_field(_, ast::class_mutable, _) => { + do cx.with_ambient_variance(rv_invariant) { + visit::visit_struct_field(cm, cx, visitor); + } + } + ast::named_field(_, ast::class_immutable, _) | + ast::unnamed_field => { + visit::visit_struct_field(cm, cx, visitor); + } + } } fn determine_rp_in_crate(sess: session, @@ -582,32 +717,56 @@ fn determine_rp_in_crate(sess: session, dep_map: int_hash(), worklist: dvec(), mut item_id: 0, - mut anon_implies_rp: false}); + mut anon_implies_rp: false, + mut ambient_variance: rv_covariant}); - // gather up the base set, worklist and dep_map: + // Gather up the base set, worklist and dep_map let visitor = visit::mk_vt(@{ visit_fn: determine_rp_in_fn, visit_item: determine_rp_in_item, visit_ty: determine_rp_in_ty, visit_ty_method: determine_rp_in_ty_method, + visit_struct_field: determine_rp_in_struct_field, with *visit::default_visitor() }); visit::visit_crate(*crate, cx, visitor); - // propagate indirect dependencies + // 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. while cx.worklist.len() != 0 { - let id = cx.worklist.pop(); - debug!{"popped %d from worklist", id}; - match cx.dep_map.find(id) { + let c_id = cx.worklist.pop(); + let c_variance = cx.region_paramd_items.get(c_id); + debug!["popped %d from worklist", c_id]; + match cx.dep_map.find(c_id) { none => {} - some(vec) => { - for vec.each |to_id| { - cx.add_rp(to_id); + some(deps) => { + for deps.each |dep| { + let v = add_variance(dep.ambient_variance, c_variance); + cx.add_rp(dep.id, v); } } } } + debug!("%s", { + debug!("Region variance results:"); + for cx.region_paramd_items.each |key, value| { + debug!("item %? (%s) is parameterized with variance %?", + key, + ast_map::node_id_to_str(ast_map, key, + sess.parse_sess.interner), + value); + } + "----" + }); + // return final set return cx.region_paramd_items; } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 2f838ad8d40e..7ef772a31121 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -2348,7 +2348,7 @@ fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id) } csearch::found(ast::ii_method(impl_did, mth)) => { ccx.external.insert(fn_id, some(mth.id)); - let {bounds: impl_bnds, rp: _, ty: impl_ty} = + let {bounds: impl_bnds, region_param: _, ty: impl_ty} = ty::lookup_item_type(ccx.tcx, impl_did); if (*impl_bnds).len() + mth.tps.len() == 0u { let llfn = get_item_val(ccx, mth.id); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 773d8f3a6844..13bb97fcdbf9 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -17,6 +17,8 @@ import middle::lint::{get_lint_level, allow}; import syntax::ast::*; import syntax::print::pprust::*; import util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str}; +import std::serialization::{serialize_option, + deserialize_option}; export tv_vid, tvi_vid, region_vid, vid; export br_hashmap; @@ -181,6 +183,10 @@ export ast_proto_to_proto; export is_blockish; export method_call_bounds; export hash_region; +export region_variance, rv_covariant, rv_invariant, rv_contravariant; +export serialize_region_variance, deserialize_region_variance; +export opt_region_variance; +export serialize_opt_region_variance, deserialize_opt_region_variance; // Data types @@ -226,6 +232,12 @@ enum ast_ty_to_ty_cache_entry { atttce_resolved(t) /* resolved to a type, irrespective of region */ } +#[auto_serialize] +type opt_region_variance = option; + +#[auto_serialize] +enum region_variance { rv_covariant, rv_invariant, rv_contravariant } + // N.B.: Borrows from inlined content are not accurately deserialized. This // is because we don't need the details in trans, we only care if there is an // entry in the table or not. @@ -565,7 +577,7 @@ fn param_bounds_to_kind(bounds: param_bounds) -> kind { /// - `ty`: the base type. May have reference to the (unsubstituted) bound /// region `&self` or to (unsubstituted) ty_param types type ty_param_bounds_and_ty = {bounds: @~[param_bounds], - rp: bool, + region_param: option, ty: t}; type type_cache = hashmap; diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 807aff161746..641d2e89c6c0 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -200,7 +200,7 @@ fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def { } fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty { - {bounds: @~[], rp: false, ty: t} + {bounds: @~[], region_param: none, ty: t} } fn require_same_types( diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs index 76788bd05555..a636fbded1a2 100644 --- a/src/rustc/middle/typeck/astconv.rs +++ b/src/rustc/middle/typeck/astconv.rs @@ -84,20 +84,20 @@ fn ast_path_to_substs_and_ty( path: @ast::path) -> ty_param_substs_and_ty { let tcx = self.tcx(); - let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} = + let {bounds: decl_bounds, region_param: decl_rp, ty: decl_ty} = self.get_item_ty(did); - debug!{"ast_path_to_substs_and_ty: did=%? decl_rp=%b", - did, decl_rp}; + debug!["ast_path_to_substs_and_ty: did=%? decl_rp=%?", + did, decl_rp]; // If the type is parameterized by the self region, then replace self // region with the current anon region binding (in other words, // whatever & would get replaced with). let self_r = match (decl_rp, path.rp) { - (false, none) => { + (none, none) => { none } - (false, some(_)) => { + (none, some(_)) => { tcx.sess.span_err( path.span, fmt!{"no region bound is allowed on `%s`, \ @@ -105,12 +105,12 @@ fn ast_path_to_substs_and_ty( ty::item_path_str(tcx, did)}); none } - (true, none) => { + (some(_), none) => { let res = rscope.anon_region(path.span); let r = get_region_reporting_err(self.tcx(), path.span, res); some(r) } - (true, some(r)) => { + (some(_), some(r)) => { some(ast_region_to_region(self, rscope, path.span, r)) } }; diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 87817bd2e077..535ba01fb926 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -71,7 +71,8 @@ import astconv::{ast_region_to_region}; import middle::ty::{tv_vid, vid}; import regionmanip::{replace_bound_regions_in_fn_ty}; import rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope}; -import rscope::{in_binding_rscope, region_scope, type_rscope}; +import rscope::{in_binding_rscope, region_scope, type_rscope, + bound_self_region}; import syntax::ast::ty_i; import typeck::infer::{resolve_type, force_tvar}; @@ -84,42 +85,39 @@ type self_info = { explicit_self: ast::self_ty }; -type fn_ctxt_ = +struct fn_ctxt { // var_bindings, locals and next_var_id are shared // with any nested functions that capture the environment // (and with any functions whose environment is being captured). - {self_impl_def_id: option, - ret_ty: ty::t, - // Used by loop bodies that return from the outer function - indirect_ret_ty: option, - purity: ast::purity, - infcx: infer::infer_ctxt, - locals: hashmap, + self_impl_def_id: option; + ret_ty: ty::t; + // Used by loop bodies that return from the outer function + indirect_ret_ty: option; + purity: ast::purity; + infcx: infer::infer_ctxt; + locals: hashmap; - // Sometimes we generate region pointers where the precise region - // to use is not known. For example, an expression like `&x.f` - // where `x` is of type `@T`: in this case, we will be rooting - // `x` onto the stack frame, and we could choose to root it until - // the end of (almost) any enclosing block or expression. We - // want to pick the narrowest block that encompasses all uses. - // - // What we do in such cases is to generate a region variable with - // `region_lb` as a lower bound. The regionck pass then adds - // other constriants based on how the variable is used and region - // inference selects the ultimate value. Finally, borrowck is - // charged with guaranteeing that the value whose address was taken - // can actually be made to live as long as it needs to live. - mut region_lb: ast::node_id, + // Sometimes we generate region pointers where the precise region + // to use is not known. For example, an expression like `&x.f` + // where `x` is of type `@T`: in this case, we will be rooting + // `x` onto the stack frame, and we could choose to root it until + // the end of (almost) any enclosing block or expression. We + // want to pick the narrowest block that encompasses all uses. + // + // What we do in such cases is to generate a region variable with + // `region_lb` as a lower bound. The regionck pass then adds + // other constriants based on how the variable is used and region + // inference selects the ultimate value. Finally, borrowck is + // charged with guaranteeing that the value whose address was taken + // can actually be made to live as long as it needs to live. + mut region_lb: ast::node_id; - in_scope_regions: isr_alist, + in_scope_regions: isr_alist; - node_types: hashmap, - node_type_substs: hashmap, + node_types: hashmap; + node_type_substs: hashmap; - ccx: @crate_ctxt}; - -enum fn_ctxt { - fn_ctxt_(fn_ctxt_) + ccx: @crate_ctxt; } // Used by check_const and check_enum_variants @@ -127,17 +125,19 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t, region_bnd: ast::node_id) -> @fn_ctxt { // 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 - @fn_ctxt_({self_impl_def_id: none, - ret_ty: rty, - indirect_ret_ty: none, - purity: ast::pure_fn, - infcx: infer::new_infer_ctxt(ccx.tcx), - locals: int_hash(), - mut region_lb: region_bnd, - in_scope_regions: @nil, - node_types: map::int_hash(), - node_type_substs: map::int_hash(), - ccx: ccx}) + @fn_ctxt { + self_impl_def_id: none, + ret_ty: rty, + indirect_ret_ty: none, + purity: ast::pure_fn, + infcx: infer::new_infer_ctxt(ccx.tcx), + locals: int_hash(), + mut region_lb: region_bnd, + in_scope_regions: @nil, + node_types: map::int_hash(), + node_type_substs: map::int_hash(), + ccx: ccx + } } // a list of mapping from in-scope-region-names ("isr") to the @@ -245,17 +245,19 @@ fn check_fn(ccx: @crate_ctxt, } } else { none }; - @fn_ctxt_({self_impl_def_id: self_info.map(|info| info.def_id), - ret_ty: ret_ty, - indirect_ret_ty: indirect_ret_ty, - purity: purity, - infcx: infcx, - locals: locals, - mut region_lb: body.node.id, - in_scope_regions: isr, - node_types: node_types, - node_type_substs: node_type_substs, - ccx: ccx}) + @fn_ctxt { + self_impl_def_id: self_info.map(|info| info.def_id), + ret_ty: ret_ty, + indirect_ret_ty: indirect_ret_ty, + purity: purity, + infcx: infcx, + locals: locals, + mut region_lb: body.node.id, + in_scope_regions: isr, + node_types: node_types, + node_type_substs: node_type_substs, + ccx: ccx + } }; // Update the self_info to contain an accurate self type (taking @@ -478,9 +480,9 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { check_bare_fn(ccx, decl, body, it.id, none); } ast::item_impl(tps, _, ty, ms) => { - let rp = ccx.tcx.region_paramd_items.contains_key(it.id); - debug!{"item_impl %s with id %d rp %b", - ccx.tcx.sess.str_of(it.ident), it.id, rp}; + let rp = ccx.tcx.region_paramd_items.find(it.id); + debug!("item_impl %s with id %d rp %?", + ccx.tcx.sess.str_of(it.ident), it.id, rp); let self_ty = ccx.to_ty(rscope::type_rscope(rp), ty); for ms.each |m| { check_method(ccx, m, self_ty, local_def(it.id)); @@ -701,6 +703,15 @@ impl @fn_ctxt { self.region_lb = old_region_lb; return v; } + + fn region_var_if_parameterized(rp: option, + span: span) + -> option { + match rp { + some(_) => some(self.infcx.next_region_var_nb(span)), + none => none + } + } } fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t { @@ -789,14 +800,14 @@ fn impl_self_ty(fcx: @fn_ctxt, require_rp: bool) -> ty_param_substs_and_ty { let tcx = fcx.ccx.tcx; - let {n_tps, rp, raw_ty} = if did.crate == ast::local_crate { - let rp = fcx.tcx().region_paramd_items.contains_key(did.node); + let {n_tps, region_param, raw_ty} = if did.crate == ast::local_crate { + let region_param = fcx.tcx().region_paramd_items.find(did.node); match check tcx.items.find(did.node) { some(ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), _}, _)) => { {n_tps: ts.len(), - rp: rp, - raw_ty: fcx.ccx.to_ty(rscope::type_rscope(rp), st)} + region_param: region_param, + raw_ty: fcx.ccx.to_ty(rscope::type_rscope(region_param), st)} } some(ast_map::node_item(@{node: ast::item_class(_, ts), id: class_id, _},_)) => { @@ -805,10 +816,9 @@ fn impl_self_ty(fcx: @fn_ctxt, we substitute in fresh vars for them) */ {n_tps: ts.len(), - rp: rp, + region_param: region_param, raw_ty: ty::mk_class(tcx, local_def(class_id), - {self_r: if rp {some(ty::re_bound(ty::br_self))} - else {none}, + {self_r: rscope::bound_self_region(region_param), self_ty: none, tps: ty::ty_params_to_tys(tcx, ts)})} } @@ -818,13 +828,15 @@ fn impl_self_ty(fcx: @fn_ctxt, } else { let ity = ty::lookup_item_type(tcx, did); {n_tps: vec::len(*ity.bounds), - rp: ity.rp, + region_param: ity.region_param, raw_ty: ity.ty} }; - let rp = rp || require_rp; - let self_r = if rp {some(fcx.infcx.next_region_var(expr.span, expr.id))} - else {none}; + let self_r = if region_param.is_some() || require_rp { + some(fcx.infcx.next_region_var(expr.span, expr.id)) + } else { + none + }; let tps = fcx.infcx.next_ty_vars(n_tps); let substs = {self_r: self_r, self_ty: none, tps: tps}; @@ -1832,7 +1844,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, let type_parameter_count, region_parameterized, raw_type; if class_id.crate == ast::local_crate { region_parameterized = - tcx.region_paramd_items.contains_key(class_id.node); + tcx.region_paramd_items.find(class_id.node); match tcx.items.find(class_id.node) { some(ast_map::node_item(@{ node: ast::item_class(_, type_parameters), @@ -1841,12 +1853,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, type_parameter_count = type_parameters.len(); - let self_region; - if region_parameterized { - self_region = some(ty::re_bound(ty::br_self)); - } else { - self_region = none; - } + let self_region = + bound_self_region(region_parameterized); raw_type = ty::mk_class(tcx, class_id, { self_r: self_region, @@ -1862,18 +1870,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } else { let item_type = ty::lookup_item_type(tcx, class_id); type_parameter_count = (*item_type.bounds).len(); - region_parameterized = item_type.rp; + region_parameterized = item_type.region_param; raw_type = item_type.ty; } // Generate the struct type. - let self_region; - if region_parameterized { - self_region = some(fcx.infcx.next_region_var(expr.span, expr.id)); - } else { - self_region = none; - } - + let self_region = + fcx.region_var_if_parameterized(region_parameterized, + expr.span); let type_parameters = fcx.infcx.next_ty_vars(type_parameter_count); let substitutions = { self_r: self_region, @@ -2082,8 +2086,8 @@ fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool { fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool { let fcx = match blk.node.rules { - ast::unchecked_blk => @fn_ctxt_({purity: ast::impure_fn with **fcx0}), - ast::unsafe_blk => @fn_ctxt_({purity: ast::unsafe_fn with **fcx0}), + ast::unchecked_blk => @fn_ctxt {purity: ast::impure_fn with *fcx0}, + ast::unsafe_blk => @fn_ctxt {purity: ast::unsafe_fn with *fcx0}, ast::default_blk => fcx0 }; do fcx.with_region_lb(blk.node.id) { @@ -2294,7 +2298,7 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> // extern functions are just u8 pointers return { bounds: @~[], - rp: false, + region_param: none, ty: ty::mk_ptr( fcx.ccx.tcx, { @@ -2358,19 +2362,21 @@ fn instantiate_path(fcx: @fn_ctxt, // determine the region bound, using the value given by the user // (if any) and otherwise using a fresh region variable let self_r = match pth.rp { - some(r) if !tpt.rp => { - fcx.ccx.tcx.sess.span_err - (span, ~"this item is not region-parameterized"); - none - } some(r) => { - some(ast_region_to_region(fcx, fcx, span, r)) - } - none if tpt.rp => { - some(fcx.infcx.next_region_var_with_lb(span, region_lb)) + match tpt.region_param { + none => { + fcx.ccx.tcx.sess.span_err + (span, ~"this item is not region-parameterized"); + none + } + some(_) => { + some(ast_region_to_region(fcx, fcx, span, r)) + } + } } none => { - none + fcx.region_var_if_parameterized( + tpt.region_param, span) } }; diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs index b7f39f4ba405..5691af2b91a9 100644 --- a/src/rustc/middle/typeck/coherence.rs +++ b/src/rustc/middle/typeck/coherence.rs @@ -402,11 +402,9 @@ struct CoherenceChecker { fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t { // NDM--this span is bogus. - let self_region = if !polytype.rp { - none - } else { - some(self.inference_context.next_region_var_nb(dummy_sp())) - }; + let self_region = + polytype.region_param.map( + |_r| self.inference_context.next_region_var_nb(dummy_sp())); let bounds_count = polytype.bounds.len(); let type_parameters = diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index 5359e43401cf..85b1ebc58de2 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -112,7 +112,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt, enum_ty: ty::t, variants: ~[ast::variant], ty_params: ~[ast::ty_param], - rp: bool) { + rp: option) { let tcx = ccx.tcx; // Create a set of parameter types shared among all the variants. @@ -150,7 +150,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt, none => {} some(result_ty) => { let tpt = {bounds: ty_param_bounds(ccx, ty_params), - rp: rp, + region_param: rp, ty: result_ty}; tcx.tcache.insert(local_def(variant.node.id), tpt); write_ty_to_tcx(tcx, variant.node.id, result_ty); @@ -167,7 +167,8 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) { fn make_static_method_ty(ccx: @crate_ctxt, am: ast::ty_method, - rp: bool, m: ty::method, + rp: option, + m: ty::method, // Take this as an argument b/c we may check // the impl before the trait. trait_ty: ty::t, @@ -197,20 +198,22 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) { let bounds = @(*trait_bounds + ~[@~[ty::bound_trait(trait_ty)]] + *m.tps); ccx.tcx.tcache.insert(local_def(am.id), - {bounds: bounds, rp: rp, ty: ty}); + {bounds: bounds, + region_param: rp, + ty: ty}); } let tcx = ccx.tcx; - let rp = tcx.region_paramd_items.contains_key(id); + let region_paramd = tcx.region_paramd_items.find(id); match tcx.items.get(id) { ast_map::node_item(@{node: ast::item_trait(params, _, ms), _}, _) => { store_methods::(ccx, id, ms, |m| { let trait_bounds = ty_param_bounds(ccx, params); let ty_m = trait_method_to_ty_method(m); - let method_ty = ty_of_ty_method(ccx, ty_m, rp); + let method_ty = ty_of_ty_method(ccx, ty_m, region_paramd); if ty_m.self_ty.node == ast::sty_static { - make_static_method_ty(ccx, ty_m, rp, + make_static_method_ty(ccx, ty_m, region_paramd, method_ty, trait_ty, trait_bounds); } method_ty @@ -220,7 +223,7 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) { // All methods need to be stored, since lookup_method // relies on the same method cache for self-calls store_methods::<@ast::method>(ccx, id, struct_def.methods, |m| { - ty_of_method(ccx, m, rp) + ty_of_method(ccx, m, region_paramd) }); } _ => { /* Ignore things that aren't traits or classes */ } @@ -316,7 +319,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, fn check_methods_against_trait(ccx: @crate_ctxt, tps: ~[ast::ty_param], - rp: bool, + rp: option, selfty: ty::t, a_trait_ty: @ast::trait_ref, impl_ms: ~[converted_method]) { @@ -370,21 +373,23 @@ fn check_methods_against_trait(ccx: @crate_ctxt, } // fn fn convert_field(ccx: @crate_ctxt, - rp: bool, + rp: option, bounds: @~[ty::param_bounds], v: @ast::struct_field) { let tt = ccx.to_ty(type_rscope(rp), 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), - {bounds: bounds, rp: rp, ty: tt}); + {bounds: bounds, + region_param: rp, + ty: tt}); } type converted_method = {mty: ty::method, id: ast::node_id, span: span}; fn convert_methods(ccx: @crate_ctxt, ms: ~[@ast::method], - rp: bool, + rp: option, rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] { let tcx = ccx.tcx; @@ -397,7 +402,9 @@ fn convert_methods(ccx: @crate_ctxt, // n.b.: the type of a method is parameterized by both // the tps on the receiver and those on the method itself - {bounds: @(vec::append(*rcvr_bounds, *bounds)), rp: rp, ty: fty}); + {bounds: @(vec::append(*rcvr_bounds, *bounds)), + region_param: rp, + ty: fty}); write_ty_to_tcx(tcx, m.id, fty); {mty: mty, id: m.id, span: m.span} } @@ -405,9 +412,9 @@ fn convert_methods(ccx: @crate_ctxt, fn convert(ccx: @crate_ctxt, it: @ast::item) { let tcx = ccx.tcx; - let rp = tcx.region_paramd_items.contains_key(it.id); - debug!{"convert: item %s with id %d rp %b", tcx.sess.str_of(it.ident), - it.id, rp}; + let rp = tcx.region_paramd_items.find(it.id); + #debug["convert: item %s with id %d rp %?", + tcx.sess.str_of(it.ident), it.id, rp]; match it.node { // These don't define types. ast::item_foreign_mod(_) | ast::item_mod(_) => {} @@ -423,7 +430,7 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) { write_ty_to_tcx(tcx, it.id, selfty); tcx.tcache.insert(local_def(it.id), {bounds: i_bounds, - rp: rp, + region_param: rp, ty: selfty}); let cms = convert_methods(ccx, ms, rp, i_bounds); @@ -465,8 +472,11 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) { } } -fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def, - tps: ~[ast::ty_param], tpt: ty::ty_param_bounds_and_ty, +fn convert_struct(ccx: @crate_ctxt, + rp: option, + struct_def: @ast::struct_def, + tps: ~[ast::ty_param], + tpt: ty::ty_param_bounds_and_ty, id: ast::node_id) { let tcx = ccx.tcx; do option::iter(struct_def.ctor) |ctor| { @@ -475,7 +485,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def, |a| ty_of_arg(ccx, type_rscope(rp), a, none) ); let t_res = ty::mk_class( tcx, local_def(id), - {self_r: if rp {some(ty::re_bound(ty::br_self))} else {none}, + {self_r: rscope::bound_self_region(rp), self_ty: none, tps: ty::ty_params_to_tys(tcx, tps)}); let t_ctor = ty::mk_fn( @@ -488,7 +498,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def, write_ty_to_tcx(tcx, ctor.node.id, t_ctor); tcx.tcache.insert(local_def(ctor.node.id), {bounds: tpt.bounds, - rp: rp, + region_param: rp, ty: t_ctor}); } @@ -501,7 +511,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def, write_ty_to_tcx(tcx, dtor.node.id, t_dtor); tcx.tcache.insert(local_def(dtor.node.id), {bounds: tpt.bounds, - rp: rp, + region_param: rp, ty: t_dtor}); }; ensure_trait_methods(ccx, id, tpt.ty); @@ -536,8 +546,7 @@ fn convert_foreign(ccx: @crate_ctxt, i: @ast::foreign_item) { fn ty_of_method(ccx: @crate_ctxt, m: @ast::method, - rp: bool) -> ty::method { - // XXX: Are the bounds correct here? + rp: option) -> ty::method { {ident: m.ident, tps: ty_param_bounds(ccx, m.tps), fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, @~[], @@ -549,7 +558,7 @@ fn ty_of_method(ccx: @crate_ctxt, fn ty_of_ty_method(self: @crate_ctxt, m: ast::ty_method, - rp: bool) -> ty::method { + rp: option) -> ty::method { {ident: m.ident, tps: ty_param_bounds(self, m.tps), fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, @~[], m.decl, @@ -564,7 +573,8 @@ fn ty_of_ty_method(self: @crate_ctxt, it's bound to a valid trait type. Returns the def_id for the defining trait. Fails if the type is a type other than an trait type. */ -fn instantiate_trait_ref(ccx: @crate_ctxt, t: @ast::trait_ref, rp: bool) +fn instantiate_trait_ref(ccx: @crate_ctxt, t: @ast::trait_ref, + rp: option) -> (ast::def_id, ty_param_substs_and_ty) { let sp = t.path.span, err = ~"can only implement trait types", @@ -596,7 +606,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) some(tpt) => return tpt, _ => {} } - let rp = tcx.region_paramd_items.contains_key(it.id); + let rp = tcx.region_paramd_items.find(it.id); match it.node { ast::item_const(t, _) => { let typ = ccx.to_ty(empty_rscope, t); @@ -609,7 +619,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, @~[], decl, none, it.span); let tpt = {bounds: bounds, - rp: false, // functions do not have a self + region_param: none, ty: ty::mk_fn(ccx.tcx, tofd)}; debug!{"type of %s (id %d) is %s", tcx.sess.str_of(it.ident), it.id, ty_to_str(tcx, tpt.ty)}; @@ -622,7 +632,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) none => { } } - let rp = tcx.region_paramd_items.contains_key(it.id); + let rp = tcx.region_paramd_items.find(it.id); let tpt = { let ty = { let t0 = ccx.to_ty(type_rscope(rp), t); @@ -634,7 +644,9 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) ty::mk_with_id(tcx, t0, def_id) } }; - {bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty} + {bounds: ty_param_bounds(ccx, tps), + region_param: rp, + ty: ty} }; tcx.tcache.insert(local_def(it.id), tpt); @@ -644,21 +656,27 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) // Create a new generic polytype. let {bounds, substs} = mk_substs(ccx, tps, rp); let t = ty::mk_enum(tcx, local_def(it.id), substs); - let tpt = {bounds: bounds, rp: rp, ty: t}; + let tpt = {bounds: bounds, + region_param: rp, + ty: t}; tcx.tcache.insert(local_def(it.id), tpt); return tpt; } ast::item_trait(tps, _, ms) => { let {bounds, substs} = mk_substs(ccx, tps, rp); let t = ty::mk_trait(tcx, local_def(it.id), substs, ty::vstore_box); - let tpt = {bounds: bounds, rp: rp, ty: t}; + let tpt = {bounds: bounds, + region_param: rp, + ty: t}; tcx.tcache.insert(local_def(it.id), tpt); return tpt; } ast::item_class(_, tps) => { let {bounds,substs} = mk_substs(ccx, tps, rp); let t = ty::mk_class(tcx, local_def(it.id), substs); - let tpt = {bounds: bounds, rp: rp, ty: t}; + let tpt = {bounds: bounds, + region_param: rp, + ty: t}; tcx.tcache.insert(local_def(it.id), tpt); return tpt; } @@ -736,7 +754,7 @@ fn ty_of_foreign_fn_decl(ccx: @crate_ctxt, inputs: input_tys, output: output_ty, ret_style: ast::return_val}); - let tpt = {bounds: bounds, rp: false, ty: t_fn}; + let tpt = {bounds: bounds, region_param: none, ty: t_fn}; ccx.tcx.tcache.insert(def_id, tpt); return tpt; } @@ -754,10 +772,11 @@ fn mk_ty_params(ccx: @crate_ctxt, atps: ~[ast::ty_param]) })} } -fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param], rp: bool) +fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param], + rp: option) -> {bounds: @~[ty::param_bounds], substs: ty::substs} { let {bounds, params} = mk_ty_params(ccx, atps); - let self_r = if rp {some(ty::re_bound(ty::br_self))} else {none}; + let self_r = rscope::bound_self_region(rp); {bounds: bounds, substs: {self_r: self_r, self_ty: none, tps: params}} } diff --git a/src/rustc/middle/typeck/infer/combine.rs b/src/rustc/middle/typeck/infer/combine.rs index a955165ace0c..308818d50f01 100644 --- a/src/rustc/middle/typeck/infer/combine.rs +++ b/src/rustc/middle/typeck/infer/combine.rs @@ -58,7 +58,8 @@ trait combine { fn tys(a: ty::t, b: ty::t) -> cres; fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]>; fn self_tys(a: option, b: option) -> cres>; - fn substs(as: &ty::substs, bs: &ty::substs) -> cres; + fn substs(did: ast::def_id, as: &ty::substs, + bs: &ty::substs) -> cres; fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres; fn flds(a: ty::field, b: ty::field) -> cres; fn modes(a: ast::mode, b: ast::mode) -> cres; @@ -148,12 +149,57 @@ fn eq_opt_regions( } fn super_substs( - self: &C, a: &ty::substs, b: &ty::substs) -> cres { + self: &C, did: ast::def_id, + a: &ty::substs, b: &ty::substs) -> cres { + + fn relate_region_param( + self: &C, + did: ast::def_id, + a: option, + b: option) + -> cres> + { + let polyty = ty::lookup_item_type(self.infcx().tcx, did); + match (polyty.region_param, a, b) { + (none, none, none) => { + ok(none) + } + (some(ty::rv_invariant), some(a), some(b)) => { + do eq_regions(self, a, b).then { + ok(some(a)) + } + } + (some(ty::rv_covariant), some(a), some(b)) => { + do self.regions(a, b).chain |r| { + ok(some(r)) + } + } + (some(ty::rv_contravariant), some(a), some(b)) => { + do self.contraregions(a, b).chain |r| { + ok(some(r)) + } + } + (_, _, _) => { + // If these two substitutions are for the same type (and + // they should be), then the type should either + // consistently have a region parameter or not have a + // region parameter, and that should match with the + // polytype. + self.infcx().tcx.sess.bug( + fmt!("substitution a had opt_region %s and \ + b had opt_region %s with variance %?", + a.to_str(self.infcx()), + b.to_str(self.infcx()), + polyty.region_param)); + } + } + } do self.tps(a.tps, b.tps).chain |tps| { do self.self_tys(a.self_ty, b.self_ty).chain |self_ty| { - do eq_opt_regions(self, a.self_r, b.self_r).chain - |self_r| { + do relate_region_param(self, did, + a.self_r, b.self_r).chain |self_r| + { ok({self_r: self_r, self_ty: self_ty, tps: tps}) } } @@ -348,7 +394,7 @@ fn super_tys( (ty::ty_enum(a_id, ref a_substs), ty::ty_enum(b_id, ref b_substs)) if a_id == b_id => { - do self.substs(a_substs, b_substs).chain |substs| { + do self.substs(a_id, a_substs, b_substs).chain |substs| { ok(ty::mk_enum(tcx, a_id, substs)) } } @@ -356,7 +402,7 @@ fn super_tys( (ty::ty_trait(a_id, ref a_substs, a_vstore), ty::ty_trait(b_id, ref b_substs, b_vstore)) if a_id == b_id => { - do self.substs(a_substs, b_substs).chain |substs| { + do self.substs(a_id, a_substs, b_substs).chain |substs| { do self.vstores(ty::terr_trait, a_vstore, b_vstore).chain |vs| { ok(ty::mk_trait(tcx, a_id, substs, vs)) } @@ -365,7 +411,7 @@ fn super_tys( (ty::ty_class(a_id, ref a_substs), ty::ty_class(b_id, ref b_substs)) if a_id == b_id => { - do self.substs(a_substs, b_substs).chain |substs| { + do self.substs(a_id, a_substs, b_substs).chain |substs| { ok(ty::mk_class(tcx, a_id, substs)) } } diff --git a/src/rustc/middle/typeck/infer/glb.rs b/src/rustc/middle/typeck/infer/glb.rs index ae71e97c1252..966611ab0d9c 100644 --- a/src/rustc/middle/typeck/infer/glb.rs +++ b/src/rustc/middle/typeck/infer/glb.rs @@ -151,8 +151,10 @@ impl Glb: combine { super_fns(&self, a, b) } - fn substs(as: &ty::substs, bs: &ty::substs) -> cres { - super_substs(&self, as, bs) + fn substs(did: ast::def_id, + as: &ty::substs, + bs: &ty::substs) -> cres { + super_substs(&self, did, as, bs) } fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> { diff --git a/src/rustc/middle/typeck/infer/lub.rs b/src/rustc/middle/typeck/infer/lub.rs index 66bf31b1715d..d5a6b44aa5c7 100644 --- a/src/rustc/middle/typeck/infer/lub.rs +++ b/src/rustc/middle/typeck/infer/lub.rs @@ -130,8 +130,10 @@ impl Lub: combine { super_fns(&self, a, b) } - fn substs(as: &ty::substs, bs: &ty::substs) -> cres { - super_substs(&self, as, bs) + fn substs(did: ast::def_id, + as: &ty::substs, + bs: &ty::substs) -> cres { + super_substs(&self, did, as, bs) } fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> { diff --git a/src/rustc/middle/typeck/infer/sub.rs b/src/rustc/middle/typeck/infer/sub.rs index 682afb4ea073..d2bdc61724cc 100644 --- a/src/rustc/middle/typeck/infer/sub.rs +++ b/src/rustc/middle/typeck/infer/sub.rs @@ -184,8 +184,10 @@ impl Sub: combine { super_args(&self, a, b) } - fn substs(as: &ty::substs, bs: &ty::substs) -> cres { - super_substs(&self, as, bs) + fn substs(did: ast::def_id, + as: &ty::substs, + bs: &ty::substs) -> cres { + super_substs(&self, did, as, bs) } fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> { diff --git a/src/rustc/middle/typeck/rscope.rs b/src/rustc/middle/typeck/rscope.rs index f11b09913afa..340399427fc9 100644 --- a/src/rustc/middle/typeck/rscope.rs +++ b/src/rustc/middle/typeck/rscope.rs @@ -17,14 +17,13 @@ impl empty_rscope: region_scope { } } -enum type_rscope = bool; +enum type_rscope = option; impl type_rscope: region_scope { fn anon_region(_span: span) -> result { - if *self { - result::ok(ty::re_bound(ty::br_self)) - } else { - result::err(~"to use region types here, the containing type \ - must be declared with a region bound") + match *self { + some(_) => result::ok(ty::re_bound(ty::br_self)), + none => result::err(~"to use region types here, the containing \ + type must be declared with a region bound") } } fn named_region(span: span, id: ast::ident) -> result { @@ -39,6 +38,13 @@ impl type_rscope: region_scope { } } +fn bound_self_region(rp: option) -> option { + match rp { + some(_) => some(ty::re_bound(ty::br_self)), + none => none + } +} + enum anon_rscope = {anon: ty::region, base: region_scope}; fn in_anon_rscope(self: RS, r: ty::region) -> @anon_rscope { diff --git a/src/test/compile-fail/regions-creating-enums3.rs b/src/test/compile-fail/regions-creating-enums3.rs index 5fe93584dd53..434b360d18cf 100644 --- a/src/test/compile-fail/regions-creating-enums3.rs +++ b/src/test/compile-fail/regions-creating-enums3.rs @@ -5,7 +5,6 @@ enum ast { fn mk_add_bad1(x: &a/ast, y: &b/ast) -> ast/&a { add(x, y) //~ ERROR cannot infer an appropriate lifetime - //~^ ERROR cannot infer an appropriate lifetime } fn main() { diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs index ec3acd625596..e4c86c14b3e8 100644 --- a/src/test/compile-fail/regions-creating-enums4.rs +++ b/src/test/compile-fail/regions-creating-enums4.rs @@ -6,7 +6,6 @@ enum ast { fn mk_add_bad2(x: &a/ast, y: &a/ast, z: &ast) -> ast { add(x, y) //~^ ERROR cannot infer an appropriate lifetime - //~^^ ERROR cannot infer an appropriate lifetime } 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 new file mode 100644 index 000000000000..07d456d31885 --- /dev/null +++ b/src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs @@ -0,0 +1,18 @@ +struct contravariant { + f: ∫ +} + +fn to_same_lifetime(bi: contravariant/&r) { + let bj: contravariant/&r = bi; +} + +fn to_shorter_lifetime(bi: contravariant/&r) { + let bj: contravariant/&blk = bi; +} + +fn to_longer_lifetime(bi: contravariant/&r) -> contravariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file 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 new file mode 100644 index 000000000000..970350748d55 --- /dev/null +++ b/src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs @@ -0,0 +1,23 @@ +// 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 { + f: fn@() -> &self/int; +} + +fn to_same_lifetime(bi: contravariant/&r) { + let bj: contravariant/&r = bi; +} + +fn to_shorter_lifetime(bi: contravariant/&r) { + let bj: contravariant/&blk = bi; +} + +fn to_longer_lifetime(bi: contravariant/&r) -> contravariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file 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 new file mode 100644 index 000000000000..a2a0d1317606 --- /dev/null +++ b/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs @@ -0,0 +1,22 @@ +// Covariant with respect to a region: +// +// You can upcast to a *larger region* but not a smaller one. + +struct covariant { + f: fn@(x: &self/int) -> int; +} + +fn to_same_lifetime(bi: covariant/&r) { + let bj: covariant/&r = bi; +} + +fn to_shorter_lifetime(bi: covariant/&r) { + let bj: covariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: covariant/&r) -> covariant/&static { + bi +} + +fn main() { +} \ No newline at end of file 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 new file mode 100644 index 000000000000..d7ed0ed1d76d --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs @@ -0,0 +1,22 @@ +// Invariance with respect to a region: +// +// You cannot convert between regions. + +struct invariant { + f: fn(x: &self/int) -> &self/int; +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs new file mode 100644 index 000000000000..0b0a8bb74481 --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs @@ -0,0 +1,18 @@ +struct invariant { + f: @mut ∫ +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs new file mode 100644 index 000000000000..cad76bb63806 --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs @@ -0,0 +1,18 @@ +struct invariant { + f: @[mut &int]; +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-3.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-3.rs new file mode 100644 index 000000000000..0f8441110f13 --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-3.rs @@ -0,0 +1,18 @@ +struct invariant { + f: fn@(x: @mut &self/int); +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs new file mode 100644 index 000000000000..e39293ddcb13 --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs @@ -0,0 +1,18 @@ +struct invariant { + f: fn@() -> @mut &self/int; +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs new file mode 100644 index 000000000000..f00888add68a --- /dev/null +++ b/src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs @@ -0,0 +1,18 @@ +struct invariant { + mut f: ∫ +} + +fn to_same_lifetime(bi: invariant/&r) { + let bj: invariant/&r = bi; +} + +fn to_shorter_lifetime(bi: invariant/&r) { + let bj: invariant/&blk = bi; //~ ERROR mismatched types +} + +fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static { + bi //~ ERROR mismatched types +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/run-pass/regions-infer-contravariance-due-to-ret.rs b/src/test/run-pass/regions-infer-contravariance-due-to-ret.rs new file mode 100644 index 000000000000..e6b00890fabb --- /dev/null +++ b/src/test/run-pass/regions-infer-contravariance-due-to-ret.rs @@ -0,0 +1,18 @@ +struct boxed_int { + f: ∫ +} + +fn max(bi: &r/boxed_int, f: &r/int) -> int { + if *bi.f > *f {*bi.f} else {*f} +} + +fn with(bi: &boxed_int) -> int { + let i = 22; + max(bi, &i) +} + +fn main() { + let g = 21; + let foo = boxed_int { f: &g }; + assert with(&foo) == 22; +} \ No newline at end of file diff --git a/src/test/run-pass/regions-infer-contravariance.rs b/src/test/run-pass/regions-infer-contravariance.rs new file mode 100644 index 000000000000..000755637503 --- /dev/null +++ b/src/test/run-pass/regions-infer-contravariance.rs @@ -0,0 +1,21 @@ +struct boxed_int { + f: ∫ +} + +fn get(bi: &r/boxed_int) -> &r/int { + bi.f +} + +fn with(bi: &r/boxed_int) { + // Here, the upcast is allowed because the `boxed_int` type is + // contravariant with respect to `&r`. See also + // compile-fail/regions-infer-invariance-due-to-mutability.rs + let bi: &blk/boxed_int/&blk = bi; + assert *get(bi) == 22; +} + +fn main() { + let g = 22; + let foo = boxed_int { f: &g }; + with(&foo); +} \ No newline at end of file diff --git a/src/test/run-pass/regions-trait.rs b/src/test/run-pass/regions-trait.rs index 4be94ba24469..d5e009c5bb7a 100644 --- a/src/test/run-pass/regions-trait.rs +++ b/src/test/run-pass/regions-trait.rs @@ -20,7 +20,5 @@ fn main() { let ctxt = { v: 22u }; let hc = { c: &ctxt }; - // This no longer works, interestingly, due to the ownership - // requirement. Perhaps this ownership requirement is too strict. - // assert get_v(hc as get_ctxt) == 22u; + assert get_v(hc as get_ctxt) == 22u; }