about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-04-10 17:16:48 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-04-11 17:21:53 -0700
commitc1c60c023ec3b9b5bb85aa68d00667c4ab61ee00 (patch)
tree199e1451073265545e71b930aabb28a8a1d7d8b7
parent8967c4b409396edf610967b329f0bf6cbcee4a2f (diff)
downloadrust-c1c60c023ec3b9b5bb85aa68d00667c4ab61ee00.tar.gz
rust-c1c60c023ec3b9b5bb85aa68d00667c4ab61ee00.zip
first attempt at an assignability check
-rw-r--r--src/rustc/middle/infer.rs178
-rw-r--r--src/test/compile-fail/vec-concat-bug.rs7
-rw-r--r--src/test/run-pass/regions-borrow.rs7
3 files changed, 127 insertions, 65 deletions
diff --git a/src/rustc/middle/infer.rs b/src/rustc/middle/infer.rs
index ce836221d7c..95f5ae19f1c 100644
--- a/src/rustc/middle/infer.rs
+++ b/src/rustc/middle/infer.rs
@@ -201,41 +201,11 @@ impl of st for ty::region {
     }
 }
 
-// Most of these methods, like tys() and so forth, take two parameters
-// a and b and they are tasked with "ensuring that a is a subtype of
-// b".  They return success or failure.  They make changes in-place to
-// the variable bindings: these changes are recorded in the `bindings`
-// array, which then allows the changes to be rolled back if needed.
-//
-// The merge() and merge_bnds() methods are somewhat different in that
-// they compute a new type range for a variable (generally a subset of
-// the old range).  They therefore return a result.
-impl unify_methods for infer_ctxt {
-    fn uok() -> ures {
-        ok(())
-    }
-
-    fn uerr(e: ty::type_err) -> ures {
-        #debug["Unification error: %?", e];
-        err(e)
-    }
-
-    fn set<V:copy vid, T:copy to_str>(
-        vb: vals_and_bindings<V, T>, vid: V,
-        +new_v: var_value<V, T>) {
-
-        let old_v = vb.vals.get(vid.to_uint());
-        vec::push(vb.bindings, (vid, old_v));
-        vb.vals.insert(vid.to_uint(), new_v);
-
-        #debug["Updating variable %s from %s to %s",
-               vid.to_str(), old_v.to_str(self), new_v.to_str(self)];
-    }
-
-    fn set_ty(vid: ty_vid, +new_v: var_value<ty_vid, ty::t>) {
-        self.set(self.vb, vid, new_v);
-    }
+fn uok() -> ures {
+    ok(())
+}
 
+impl methods for infer_ctxt {
     fn commit<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
 
         assert self.vb.bindings.len() == 0u;
@@ -276,6 +246,21 @@ impl unify_methods for infer_ctxt {
         }
         ret r;
     }
+}
+
+impl unify_methods for infer_ctxt {
+
+    fn set<V:copy vid, T:copy to_str>(
+        vb: vals_and_bindings<V, T>, vid: V,
+        +new_v: var_value<V, T>) {
+
+        let old_v = vb.vals.get(vid.to_uint());
+        vec::push(vb.bindings, (vid, old_v));
+        vb.vals.insert(vid.to_uint(), new_v);
+
+        #debug["Updating variable %s from %s to %s",
+               vid.to_str(), old_v.to_str(self), new_v.to_str(self)];
+    }
 
     fn get<V:copy vid, T:copy>(
         vb: vals_and_bindings<V, T>, vid: V)
@@ -397,7 +382,7 @@ impl unify_methods for infer_ctxt {
             // be relatable:
             self.bnds(bnds.lb, bnds.ub).then {||
                 self.set(vb, v_id, bounded(bnds));
-                self.uok()
+                uok()
             }
         }}}}}
     }
@@ -414,7 +399,7 @@ impl unify_methods for infer_ctxt {
                a_id.to_str(), a_bounds.to_str(self),
                b_id.to_str(), b_bounds.to_str(self)];
 
-        if a_id == b_id { ret self.uok(); }
+        if a_id == b_id { ret uok(); }
 
         // If both A's UB and B's LB have already been bound to types,
         // see if we can make those types subtypes.
@@ -437,7 +422,7 @@ impl unify_methods for infer_ctxt {
         // A remains a subtype of B.  Actually, there are other options,
         // but that's the route we choose to take.
         self.set_var_to_merged_bounds(vb, a_id, a_bounds, b_bounds).then {||
-            self.uok()
+            uok()
         }
     }
 
@@ -470,7 +455,7 @@ impl unify_methods for infer_ctxt {
         actual_constr: @ty::type_constr) -> ures {
 
         let err_res =
-            self.uerr(ty::terr_constr_mismatch(expected, actual_constr));
+            err(ty::terr_constr_mismatch(expected, actual_constr));
 
         if expected.node.id != actual_constr.node.id { ret err_res; }
         let expected_arg_len = vec::len(expected.node.args);
@@ -505,7 +490,7 @@ impl unify_methods for infer_ctxt {
             }
             i += 1u;
         }
-        ret self.uok();
+        ret uok();
     }
 
     fn bnds<T:copy to_str st>(
@@ -517,7 +502,7 @@ impl unify_methods for infer_ctxt {
               (none, none) |
               (some(_), none) |
               (none, some(_)) {
-                self.uok()
+                uok()
               }
               (some(t_a), some(t_b)) {
                 t_a.sub(self, t_b)
@@ -534,7 +519,7 @@ impl unify_methods for infer_ctxt {
                 self.constrs(a, b)
             }
         } else {
-            self.uerr(ty::terr_constr_len(bs.len(), as.len()))
+            err(ty::terr_constr_len(bs.len(), as.len()))
         }
     }
 
@@ -689,32 +674,102 @@ impl resolve_methods for infer_ctxt {
 }
 
 // ______________________________________________________________________
+// Type assignment
+//
+// True if rvalues of type `a` can be assigned to lvalues of type `b`.
+
+impl assignment for infer_ctxt {
+    fn assign_tys(a: ty::t, b: ty::t,
+                  encl_blk_id: ast::node_id) -> ures {
+
+        #debug["assign_tys(%s, %s)", a.to_str(self), b.to_str(self)];
+        let _r = indenter();
+
+        alt (ty::get(a).struct, ty::get(b).struct) {
+          (ty::ty_bot, _) {
+            uok()
+          }
+
+          (ty::ty_var(a_id), ty::ty_var(b_id)) {
+            let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
+            let {root:_, bounds:b_bounds} = self.get(self.vb, b_id);
+            self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
+          }
+
+          (ty::ty_var(a_id), _) {
+            let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
+            let b_bounds = {lb: some(b), ub: none};
+            self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
+          }
+
+          (_, ty::ty_var(b_id)) {
+            let a_bounds = {lb: none, ub: some(a)};
+            let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
+            self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
+          }
+
+          (ty::ty_box(a_mt), ty::ty_rptr(_, _)) |
+          (ty::ty_uniq(a_mt), ty::ty_rptr(_, _)) {
+            let a_r = ty::re_scope(encl_blk_id);
+            let a_ty = ty::mk_rptr(self.tcx, a_r, a_mt);
+            self.sub_tys(a_ty, b).to_ures()
+          }
+
+          _ {
+            self.sub_tys(a, b).to_ures()
+          }
+        }
+    }
+
+    fn assign_tys_or_sub(a: ty::t, b: ty::t,
+                         a_b: ty::t, b_b: ty::t,
+                         encl_blk_id: ast::node_id) -> ures {
+        self.try {||
+            self.assign_tys(a_b, b_b, encl_blk_id)
+        }.chain_err {|_e|
+            self.sub_tys(a, b)
+        }
+    }
+
+    fn assign_vars_or_sub(a: ty::t, b: ty::t,
+                          a_bounds: bounds<ty::t>, b_bounds: bounds<ty::t>,
+                          encl_blk_id: ast::node_id) -> ures {
+
+        alt (a_bounds.ub, b_bounds.lb) {
+          (some(a_ub), some(b_lb)) {
+            self.assign_tys_or_sub(a, b, a_ub, b_lb, encl_blk_id)
+          }
+          _ {
+            self.sub_tys(a, b).to_ures()
+          }
+        }
+    }
+}
+
+// ______________________________________________________________________
 // Type combining
 //
-// There are two type combiners, lub and gub.  The first computes the
-// Least Upper Bound of two types `a` and `b`---that is, a mutual
+// There are three type combiners, sub, lub, and gub.  `sub` is a bit
+// different from the other two: it takes two types and makes the first
+// a subtype of the other, or fails if this cannot be done.  It does
+// return a new type but its return value is meaningless---it is only
+// there to allow for greater code reuse.
+//
+// `lub` and `glb` compute the Least Upper Bound and Greatest Lower
+// Bound respectively of two types `a` and `b`.  The LUB is a mutual
 // supertype type `c` where `a <: c` and `b <: c`.  As the name
-// implies, it tries to pick the most precise `c` possible.  `glb`
-// computes the greatest lower bound---that is, it computes a mutual
-// subtype, aiming for the most general such type possible.  Both
-// computations may fail.
+// implies, it tries to pick the most precise `c` possible.  The GLB
+// is a mutual subtype, aiming for the most general such type
+// possible.  Both computations may fail.
 //
 // There is a lot of common code for these operations, which is
-// abstracted out into functions named `c_X()` which take a combiner
+// abstracted out into functions named `super_X()` which take a combiner
 // instance as the first parameter.  This would be better implemented
 // using traits.
 //
-// The `c_X()` top-level items work for *both LUB and GLB*: any
-// operation which varies between LUB and GLB will be dynamically
-// dispatched using a `self.Y()` operation.
-//
-// In principle, the subtyping relation computed above could be built
-// on the combine framework---this would result in less code but would
-// be less efficient.  There is a significant performance gain from
-// not recreating types unless we need to.  Even so, we could write
-// the routines with a few more generics in there to mask type
-// construction (which is, after all, the significant expense) but I
-// haven't gotten around to it.
+// The `super_X()` top-level items work for *sub, lub, and glb*: any
+// operation which varies will be dynamically dispatched using a
+// `self.Y()` operation.
 
 type cres<T> = result<T,ty::type_err>;
 
@@ -736,7 +791,7 @@ iface combine {
     fn regions(a: ty::region, b: ty::region) -> cres<ty::region>;
 }
 
-enum sub = infer_ctxt;  // "less than" == "subtype" or "subregion"
+enum sub = infer_ctxt;  // "subtype", "subregion" etc
 enum lub = infer_ctxt;  // "least upper bound" (common supertype)
 enum glb = infer_ctxt;  // "greatest lower bound" (common subtype)
 
@@ -1445,7 +1500,8 @@ impl of combine for glb {
 // ______________________________________________________________________
 // Lattice operations on variables
 //
-// this is common code used by both LUB and GLB
+// This is common code used by both LUB and GLB to compute the LUB/GLB
+// for pairs of variables or for variables and values.
 
 iface lattice_ops {
     fn bnd<T:copy>(b: bounds<T>) -> option<T>;
diff --git a/src/test/compile-fail/vec-concat-bug.rs b/src/test/compile-fail/vec-concat-bug.rs
index 464203ed6cf..b75623d6233 100644
--- a/src/test/compile-fail/vec-concat-bug.rs
+++ b/src/test/compile-fail/vec-concat-bug.rs
@@ -1,11 +1,10 @@
-// xfail-test
-
 fn concat<T: copy>(v: [const [const T]]) -> [T] {
     let mut r = [];
 
     // Earlier versions of our type checker accepted this:
-    for v.each {|inner|
-        //!^ ERROR found `[const 'a]` (values differ in mutability)
+    vec::iter(v) {|&&inner: [T]|
+        //!^ ERROR values differ in mutability
+        //!^^ ERROR values differ in mutability
         r += inner;
     }
 
diff --git a/src/test/run-pass/regions-borrow.rs b/src/test/run-pass/regions-borrow.rs
new file mode 100644
index 00000000000..1e9d3954097
--- /dev/null
+++ b/src/test/run-pass/regions-borrow.rs
@@ -0,0 +1,7 @@
+fn foo(x: &uint) -> &uint { x }
+
+fn main() {
+    let p = @3u;
+    let r = foo(p);
+    assert *p == *r;
+}
\ No newline at end of file