]> git.lizzy.rs Git - rust.git/commitdiff
Infer variance of types with respect to the region parameter.
authorNiko Matsakis <niko@alum.mit.edu>
Thu, 9 Aug 2012 16:59:50 +0000 (09:59 -0700)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 23 Aug 2012 13:30:43 +0000 (06:30 -0700)
A similar approach could be used for type parameters.

Fixes #2282.

33 files changed:
src/libcore/task.rs
src/libsyntax/ast_map.rs
src/rustc/metadata/csearch.rs
src/rustc/metadata/decoder.rs
src/rustc/metadata/encoder.rs
src/rustc/middle/astencode.rs
src/rustc/middle/region.rs
src/rustc/middle/trans/base.rs
src/rustc/middle/ty.rs
src/rustc/middle/typeck.rs
src/rustc/middle/typeck/astconv.rs
src/rustc/middle/typeck/check.rs
src/rustc/middle/typeck/coherence.rs
src/rustc/middle/typeck/collect.rs
src/rustc/middle/typeck/infer/combine.rs
src/rustc/middle/typeck/infer/glb.rs
src/rustc/middle/typeck/infer/lub.rs
src/rustc/middle/typeck/infer/sub.rs
src/rustc/middle/typeck/rscope.rs
src/test/compile-fail/regions-creating-enums3.rs
src/test/compile-fail/regions-creating-enums4.rs
src/test/compile-fail/regions-infer-contravariance-due-to-immutability.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-covariance-due-to-arg.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-arg-and-ret.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-mutability-1.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-mutability-3.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs [new file with mode: 0644]
src/test/compile-fail/regions-infer-invariance-due-to-mutability.rs [new file with mode: 0644]
src/test/run-pass/regions-infer-contravariance-due-to-ret.rs [new file with mode: 0644]
src/test/run-pass/regions-infer-contravariance.rs [new file with mode: 0644]
src/test/run-pass/regions-trait.rs

index 85d1ee3da94cc37680310c10ce92da1587fd56bd..8d6453f0059701804af93764edefd780d94ae56f 100644 (file)
@@ -770,6 +770,7 @@ fn taskset_each(tasks: &TaskSet, blk: fn(+*rust_task) -> bool) {
 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 {
index 2cc8cb23f2e48e74bfc22794385e886799cf7b51..9c90a422c53a4cb3b4b71785fc26cb98312c947d 100644 (file)
@@ -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=%?)",
index 52e6841b2ed830897f899595b1330067f00f08d9..09de007f0a56937faf4fac53eed67e2794376c02 100644 (file)
@@ -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<ty::region_variance> {
     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,
index a1a2b7245eb983c176ad491a84e234452f224c70..b78be76dc690aeb7390639bb39c364db3a80aa12 100644 (file)
@@ -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<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 {
@@ -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<ty::region_variance> {
+
     let item = lookup_item(id, cdata.data);
     return item_ty_region_param(item);
 }
index 7b2e2b8a2174998bc5fef09dd45be3daa7ecdc15..f37cdd331ccad38dd48cb36e9ccc056d4a6691f3 100644 (file)
@@ -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) {
index 34ebeb2fe199cc1532bb8de44f1d629e31f12377..f6008ead9395cdc69c37ea28981338a9ded66ce6 100644 (file)
@@ -601,8 +601,10 @@ fn emit_tpbt(ecx: @e::encode_ctxt, tpbt: ty::ty_param_bounds_and_ty) {
                     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 @@ fn read_ty_param_bounds_and_ty(xcx: extended_decode_ctxt)
                 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)
index d3c34ceea4d8651980af12d63757f6f0bb92eb3c..aa2dc699940c0a52593712529109a2ac3b2677f5 100644 (file)
@@ -16,6 +16,7 @@
 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<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,
@@ -381,42 +383,98 @@ fn resolve_crate(sess: session, def_map: resolve3::DefMap,
 
     // 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 @@ fn region_is_relevant(r: @ast::region) -> bool {
         }
     }
 
-    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 @@ fn with(item_id: ast::node_id, anon_implies_rp: bool, f: fn()) {
         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<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);
     }
 }
 
@@ -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(*) => {
+      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,
@@ -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;
 }
index 2f838ad8d40eb71d6f3ba20b879c71e47aeee50e..7ef772a311215d8f442ea0250b5e4fb0bed02721 100644 (file)
@@ -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);
index 773d8f3a684462861ce69512259dbce79ad8dd20..13bb97fcdbf9da0bf94518e0a109c0ec27b9d5a1 100644 (file)
@@ -17,6 +17,8 @@
 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
 
@@ -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<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.
@@ -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<region_variance>,
                                ty: t};
 
 type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>;
index 807aff1617462c24cd38d72a72f19065214a4e37..641d2e89c6c077ee1e675081d25847b967537995 100644 (file)
@@ -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(
index 76788bd0555528438485b999e1a39ad076c53c19..a636fbded1a2ee27bc5fecd7787b14eabfd0eff5 100644 (file)
@@ -84,20 +84,20 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
     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<AC: ast_conv, RS: region_scope copy owned>(
                  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))
       }
     };
index 87817bd2e077ec061407245983d93b1b517c46b7..535ba01fb92644040d53ef47fdf550510d5db04e 100644 (file)
@@ -71,7 +71,8 @@
 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
@@ -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 @@ fn with_region_lb<R>(lb: ast::node_id, f: fn() -> R) -> R {
         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 {
@@ -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 get_node(f: spanned<field>) -> field { f.node }
         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 get_node(f: spanned<field>) -> field { f.node }
 
                     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 get_node(f: spanned<field>) -> field { f.node }
         } 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)
       }
     };
 
index b7f39f4ba405adebd34de5d327980956ba43fcb7..5691af2b91a94d0999e4c25d7ac55a7e96a8181c 100644 (file)
@@ -402,11 +402,9 @@ fn polytypes_unify(polytype_a: ty_param_bounds_and_ty,
 
     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 =
index 5359e43401cf749b002b4b62e34dca147ffeb296..85b1ebc58de2371520f99c699e64794399c828f4 100644 (file)
@@ -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<ty::region_variance>) {
     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 store_methods<T>(ccx: @crate_ctxt, id: ast::node_id,
 
     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,
@@ -197,20 +198,22 @@ fn make_static_method_ty(ccx: @crate_ctxt,
         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
@@ -220,7 +223,7 @@ fn make_static_method_ty(ccx: @crate_ctxt,
         // 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 replace_bound_self(tcx: ty::ctxt, ty: ty::t,
 
 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]) {
@@ -370,21 +373,23 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
 } // 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;
@@ -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<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| {
@@ -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::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, @~[],
@@ -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::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,
@@ -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<ty::region_variance>)
     -> (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<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}}
 }
index a955165ace0c671cfec07b38dfd9cfff7081f0aa..308818d50f013b6e84a7f4db4f58efc107efc788 100644 (file)
@@ -58,7 +58,8 @@ trait combine {
     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>;
@@ -148,12 +149,57 @@ fn eq_opt_regions<C:combine>(
 }
 
 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})
             }
         }
@@ -348,7 +394,7 @@ fn super_tys<C:combine>(
       (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<C:combine>(
       (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<C:combine>(
 
       (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))
         }
       }
index ae71e97c12526319d42f48e0ba8c918f0401bc6e..966611ab0d9c727b9774ce66dd3f4e8ff94a96b1 100644 (file)
@@ -151,8 +151,10 @@ fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
         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]> {
index 66bf31b1715d4b53de066c5f37fafa6b3dd4d523..d5a6b44aa5c7df2a77aa0600aa26ba07b784f0e9 100644 (file)
@@ -130,8 +130,10 @@ fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
         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]> {
index 682afb4ea073db7cc182bd887fc5889bc194fe21..d2bdc61724ccf64201f5068491b54396190ec584 100644 (file)
@@ -184,8 +184,10 @@ fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
         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]> {
index f11b09913afa228dcda40b187aabbdec765981a3..340399427fc94f987ae4a4d9e9376b90bd5f1c74 100644 (file)
@@ -17,14 +17,13 @@ fn named_region(_span: span, id: ast::ident) -> result<ty::region, ~str> {
     }
 }
 
-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> {
@@ -39,6 +38,13 @@ 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 {
index 5fe93584dd5331d1a69ec847bd9628c290e7d61b..434b360d18cf0d70bd0e29f1e40887616c2a84af 100644 (file)
@@ -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() {
index ec3acd625596afacfed549f6150660ca4c1f5d1e..e4c86c14b3e85a8128b6399563c619b840635212 100644 (file)
@@ -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 (file)
index 0000000..07d456d
--- /dev/null
@@ -0,0 +1,18 @@
+struct contravariant {
+    f: &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-contravariance-due-to-ret.rs b/src/test/compile-fail/regions-infer-contravariance-due-to-ret.rs
new file mode 100644 (file)
index 0000000..9703507
--- /dev/null
@@ -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 (file)
index 0000000..a2a0d13
--- /dev/null
@@ -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 (file)
index 0000000..d7ed0ed
--- /dev/null
@@ -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 (file)
index 0000000..0b0a8bb
--- /dev/null
@@ -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-2.rs b/src/test/compile-fail/regions-infer-invariance-due-to-mutability-2.rs
new file mode 100644 (file)
index 0000000..cad76bb
--- /dev/null
@@ -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 (file)
index 0000000..0f84411
--- /dev/null
@@ -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 (file)
index 0000000..e39293d
--- /dev/null
@@ -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 (file)
index 0000000..f00888a
--- /dev/null
@@ -0,0 +1,18 @@
+struct invariant {
+    mut f: &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/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 (file)
index 0000000..e6b0089
--- /dev/null
@@ -0,0 +1,18 @@
+struct boxed_int {
+    f: &int;
+}
+
+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 (file)
index 0000000..0007556
--- /dev/null
@@ -0,0 +1,21 @@
+struct boxed_int {
+    f: &int;
+}
+
+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
index 4be94ba2446931b6a963437f52a9fa8ab4b8751e..d5e009c5bb7afeab55e45bc26abd6f998dd1b351 100644 (file)
@@ -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;
 }