about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-10-05 11:50:53 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-10-05 11:52:08 -0700
commit1b732145ec569cd67e2a5d0cc4dd37d5cd60c961 (patch)
treef322c22432d6427d6665ea4f3c5615526d06fb5d
parentc56a7e5c25c2a411ba119f3a17bfa6db0b1722ed (diff)
rustc: Implement simple trait inheritance.
Generic trait inheritance, cross-crate trait inheritance, and vtable-based
trait inheritance don't work yet.
-rw-r--r--src/rustc/middle/resolve.rs6
-rw-r--r--src/rustc/middle/typeck/check/vtable.rs102
-rw-r--r--src/rustc/middle/typeck/coherence.rs37
-rw-r--r--src/test/run-pass/trait-inheritance-simple.rs26
4 files changed, 136 insertions, 35 deletions
diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs
index eca0687f2fd..5f30346a28e 100644
--- a/src/rustc/middle/resolve.rs
+++ b/src/rustc/middle/resolve.rs
@@ -4181,9 +4181,9 @@ impl Resolver {
         }
 
         return self.resolve_identifier(path.idents.last(),
-                                    namespace,
-                                    check_ribs,
-                                    path.span);
+                                       namespace,
+                                       check_ribs,
+                                       path.span);
     }
 
     fn resolve_identifier(identifier: ident,
diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs
index e8595b2f50a..e8926468b3a 100644
--- a/src/rustc/middle/typeck/check/vtable.rs
+++ b/src/rustc/middle/typeck/check/vtable.rs
@@ -51,8 +51,8 @@ fn lookup_vtables(fcx: @fn_ctxt,
             match *bound {
               ty::bound_trait(i_ty) => {
                 let i_ty = ty::subst(tcx, substs, i_ty);
-                match lookup_vtable(fcx, expr, *ty, i_ty, allow_unsafe,
-                                    is_early) {
+                match lookup_vtable_covariant(fcx, expr, *ty, i_ty,
+                                              allow_unsafe, is_early) {
                     Some(vtable) => result.push(vtable),
                     None => {
                         fcx.tcx().sess.span_fatal(
@@ -91,20 +91,67 @@ fn relate_trait_tys(fcx: @fn_ctxt, expr: @ast::expr,
     demand::suptype(fcx, expr.span, exp_trait_ty, act_trait_ty)
 }
 
-/*
-Look up the vtable to use when treating an item of type <t>
-as if it has type <trait_ty>
-*/
-fn lookup_vtable(fcx: @fn_ctxt,
-                 expr: @ast::expr,
-                 ty: ty::t,
-                 trait_ty: ty::t,
-                 allow_unsafe: bool,
-                 is_early: bool)
-    -> Option<vtable_origin>
-{
+// Look up the vtable to use when treating an item of type `t` as if it has
+// type `trait_ty`. This does allow subtraits.
+fn lookup_vtable_covariant(fcx: @fn_ctxt,
+                           expr: @ast::expr,
+                           ty: ty::t,
+                           trait_ty: ty::t,
+                           allow_unsafe: bool,
+                           is_early: bool)
+                        -> Option<vtable_origin> {
+    let worklist = dvec::DVec();
+    worklist.push(trait_ty);
+    while worklist.len() > 0 {
+        let trait_ty = worklist.pop();
+        let result = lookup_vtable_invariant(fcx, expr, ty, trait_ty,
+                                             allow_unsafe, is_early);
+        if result.is_some() {
+            return result;
+        }
+
+        // Add subtraits to the worklist, if applicable.
+        match ty::get(trait_ty).sty {
+            ty::ty_trait(trait_id, _, _) => {
+                let table = fcx.ccx.coherence_info.supertrait_to_subtraits;
+                match table.find(trait_id) {
+                    None => {}
+                    Some(subtraits) => {
+                        for subtraits.each |subtrait_id| {
+                            // XXX: This is wrong; subtraits should themselves
+                            // have substs.
+                            let substs =
+                                { self_r: None, self_ty: None, tps: ~[] };
+                            let trait_ty = ty::mk_trait(fcx.ccx.tcx,
+                                                        *subtrait_id,
+                                                        substs,
+                                                        ty::vstore_box);
+                            worklist.push(trait_ty);
+                        }
+                    }
+                }
+            }
+            _ => {
+                fcx.ccx.tcx.sess.impossible_case(expr.span,
+                                                 "lookup_vtable_covariant: \
+                                                  non-trait in worklist");
+            }
+        }
+    }
 
-    debug!("lookup_vtable(ty=%s, trait_ty=%s)",
+    return None;
+}
+
+// Look up the vtable to use when treating an item of type `t` as if it has
+// type `trait_ty`. This does not allow subtraits.
+fn lookup_vtable_invariant(fcx: @fn_ctxt,
+                           expr: @ast::expr,
+                           ty: ty::t,
+                           trait_ty: ty::t,
+                           allow_unsafe: bool,
+                           is_early: bool)
+                        -> Option<vtable_origin> {
+    debug!("lookup_vtable_invariant(ty=%s, trait_ty=%s)",
            fcx.infcx().ty_to_str(ty), fcx.inh.infcx.ty_to_str(trait_ty));
     let _i = indenter();
 
@@ -112,7 +159,7 @@ fn lookup_vtable(fcx: @fn_ctxt,
     let (trait_id, trait_substs) = match ty::get(trait_ty).sty {
         ty::ty_trait(did, substs, _) => (did, substs),
         _ => tcx.sess.impossible_case(expr.span,
-                                      "lookup_vtable: \
+                                      "lookup_vtable_invariant: \
                                        don't know how to handle a non-trait")
     };
     let ty = match fixup_ty(fcx, expr, ty, is_early) {
@@ -150,7 +197,7 @@ fn lookup_vtable(fcx: @fn_ctxt,
                             }
                             _ => tcx.sess.impossible_case(
                                 expr.span,
-                                "lookup_vtable: in loop, \
+                                "lookup_vtable_invariant: in loop, \
                                  don't know how to handle a non-trait ity")
                         }
                         n_bound += 1u;
@@ -462,13 +509,14 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
         let target_ty = fcx.expr_ty(ex);
         match ty::get(target_ty).sty {
           ty::ty_trait(*) => {
-            /*
-            Look up vtables for the type we're casting to,
-            passing in the source and target type
-            */
+            // Look up vtables for the type we're casting to, passing in the
+            // source and target type.
+            //
+            // XXX: This is invariant and shouldn't be. --pcw
+
             let ty = fcx.expr_ty(src);
-            let vtable_opt = lookup_vtable(fcx, ex, ty, target_ty, true,
-                                           is_early);
+            let vtable_opt = lookup_vtable_invariant(fcx, ex, ty, target_ty,
+                                                     true, is_early);
             match vtable_opt {
                 None => {
                     // Try the new-style boxed trait; "@int as @Trait".
@@ -476,10 +524,10 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
                     let ty = structurally_resolved_type(fcx, ex.span, ty);
                     match ty::get(ty).sty {
                         ty::ty_box(boxed_ty) => {
-                            let vtable_opt = lookup_vtable(fcx, ex,
-                                                           boxed_ty.ty,
-                                                           target_ty, true,
-                                                           is_early);
+                            let vtable_opt =
+                                lookup_vtable_invariant(fcx, ex, boxed_ty.ty,
+                                                        target_ty, true,
+                                                        is_early);
                             match vtable_opt {
                                 Some(vtable) => {
                                     /*
diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs
index e9238e30c66..89cd696eb6f 100644
--- a/src/rustc/middle/typeck/coherence.rs
+++ b/src/rustc/middle/typeck/coherence.rs
@@ -126,12 +126,16 @@ struct CoherenceInfo {
     // Contains implementations of methods associated with a trait. For these,
     // the associated trait must be imported at the call site.
     extension_methods: HashMap<def_id,@DVec<@Impl>>,
+
+    // A mapping from a supertrait to its subtraits.
+    supertrait_to_subtraits: HashMap<def_id,@DVec<def_id>>
 }
 
 fn CoherenceInfo() -> CoherenceInfo {
     CoherenceInfo {
         inherent_methods: HashMap(),
-        extension_methods: HashMap()
+        extension_methods: HashMap(),
+        supertrait_to_subtraits: HashMap()
     }
 }
 
@@ -161,7 +165,6 @@ struct CoherenceChecker {
 }
 
 impl CoherenceChecker {
-
     // Create a mapping containing a MethodInfo for every provided
     // method in every trait.
     fn build_provided_methods_map(crate: @crate) {
@@ -225,9 +228,9 @@ impl CoherenceChecker {
     }
 
     fn check_coherence(crate: @crate) {
-
-        // Check implementations. This populates the tables containing the
-        // inherent methods and extension methods.
+        // Check implementations and traits. This populates the tables
+        // containing the inherent methods and extension methods. It also
+        // builds up the trait inheritance table.
         visit_crate(*crate, (), mk_simple_visitor(@{
             visit_item: |item| {
                 debug!("(checking coherence) item '%s'",
@@ -240,6 +243,9 @@ impl CoherenceChecker {
                     item_class(struct_def, _) => {
                         self.check_implementation(item, struct_def.traits);
                     }
+                    item_trait(_, supertraits, _) => {
+                        self.register_inherited_trait(item, supertraits);
+                    }
                     _ => {
                         // Nothing to do.
                     }
@@ -324,6 +330,27 @@ impl CoherenceChecker {
         }
     }
 
+    fn register_inherited_trait(item: @item, supertraits: ~[@trait_ref]) {
+        // XXX: This is wrong. We need to support substitutions; e.g.
+        // trait Foo : Bar<int>.
+        let supertrait_to_subtraits =
+            self.crate_context.coherence_info.supertrait_to_subtraits;
+        let subtrait_id = local_def(item.id);
+        for supertraits.each |supertrait| {
+            let supertrait_id = self.trait_ref_to_trait_def_id(*supertrait);
+            match supertrait_to_subtraits.find(supertrait_id) {
+                None => {
+                    let new_vec = @dvec::DVec();
+                    new_vec.push(subtrait_id);
+                    supertrait_to_subtraits.insert(supertrait_id, new_vec);
+                }
+                Some(existing_vec) => {
+                    existing_vec.push(subtrait_id);
+                }
+            }
+        }
+    }
+
     fn add_inherent_method(base_def_id: def_id, implementation: @Impl) {
         let implementation_list;
         match self.crate_context.coherence_info.inherent_methods
diff --git a/src/test/run-pass/trait-inheritance-simple.rs b/src/test/run-pass/trait-inheritance-simple.rs
new file mode 100644
index 00000000000..fcd4cf1de6b
--- /dev/null
+++ b/src/test/run-pass/trait-inheritance-simple.rs
@@ -0,0 +1,26 @@
+trait Foo {
+    fn f();
+}
+
+trait Bar : Foo {
+    fn g();
+}
+
+struct A {
+    x: int
+}
+
+impl A : Bar {
+    fn g() { io::println("in g"); }
+    fn f() { io::println("in f"); }
+}
+
+fn h<T:Foo>(a: &T) {
+    a.f();
+}
+
+fn main() {
+    let a = A { x: 3 };
+    h(&a);
+}
+