]> git.lizzy.rs Git - rust.git/commitdiff
Permit bindings of (and references to) associated types defined in supertraits.
authorNiko Matsakis <niko@alum.mit.edu>
Mon, 5 Jan 2015 10:36:41 +0000 (05:36 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 5 Jan 2015 12:11:48 +0000 (07:11 -0500)
src/librustc_typeck/astconv.rs
src/test/compile-fail/associated-type-projection-from-multiple-supertraits.rs [new file with mode: 0644]
src/test/compile-fail/associated-type-projection-from-supertrait.rs [new file with mode: 0644]
src/test/compile-fail/associated-types-binding-to-type-defined-in-supertrait.rs [new file with mode: 0644]
src/test/run-pass/associated-types-iterator-binding.rs [new file with mode: 0644]

index ea1577d52a335c7bb9e407343dee4e03fbd64f87..e216338b1e3a6f5f3e85cc22f6103b5b601a6e26 100644 (file)
@@ -53,7 +53,8 @@
 use middle::resolve_lifetime as rl;
 use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
 use middle::subst::{VecPerParamSpace};
-use middle::ty::{self, RegionEscape, Ty};
+use middle::traits;
+use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty};
 use rscope::{self, UnelidableRscope, RegionScope, SpecificRscope,
              ShiftedRscope, BindingRscope};
 use TypeAndSubsts;
@@ -637,7 +638,7 @@ fn ast_path_to_trait_ref<'a,'tcx>(
     trait_ref
 }
 
-pub fn ast_type_binding_to_projection_predicate<'tcx>(
+fn ast_type_binding_to_projection_predicate<'tcx>(
     this: &AstConv<'tcx>,
     trait_ref: Rc<ty::TraitRef<'tcx>>,
     binding: &ConvertedBinding<'tcx>)
@@ -659,20 +660,56 @@ pub fn ast_type_binding_to_projection_predicate<'tcx>(
     //
     // We want to produce `<B as SuperTrait<int>>::T == foo`.
 
-    // FIXME(#19541): supertrait upcasting not actually impl'd :)
+    // Simple case: X is defined in the current trait.
+    if trait_defines_associated_type_named(this, trait_ref.def_id, binding.item_name) {
+        return Ok(ty::ProjectionPredicate {
+            projection_ty: ty::ProjectionTy {
+                trait_ref: trait_ref,
+                item_name: binding.item_name,
+            },
+            ty: binding.ty,
+        });
+    }
+
+    // Otherwise, we have to walk through the supertraits to find those that do.
+    let mut candidates: Vec<_> =
+        traits::supertraits(this.tcx(), trait_ref.to_poly_trait_ref())
+        .filter(|r| trait_defines_associated_type_named(this, r.def_id(), binding.item_name))
+        .collect();
+
+    if candidates.len() > 1 {
+        this.tcx().sess.span_err(
+            binding.span,
+            format!("ambiguous associated type: `{}` defined in multiple supertraits `{}`",
+                    token::get_name(binding.item_name),
+                    candidates.user_string(this.tcx())).as_slice());
+        return Err(ErrorReported);
+    }
+
+    let candidate = match candidates.pop() {
+        Some(c) => c,
+        None => {
+            this.tcx().sess.span_err(
+                binding.span,
+                format!("no associated type `{}` defined in `{}`",
+                        token::get_name(binding.item_name),
+                        trait_ref.user_string(this.tcx())).as_slice());
+            return Err(ErrorReported);
+        }
+    };
 
-    if !trait_defines_associated_type_named(this, trait_ref.def_id, binding.item_name) {
+    if ty::binds_late_bound_regions(this.tcx(), &candidate) {
         this.tcx().sess.span_err(
             binding.span,
-            format!("no associated type `{}` defined in `{}`",
+            format!("associated type `{}` defined in higher-ranked supertrait `{}`",
                     token::get_name(binding.item_name),
-                    trait_ref.user_string(this.tcx())).as_slice());
+                    candidate.user_string(this.tcx())).as_slice());
         return Err(ErrorReported);
     }
 
     Ok(ty::ProjectionPredicate {
         projection_ty: ty::ProjectionTy {
-            trait_ref: trait_ref,
+            trait_ref: candidate.0,
             item_name: binding.item_name,
         },
         ty: binding.ty,
@@ -899,6 +936,7 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>,
 {
     let tcx = this.tcx();
     let ty_param_def_id = provenance.def_id();
+
     let mut suitable_bounds: Vec<_>;
     let ty_param_name: ast::Name;
     { // contain scope of refcell:
@@ -906,13 +944,9 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>,
         let ty_param_def = &ty_param_defs[ty_param_def_id.node];
         ty_param_name = ty_param_def.name;
 
-        // FIXME(#19541): we should consider associated types in
-        // super-traits. Probably by elaborating the bounds.
-
+        // FIXME(#20300) -- search where clauses, not bounds
         suitable_bounds =
-            ty_param_def.bounds.trait_bounds // FIXME(#20300) -- search where clauses, not bounds
-            .iter()
-            .cloned()
+            traits::transitive_bounds(tcx, ty_param_def.bounds.trait_bounds.as_slice())
             .filter(|b| trait_defines_associated_type_named(this, b.def_id(), assoc_name))
             .collect();
     }
diff --git a/src/test/compile-fail/associated-type-projection-from-multiple-supertraits.rs b/src/test/compile-fail/associated-type-projection-from-multiple-supertraits.rs
new file mode 100644 (file)
index 0000000..553e36f
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test equality constraints in a where clause where the type being
+// equated appears in a supertrait.
+
+#![feature(associated_types)]
+
+pub trait Vehicle {
+    type Color;
+
+    fn go(&self) {  }
+}
+
+pub trait Box {
+    type Color;
+
+    fn mail(&self) {  }
+}
+
+pub trait BoxCar : Box + Vehicle {
+}
+
+fn dent<C:BoxCar>(c: C, color: C::Color) {
+    //~^ ERROR ambiguous associated type `Color` in bounds of `C`
+    //~| NOTE could derive from `Vehicle`
+    //~| NOTE could derive from `Box`
+}
+
+fn dent_object<COLOR>(c: BoxCar<Color=COLOR>) {
+    //~^ ERROR ambiguous associated type
+}
+
+pub fn main() { }
diff --git a/src/test/compile-fail/associated-type-projection-from-supertrait.rs b/src/test/compile-fail/associated-type-projection-from-supertrait.rs
new file mode 100644 (file)
index 0000000..01f9bd3
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test equality constraints in a where clause where the type being
+// equated appears in a supertrait.
+
+#![feature(associated_types)]
+
+pub trait Vehicle {
+    type Color;
+
+    fn go(&self) {  }
+}
+
+pub trait Car : Vehicle {
+    fn honk(&self) { }
+    fn chip_paint(&self, c: Self::Color) { }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+struct Black;
+struct ModelT;
+impl Vehicle for ModelT { type Color = Black; }
+impl Car for ModelT { }
+
+///////////////////////////////////////////////////////////////////////////
+
+struct Blue;
+struct ModelU;
+impl Vehicle for ModelU { type Color = Blue; }
+impl Car for ModelU { }
+
+///////////////////////////////////////////////////////////////////////////
+
+fn dent<C:Car>(c: C, color: C::Color) { c.chip_paint(color) }
+fn a() { dent(ModelT, Black); }
+fn b() { dent(ModelT, Blue); } //~ ERROR type mismatch
+fn c() { dent(ModelU, Black); } //~ ERROR type mismatch
+fn d() { dent(ModelU, Blue); }
+
+///////////////////////////////////////////////////////////////////////////
+
+fn e() { ModelT.chip_paint(Black); }
+fn f() { ModelT.chip_paint(Blue); } //~ ERROR mismatched types
+fn g() { ModelU.chip_paint(Black); } //~ ERROR mismatched types
+fn h() { ModelU.chip_paint(Blue); }
+
+pub fn main() { }
diff --git a/src/test/compile-fail/associated-types-binding-to-type-defined-in-supertrait.rs b/src/test/compile-fail/associated-types-binding-to-type-defined-in-supertrait.rs
new file mode 100644 (file)
index 0000000..a362529
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test equality constraints in a where clause where the type being
+// equated appears in a supertrait.
+
+#![feature(associated_types)]
+
+pub trait Vehicle {
+    type Color;
+
+    fn go(&self) {  }
+}
+
+pub trait Car : Vehicle {
+    fn honk(&self) { }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+struct Black;
+struct ModelT;
+impl Vehicle for ModelT { type Color = Black; }
+impl Car for ModelT { }
+
+///////////////////////////////////////////////////////////////////////////
+
+struct Blue;
+struct ModelU;
+impl Vehicle for ModelU { type Color = Blue; }
+impl Car for ModelU { }
+
+///////////////////////////////////////////////////////////////////////////
+
+fn black_car<C:Car<Color=Black>>(c: C) {
+}
+
+fn blue_car<C:Car<Color=Blue>>(c: C) {
+}
+
+fn a() { black_car(ModelT); }
+fn b() { blue_car(ModelT); } //~ ERROR type mismatch
+fn c() { black_car(ModelU); } //~ ERROR type mismatch
+fn d() { blue_car(ModelU); }
+
+pub fn main() { }
diff --git a/src/test/run-pass/associated-types-iterator-binding.rs b/src/test/run-pass/associated-types-iterator-binding.rs
new file mode 100644 (file)
index 0000000..f825846
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn pairwise_sub<T:DoubleEndedIterator<Item=int>>(mut t: T) -> int {
+    let mut result = 0;
+    loop {
+        let front = t.next();
+        let back = t.next_back();
+        match (front, back) {
+            (Some(f), Some(b)) => { result += b - f; }
+            _ => { return result; }
+        }
+    }
+}
+
+fn main() {
+    let v = vec!(1, 2, 3, 4, 5, 6);
+    let r =pairwise_sub(v.into_iter());
+    assert_eq!(r, 9);
+}