about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-04-17 08:40:06 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-04-17 12:02:36 -0700
commit6b549f9f147b18eaeb3a4c185e45ccd8ae310b09 (patch)
treec9fc9bb0d9a166bb8f61675a019a80c728a97114
parent58f8cb343d384b52ade37235aec40f1f0fec1df4 (diff)
repair LUB/GLB of free/scope regions, and enable test
-rw-r--r--src/rustc/middle/infer.rs101
-rw-r--r--src/rustc/middle/ty.rs4
-rw-r--r--src/test/compile-fail/regions-nested-fns.rs10
3 files changed, 74 insertions, 41 deletions
diff --git a/src/rustc/middle/infer.rs b/src/rustc/middle/infer.rs
index 074ba50e02c..24130a56048 100644
--- a/src/rustc/middle/infer.rs
+++ b/src/rustc/middle/infer.rs
@@ -1373,23 +1373,28 @@ impl of combine for lub {
                 ok(ty::re_static) // nothing lives longer than static
               }
 
-              (ty::re_var(a_id), ty::re_var(b_id)) {
-                lattice_vars(self, self.infcx().rb,
-                           a, a_id, b_id,
-                           {|x, y| self.regions(x, y) })
-              }
-
-              (ty::re_var(v_id), r) | (r, ty::re_var(v_id)) {
-                lattice_var_t(self, self.infcx().rb,
-                              v_id, r,
-                              {|x, y| self.regions(x, y) })
+              (ty::re_var(_), _) | (_, ty::re_var(_)) {
+                lattice_rvars(self, a, b)
               }
 
-              (f @ ty::re_free(f_id, f_br), ty::re_scope(s_id)) |
-              (ty::re_scope(s_id), f @ ty::re_free(f_id, f_br)) {
-                // for LUB, the scope is within the function and the free
-                // region is always a parameter to the method.
-                ok(f) // NDM--not so for nested functions
+              (f @ ty::re_free(f_id, _), ty::re_scope(s_id)) |
+              (ty::re_scope(s_id), f @ ty::re_free(f_id, _)) {
+                // For LUB, generally the scope is within the fn and
+                // the free region is a parameter to the fn.  In that case,
+                // the free region will always live as long as the fn,
+                // which is longer than the scope.
+                //
+                // However, with nested fns, it can happen that the
+                // scope surrounds the fn itself.  In that case, we do
+                // not know which will live longer---it depends on the
+                // value provided for the free region in any given
+                // call.  And so we must just back off to re_static as
+                // the LUB.
+                let rm = self.infcx().tcx.region_map;
+                alt region::nearest_common_ancestor(rm, f_id, s_id) {
+                  some(r_id) if r_id == f_id { ok(f) }
+                  _ { ok(ty::re_static) }
+                }
               }
 
               (ty::re_scope(a_id), ty::re_scope(b_id)) {
@@ -1399,7 +1404,7 @@ impl of combine for lub {
                 let rm = self.infcx().tcx.region_map;
                 alt region::nearest_common_ancestor(rm, a_id, b_id) {
                   some(r_id) { ok(ty::re_scope(r_id)) }
-                  _ { err(ty::terr_regions_differ(b, a)) }
+                  _ { ok(ty::re_static) }
                 }
               }
 
@@ -1414,7 +1419,7 @@ impl of combine for lub {
                 if a == b {
                     ok(a)
                 } else {
-                    err(ty::terr_regions_differ(b, a))
+                    ok(ty::re_static)
                 }
               }
             }
@@ -1551,24 +1556,26 @@ impl of combine for glb {
                 ok(r)
               }
 
-              (ty::re_var(a_id), ty::re_var(b_id)) {
-                lattice_vars(self, self.infcx().rb,
-                             a, a_id, b_id,
-                             {|x, y| self.regions(x, y) })
-              }
-
-              (ty::re_var(v_id), r) | (r, ty::re_var(v_id)) {
-                lattice_var_t(self, self.infcx().rb,
-                              v_id, r,
-                              {|x, y| self.regions(x, y) })
+              (ty::re_var(_), _) | (_, ty::re_var(_)) {
+                lattice_rvars(self, a, b)
               }
 
-              (f @ ty::re_free(f_id, f_br), ty::re_scope(s_id)) |
-              (ty::re_scope(s_id), f @ ty::re_free(f_id, f_br)) {
-                // for GLB, the scope is within the function and the free
-                // region is always a parameter to the method.  So the GLB
-                // must be the scope.
-                ok(b) // NDM--not so for nested functions
+              (ty::re_free(f_id, _), s @ ty::re_scope(s_id)) |
+              (s @ ty::re_scope(s_id), ty::re_free(f_id, _)) {
+                // For GLB, generally the scope is within the fn and
+                // the free region is a parameter to the fn.  In that case,
+                // the scope is always shorter than the free region.
+                //
+                // However, with nested fns, it can happen that the
+                // scope surrounds the fn itself.  In that case, we do
+                // not know which will live longer---it depends on the
+                // value provided for the free region in any given
+                // call.  And so we cannot give a GLB.
+                let rm = self.infcx().tcx.region_map;
+                alt region::nearest_common_ancestor(rm, f_id, s_id) {
+                  some(r_id) if r_id == f_id { ok(s) }
+                  _ { err(ty::terr_regions_differ(b, a)) }
+                }
               }
 
               (ty::re_scope(a_id), ty::re_scope(b_id)) {
@@ -1704,6 +1711,34 @@ fn lattice_tys<L:lattice_ops combine>(
     }
 }
 
+// Pull out some common code from LUB/GLB for handling region vars:
+fn lattice_rvars<L:lattice_ops combine>(
+    self: L, a: ty::region, b: ty::region) -> cres<ty::region> {
+
+    alt (a, b) {
+      (ty::re_var(a_id), ty::re_var(b_id)) {
+        lattice_vars(self, self.infcx().rb,
+                     a, a_id, b_id,
+                     {|x, y| self.regions(x, y) })
+      }
+
+      (ty::re_var(v_id), r) | (r, ty::re_var(v_id)) {
+        lattice_var_t(self, self.infcx().rb,
+                      v_id, r,
+                      {|x, y| self.regions(x, y) })
+      }
+
+      _ {
+        self.infcx().tcx.sess.bug(
+            #fmt["%s: lattice_rvars invoked with a=%s and b=%s, \
+                  neither of which are region variables",
+                 self.tag(),
+                 a.to_str(self.infcx()),
+                 b.to_str(self.infcx())]);
+      }
+    }
+}
+
 fn lattice_vars<V:copy vid, T:copy to_str st, L:lattice_ops combine>(
     self: L, vb: vals_and_bindings<V, T>,
     a_t: T, a_vid: V, b_vid: V,
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 20cb0990dd3..3c7421b2fd2 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -2078,8 +2078,8 @@ fn type_err_to_str(cx: ctxt, err: type_err) -> str {
                 ty_constr_to_str(a_constr);
       }
       terr_regions_differ(subregion, superregion) {
-        ret #fmt("references with lifetime %s do not outlive references with \
-                  lifetime %s",
+        ret #fmt("references with lifetime %s do not necessarily \
+                  outlive references with lifetime %s",
                  region_to_str(cx, subregion),
                  region_to_str(cx, superregion));
       }
diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs
index 5953fe9348f..3f0a871a342 100644
--- a/src/test/compile-fail/regions-nested-fns.rs
+++ b/src/test/compile-fail/regions-nested-fns.rs
@@ -1,5 +1,3 @@
-// xfail-test
-
 fn ignore<T>(t: T) {}
 
 fn nested(x: &x.int) {
@@ -9,13 +7,13 @@ fn nested(x: &x.int) {
     ignore(fn&(z: &z.int) {
         ay = x;
         ay = &y;
-        ay = z; //! ERROR foo
+        ay = z; //! ERROR references with lifetime
     });
 
     ignore(fn&(z: &z.int) -> &z.int {
-        if false { ret x; }  //! ERROR bar
-        if false { ret &y; } //! ERROR bar
-        if false { ret ay; } //! ERROR bar
+        if false { ret x; }  //! ERROR references with lifetime
+        if false { ret &y; } //! ERROR references with lifetime
+        if false { ret ay; } //! ERROR references with lifetime
         ret z;
     });
 }