A similar approach could be used for type parameters.
Fixes #2282.
fn access_group<U>(x: &TaskGroupArc, blk: fn(TaskGroupInner) -> U) -> U {
unsafe { x.with(blk) }
}
+
#[inline(always)]
fn access_ancestors<U>(x: &unsafe::Exclusive<AncestorNode>,
blk: fn(x: &mut AncestorNode) -> U) -> U {
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=%?)",
}
fn get_region_param(cstore: metadata::cstore::cstore,
- def: ast::def_id) -> bool {
+ def: ast::def_id) -> option<ty::region_variance> {
let cdata = cstore::get_crate_data(cstore, def.crate);
return decoder::get_region_param(cdata, def.node);
}
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,
@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<ty::region_variance> {
+ 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 {
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<ty::region_variance> {
+
let item = lookup_item(id, cdata.data);
return item_ty_region_param(item);
}
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) {
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);
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)
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;
// a worklist. We can then process the worklist, propagating indirect
// dependencies until a fixed point is reached.
-type region_paramd_items = hashmap<ast::node_id, ()>;
-type dep_map = hashmap<ast::node_id, @DVec<ast::node_id>>;
+type region_paramd_items = hashmap<ast::node_id, region_variance>;
+type region_dep = {ambient_variance: region_variance, id: ast::node_id};
+type dep_map = hashmap<ast::node_id, @DVec<region_dep>>;
type determine_rp_ctxt_ = {
sess: session,
// 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
}
}
- 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;
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,
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<determine_rp_ctxt>) {
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);
}
}
// 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))
}
}
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));
}
_ => {}
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))
+ }
}
}
}
}
match ty.node {
- ast::ty_fn(*) => {
+ 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) {
- visit::visit_ty(ty, cx, visitor);
+ // 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<determine_rp_ctxt>) {
+ // 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<determine_rp_ctxt>) {
+ 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,
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;
}
}
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);
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;
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
atttce_resolved(t) /* resolved to a type, irrespective of region */
}
+#[auto_serialize]
+type opt_region_variance = option<region_variance>;
+
+#[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.
/// - `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<region_variance>,
ty: t};
type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>;
}
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(
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`, \
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))
}
};
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};
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<ast::def_id>,
- ret_ty: ty::t,
- // Used by loop bodies that return from the outer function
- indirect_ret_ty: option<ty::t>,
- purity: ast::purity,
- infcx: infer::infer_ctxt,
- locals: hashmap<ast::node_id, tv_vid>,
-
- // 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,
-
- node_types: hashmap<ast::node_id, ty::t>,
- node_type_substs: hashmap<ast::node_id, ty::substs>,
-
- ccx: @crate_ctxt};
-
-enum fn_ctxt {
- fn_ctxt_(fn_ctxt_)
+ self_impl_def_id: option<ast::def_id>;
+ ret_ty: ty::t;
+ // Used by loop bodies that return from the outer function
+ indirect_ret_ty: option<ty::t>;
+ purity: ast::purity;
+ infcx: infer::infer_ctxt;
+ locals: hashmap<ast::node_id, tv_vid>;
+
+ // 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;
+
+ node_types: hashmap<ast::node_id, ty::t>;
+ node_type_substs: hashmap<ast::node_id, ty::substs>;
+
+ ccx: @crate_ctxt;
}
// Used by check_const and check_enum_variants
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
}
} 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
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));
self.region_lb = old_region_lb;
return v;
}
+
+ fn region_var_if_parameterized(rp: option<ty::region_variance>,
+ span: span)
+ -> option<ty::region> {
+ 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 {
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, _},_)) => {
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)})}
}
} 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};
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),
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,
} 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,
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) {
// extern functions are just u8 pointers
return {
bounds: @~[],
- rp: false,
+ region_param: none,
ty: ty::mk_ptr(
fcx.ccx.tcx,
{
// 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)
}
};
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 =
enum_ty: ty::t,
variants: ~[ast::variant],
ty_params: ~[ast::ty_param],
- rp: bool) {
+ rp: option<ty::region_variance>) {
let tcx = ccx.tcx;
// Create a set of parameter types shared among all the variants.
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);
fn make_static_method_ty(ccx: @crate_ctxt,
am: ast::ty_method,
- rp: bool, m: ty::method,
+ rp: option<ty::region_variance>,
+ m: ty::method,
// Take this as an argument b/c we may check
// the impl before the trait.
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::<ast::trait_method>(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
// 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 */ }
fn check_methods_against_trait(ccx: @crate_ctxt,
tps: ~[ast::ty_param],
- rp: bool,
+ rp: option<ty::region_variance>,
selfty: ty::t,
a_trait_ty: @ast::trait_ref,
impl_ms: ~[converted_method]) {
} // fn
fn convert_field(ccx: @crate_ctxt,
- rp: bool,
+ rp: option<ty::region_variance>,
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<ty::region_variance>,
rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] {
let tcx = ccx.tcx;
// 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}
}
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(_) => {}
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);
}
}
-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<ty::region_variance>,
+ 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| {
|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(
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});
}
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);
fn ty_of_method(ccx: @crate_ctxt,
m: @ast::method,
- rp: bool) -> ty::method {
- // XXX: Are the bounds correct here?
+ rp: option<ty::region_variance>) -> ty::method {
{ident: m.ident,
tps: ty_param_bounds(ccx, m.tps),
fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, @~[],
fn ty_of_ty_method(self: @crate_ctxt,
m: ast::ty_method,
- rp: bool) -> ty::method {
+ rp: option<ty::region_variance>) -> 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,
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<ty::region_variance>)
-> (ast::def_id, ty_param_substs_and_ty) {
let sp = t.path.span, err = ~"can only implement trait types",
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);
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)};
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);
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);
// 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;
}
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;
}
})}
}
-fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param], rp: bool)
+fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param],
+ rp: option<ty::region_variance>)
-> {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}}
}
fn tys(a: ty::t, b: ty::t) -> cres<ty::t>;
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]>;
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>>;
- fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs>;
+ fn substs(did: ast::def_id, as: &ty::substs,
+ bs: &ty::substs) -> cres<ty::substs>;
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty>;
fn flds(a: ty::field, b: ty::field) -> cres<ty::field>;
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode>;
}
fn super_substs<C:combine>(
- self: &C, a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
+ self: &C, did: ast::def_id,
+ a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
+
+ fn relate_region_param<C:combine>(
+ self: &C,
+ did: ast::def_id,
+ a: option<ty::region>,
+ b: option<ty::region>)
+ -> cres<option<ty::region>>
+ {
+ 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})
}
}
(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))
}
}
(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))
}
(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))
}
}
super_fns(&self, a, b)
}
- fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
- super_substs(&self, as, bs)
+ fn substs(did: ast::def_id,
+ as: &ty::substs,
+ bs: &ty::substs) -> cres<ty::substs> {
+ super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
super_fns(&self, a, b)
}
- fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
- super_substs(&self, as, bs)
+ fn substs(did: ast::def_id,
+ as: &ty::substs,
+ bs: &ty::substs) -> cres<ty::substs> {
+ super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
super_args(&self, a, b)
}
- fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
- super_substs(&self, as, bs)
+ fn substs(did: ast::def_id,
+ as: &ty::substs,
+ bs: &ty::substs) -> cres<ty::substs> {
+ super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
}
}
-enum type_rscope = bool;
+enum type_rscope = option<ty::region_variance>;
impl type_rscope: region_scope {
fn anon_region(_span: span) -> result<ty::region, ~str> {
- 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<ty::region, ~str> {
}
}
+fn bound_self_region(rp: option<ty::region_variance>) -> option<ty::region> {
+ 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<RS: region_scope copy owned>(self: RS, r: ty::region)
-> @anon_rscope {
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() {
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() {
--- /dev/null
+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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
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;
}