diff --git a/src/libcollections/btree/node.rs b/src/libcollections/btree/node.rs index 0a93bbf89c997..1c646032bf15a 100644 --- a/src/libcollections/btree/node.rs +++ b/src/libcollections/btree/node.rs @@ -1417,7 +1417,7 @@ pub type MutTraversal<'a, K, V> = AbsTraversal = AbsTraversal>; - +#[old_impl_check] impl> Iterator for AbsTraversal { type Item = TraversalItem; @@ -1433,6 +1433,7 @@ impl> Iterator for AbsTraversal { } } +#[old_impl_check] impl> DoubleEndedIterator for AbsTraversal { fn next_back(&mut self) -> Option> { let tail_is_edge = self.tail_is_edge; diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 5bf5f78af94c2..00d3e795f7432 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -26,6 +26,7 @@ #![feature(unsafe_destructor, slicing_syntax)] #![feature(unboxed_closures)] #![feature(old_orphan_check)] +#![feature(old_impl_check)] #![feature(associated_types)] #![no_std] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 3ed712b15dfdc..d22e3208bbef7 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -28,6 +28,7 @@ #![feature(rustc_diagnostic_macros)] #![feature(unboxed_closures)] #![feature(old_orphan_check)] +#![feature(old_impl_check)] #![feature(associated_types)] extern crate arena; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 425e34cd9f042..69da0f9a2109b 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -669,6 +669,7 @@ impl LintPass for UnusedAttributes { // FIXME: #19470 this shouldn't be needed forever "old_orphan_check", + "old_impl_check", ]; static CRATE_ATTRS: &'static [&'static str] = &[ diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 8c2a9993004d9..e5e425c9c2699 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1349,6 +1349,7 @@ impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for ty::Binder { } } +#[old_impl_check] impl<'tcx, S, H, K, V> Repr<'tcx> for HashMap where K : Hash + Eq + Repr<'tcx>, V : Repr<'tcx>, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index bbafcdae1bba1..137f33d301d19 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -35,7 +35,7 @@ use middle::lang_items::SizedTraitLangItem; use middle::region; use middle::resolve_lifetime; use middle::subst; -use middle::subst::{Substs}; +use middle::subst::{Substs, TypeSpace}; use middle::ty::{AsPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{self, RegionEscape, Ty, TypeScheme}; use middle::ty_fold::{self, TypeFolder, TypeFoldable}; @@ -47,6 +47,7 @@ use util::ppaux; use util::ppaux::{Repr,UserString}; use write_ty_to_tcx; +use std::collections::HashSet; use std::rc::Rc; use syntax::abi; @@ -644,6 +645,10 @@ fn convert(ccx: &CollectCtxt, it: &ast::Item) { Some(selfty), None); } + + enforce_impl_ty_params_are_constrained(ccx.tcx, + generics, + local_def(it.id)); }, ast::ItemTrait(_, _, _, ref trait_methods) => { let trait_def = trait_def_of_item(ccx, it); @@ -1605,3 +1610,96 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>( }) } } + +/// Checks that all the type parameters on an impl +fn enforce_impl_ty_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>, + ast_generics: &ast::Generics, + impl_def_id: ast::DefId) +{ + let impl_scheme = ty::lookup_item_type(tcx, impl_def_id); + let impl_trait_ref = ty::impl_trait_ref(tcx, impl_def_id); + + // The trait reference is an input, so find all type parameters + // reachable from there, to start (if this is an inherent impl, + // then just examine the self type). + let mut input_parameters: HashSet<_> = + impl_trait_ref.iter() + .flat_map(|t| t.input_types().iter()) // Types in trait ref, if any + .chain(Some(impl_scheme.ty).iter()) // Self type, always + .flat_map(|t| t.walk()) + .filter_map(to_opt_param_ty) + .collect(); + + loop { + let num_inputs = input_parameters.len(); + + let mut projection_predicates = + impl_scheme.generics.predicates + .iter() + .filter_map(|predicate| { + match *predicate { + // Ignore higher-ranked binders. For the purposes + // of this check, they don't matter because they + // only affect named regions, and we're just + // concerned about type parameters here. + ty::Predicate::Projection(ref data) => Some(data.0.clone()), + _ => None, + } + }); + + for projection in projection_predicates { + // Special case: watch out for some kind of sneaky attempt + // to project out an associated type defined by this very trait. + if Some(projection.projection_ty.trait_ref.clone()) == impl_trait_ref { + continue; + } + + let relies_only_on_inputs = + projection.projection_ty.trait_ref.input_types().iter() + .flat_map(|t| t.walk()) + .filter_map(to_opt_param_ty) + .all(|t| input_parameters.contains(&t)); + + if relies_only_on_inputs { + input_parameters.extend( + projection.ty.walk().filter_map(to_opt_param_ty)); + } + } + + if input_parameters.len() == num_inputs { + break; + } + } + + for (index, ty_param) in ast_generics.ty_params.iter().enumerate() { + let param_ty = ty::ParamTy { space: TypeSpace, + idx: index as u32, + name: ty_param.ident.name }; + if !input_parameters.contains(¶m_ty) { + if ty::has_attr(tcx, impl_def_id, "old_impl_check") { + tcx.sess.span_warn( + ty_param.span, + format!("the type parameter `{}` is not constrained by the \ + impl trait, self type, or predicates", + param_ty.user_string(tcx)).as_slice()); + } else { + tcx.sess.span_err( + ty_param.span, + format!("the type parameter `{}` is not constrained by the \ + impl trait, self type, or predicates", + param_ty.user_string(tcx)).as_slice()); + tcx.sess.span_help( + ty_param.span, + format!("you can temporarily opt out of this rule by placing \ + the `#[old_impl_check]` attribute on the impl").as_slice()); + } + } + } + + fn to_opt_param_ty<'tcx>(ty: Ty<'tcx>) -> Option { + match ty.sty { + ty::ty_param(ref d) => Some(d.clone()), + _ => None, + } + } +} diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs index 7ba329c518e91..d89a4754d2efe 100644 --- a/src/libserialize/collection_impls.rs +++ b/src/libserialize/collection_impls.rs @@ -156,6 +156,7 @@ impl< } } +#[old_impl_check] impl< K: Encodable + Hash + Eq, V: Encodable, @@ -175,6 +176,7 @@ impl< } } +#[old_impl_check] impl< K: Decodable + Hash + Eq, V: Decodable, @@ -195,6 +197,7 @@ impl< } } +#[old_impl_check] impl< T: Encodable + Hash + Eq, X, @@ -212,6 +215,7 @@ impl< } } +#[old_impl_check] impl< T: Decodable + Hash + Eq, S, diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index 8fe15f00ded73..ee94095bd158f 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -26,6 +26,7 @@ Core encoding and decoding interfaces. #![feature(macro_rules, default_type_params, phase, slicing_syntax, globs)] #![feature(unboxed_closures)] #![feature(associated_types)] +#![feature(old_impl_check)] // test harness access #[cfg(test)] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index a3fc38c34e84f..5372c51f95fff 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -439,6 +439,7 @@ impl SearchResult { } } +#[old_impl_check] impl, V, S, H: Hasher> HashMap { fn make_hash>(&self, x: &X) -> SafeHash { table::make_hash(&self.hasher, x) @@ -517,6 +518,7 @@ impl HashMap { } } +#[old_impl_check] impl, V, S, H: Hasher> HashMap { /// Creates an empty hashmap which will use the given hasher to hash keys. /// @@ -1191,6 +1193,7 @@ fn search_entry_hashed<'a, K, V, Q: ?Sized>(table: &'a mut RawTable, hash: } #[stable] +#[old_impl_check] impl, V: PartialEq, S, H: Hasher> PartialEq for HashMap { fn eq(&self, other: &HashMap) -> bool { if self.len() != other.len() { return false; } @@ -1202,9 +1205,11 @@ impl, V: PartialEq, S, H: Hasher> PartialEq for HashMap, V: Eq, S, H: Hasher> Eq for HashMap {} #[stable] +#[old_impl_check] impl + Show, V: Show, S, H: Hasher> Show for HashMap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "{{")); @@ -1219,6 +1224,7 @@ impl + Show, V: Show, S, H: Hasher> Show for HashMap } #[stable] +#[old_impl_check] impl, V, S, H: Hasher + Default> Default for HashMap { #[stable] fn default() -> HashMap { @@ -1227,6 +1233,7 @@ impl, V, S, H: Hasher + Default> Default for HashMap } #[stable] +#[old_impl_check] impl + Eq, Q: ?Sized, V, S, H: Hasher> Index for HashMap where Q: BorrowFrom + Hash + Eq { @@ -1239,6 +1246,7 @@ impl + Eq, Q: ?Sized, V, S, H: Hasher> Index for HashMap + Eq, Q: ?Sized, V, S, H: Hasher> IndexMut for HashMap where Q: BorrowFrom + Hash + Eq { @@ -1472,6 +1480,7 @@ impl<'a, Q: ?Sized + 'a + ToOwned, K: 'a, V: 'a> VacantEntry<'a, Q, K, V> { } #[stable] +#[old_impl_check] impl, V, S, H: Hasher + Default> FromIterator<(K, V)> for HashMap { fn from_iter>(iter: T) -> HashMap { let lower = iter.size_hint().0; @@ -1482,6 +1491,7 @@ impl, V, S, H: Hasher + Default> FromIterator<(K, V)> for Has } #[stable] +#[old_impl_check] impl, V, S, H: Hasher> Extend<(K, V)> for HashMap { fn extend>(&mut self, mut iter: T) { for (k, v) in iter { diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 211bfe2c10e8f..1b3d401fb8435 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -128,6 +128,7 @@ impl HashSet { } } +#[old_impl_check] impl, S, H: Hasher> HashSet { /// Creates a new empty hash set which will use the given hasher to hash /// keys. @@ -571,6 +572,7 @@ impl, S, H: Hasher> HashSet { } #[stable] +#[old_impl_check] impl, S, H: Hasher> PartialEq for HashSet { fn eq(&self, other: &HashSet) -> bool { if self.len() != other.len() { return false; } @@ -580,9 +582,11 @@ impl, S, H: Hasher> PartialEq for HashSet { } #[stable] +#[old_impl_check] impl, S, H: Hasher> Eq for HashSet {} #[stable] +#[old_impl_check] impl + fmt::Show, S, H: Hasher> fmt::Show for HashSet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "{{")); @@ -597,6 +601,7 @@ impl + fmt::Show, S, H: Hasher> fmt::Show for HashSet { } #[stable] +#[old_impl_check] impl, S, H: Hasher + Default> FromIterator for HashSet { fn from_iter>(iter: I) -> HashSet { let lower = iter.size_hint().0; @@ -607,6 +612,7 @@ impl, S, H: Hasher + Default> FromIterator for HashSet, S, H: Hasher> Extend for HashSet { fn extend>(&mut self, mut iter: I) { for k in iter { @@ -616,6 +622,7 @@ impl, S, H: Hasher> Extend for HashSet { } #[stable] +#[old_impl_check] impl, S, H: Hasher + Default> Default for HashSet { #[stable] fn default() -> HashSet { @@ -624,6 +631,7 @@ impl, S, H: Hasher + Default> Default for HashSet { } #[stable] +#[old_impl_check] impl<'a, 'b, T: Eq + Hash + Clone, S, H: Hasher + Default> BitOr<&'b HashSet> for &'a HashSet { type Output = HashSet; @@ -654,6 +662,7 @@ BitOr<&'b HashSet> for &'a HashSet { } #[stable] +#[old_impl_check] impl<'a, 'b, T: Eq + Hash + Clone, S, H: Hasher + Default> BitAnd<&'b HashSet> for &'a HashSet { type Output = HashSet; @@ -684,6 +693,7 @@ BitAnd<&'b HashSet> for &'a HashSet { } #[stable] +#[old_impl_check] impl<'a, 'b, T: Eq + Hash + Clone, S, H: Hasher + Default> BitXor<&'b HashSet> for &'a HashSet { type Output = HashSet; @@ -714,6 +724,7 @@ BitXor<&'b HashSet> for &'a HashSet { } #[stable] +#[old_impl_check] impl<'a, 'b, T: Eq + Hash + Clone, S, H: Hasher + Default> Sub<&'b HashSet> for &'a HashSet { type Output = HashSet; @@ -816,6 +827,7 @@ impl<'a, K: 'a> Iterator for Drain<'a, K> { } #[stable] +#[old_impl_check] impl<'a, T, S, H> Iterator for Intersection<'a, T, H> where T: Eq + Hash, H: Hasher { @@ -839,6 +851,7 @@ impl<'a, T, S, H> Iterator for Intersection<'a, T, H> } #[stable] +#[old_impl_check] impl<'a, T, S, H> Iterator for Difference<'a, T, H> where T: Eq + Hash, H: Hasher { @@ -862,6 +875,7 @@ impl<'a, T, S, H> Iterator for Difference<'a, T, H> } #[stable] +#[old_impl_check] impl<'a, T, S, H> Iterator for SymmetricDifference<'a, T, H> where T: Eq + Hash, H: Hasher { @@ -872,6 +886,7 @@ impl<'a, T, S, H> Iterator for SymmetricDifference<'a, T, H> } #[stable] +#[old_impl_check] impl<'a, T, S, H> Iterator for Union<'a, T, H> where T: Eq + Hash, H: Hasher { diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 5bef473db990c..61ed387dd07eb 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1604,6 +1604,7 @@ pub struct IncomingConnections<'a, A: ?Sized +'a> { inc: &'a mut A, } +#[old_impl_check] impl<'a, T, A: ?Sized + Acceptor> Iterator for IncomingConnections<'a, A> { type Item = IoResult; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b9f226c5aca73..592163e0e8c6b 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -108,6 +108,7 @@ #![feature(default_type_params, phase, lang_items, unsafe_destructor)] #![feature(slicing_syntax, unboxed_closures)] #![feature(old_orphan_check)] +#![feature(old_impl_check)] #![feature(associated_types)] // Don't link to std. We are std. diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7aa7c4fcfb301..0054cb9509ac6 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -982,8 +982,8 @@ pub enum Sign { Plus } -impl Sign where T: Int { - pub fn new(n: T) -> Sign { +impl Sign { + pub fn new(n: T) -> Sign { if n < Int::zero() { Minus } else { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0810d4ee93ac7..8132c9fe78b67 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -86,6 +86,9 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ // A way to temporarily opt out of the new orphan rules. This will *never* be accepted. ("old_orphan_check", Deprecated), + // A way to temporarily opt out of the new impl rules. This will *never* be accepted. + ("old_impl_check", Deprecated), + // OIBIT specific features ("optin_builtin_traits", Active), @@ -294,6 +297,13 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { i.span, "the new orphan check rules will eventually be strictly enforced"); } + + if attr::contains_name(i.attrs[], + "old_impl_check") { + self.gate_feature("old_impl_check", + i.span, + "`#[old_impl_check]` will be removed in the future"); + } } _ => {} diff --git a/src/test/auxiliary/nested_item.rs b/src/test/auxiliary/nested_item.rs index 1a2f429c9ebc1..21784bda27a8f 100644 --- a/src/test/auxiliary/nested_item.rs +++ b/src/test/auxiliary/nested_item.rs @@ -18,8 +18,8 @@ pub fn foo() -> int { // issue 8134 struct Foo; -impl Foo { - pub fn foo(&self) { +impl Foo { + pub fn foo(&self) { static X: uint = 1; } } @@ -33,8 +33,8 @@ impl> Parser { } struct Bar; -impl Foo { - pub fn bar(&self) { +impl Foo { + pub fn bar(&self) { static X: uint = 1; } } diff --git a/src/test/compile-fail/coherence-all-remote.rs b/src/test/compile-fail/coherence-all-remote.rs index d88b8751ea7b0..d86256a77765e 100644 --- a/src/test/compile-fail/coherence-all-remote.rs +++ b/src/test/compile-fail/coherence-all-remote.rs @@ -11,9 +11,9 @@ // aux-build:coherence-lib.rs extern crate "coherence-lib" as lib; -use lib::Remote; +use lib::Remote1; -impl Remote for int { } +impl Remote1 for int { } //~^ ERROR E0117 fn main() { } diff --git a/src/test/compile-fail/impl-unused-tps-inherent.rs b/src/test/compile-fail/impl-unused-tps-inherent.rs new file mode 100644 index 0000000000000..3803bb9b045ed --- /dev/null +++ b/src/test/compile-fail/impl-unused-tps-inherent.rs @@ -0,0 +1,35 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct MyType; + +struct MyType1(T); + +trait Bar { + type Out; +} + +impl MyType { + //~^ ERROR the type parameter `T` is not constrained +} + +impl MyType1 { + // OK, T is used in `Foo`. +} + +impl MyType1 { + //~^ ERROR the type parameter `U` is not constrained +} + +impl MyType1 where T: Bar { + // OK, T is used in `Foo`. +} + +fn main() { } diff --git a/src/test/compile-fail/impl-unused-tps.rs b/src/test/compile-fail/impl-unused-tps.rs new file mode 100644 index 0000000000000..99c6c6b89858c --- /dev/null +++ b/src/test/compile-fail/impl-unused-tps.rs @@ -0,0 +1,72 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn get(&self, A: &A) { } +} + +trait Bar { + type Out; +} + +impl Foo for [int;0] { + // OK, T is used in `Foo`. +} + +impl Foo for [int;1] { + //~^ ERROR the type parameter `U` is not constrained +} + +impl Foo for [int;2] where T : Bar { + // OK, `U` is now constrained by the output type parameter. +} + +impl,U> Foo for [int;3] { + // OK, same as above but written differently. +} + +impl Foo for U { + // OK, T, U are used everywhere. Note that the coherence check + // hasn't executed yet, so no errors about overlap. +} + +impl Bar for T { + //~^ ERROR the type parameter `U` is not constrained + + type Out = U; + + // Using `U` in an associated type within the impl is not good enough! +} + +impl Bar for T + where T : Bar +{ + //~^^^ ERROR the type parameter `U` is not constrained + + // This crafty self-referential attempt is still no good. +} + +impl Foo for T + where (T,U): Bar +{ + //~^^^ ERROR the type parameter `U` is not constrained + //~| ERROR the type parameter `V` is not constrained + + // Here, `V` is bound by an output type parameter, but the inputs + // are not themselves constrained. +} + +impl Foo<(T,U)> for T + where (T,U): Bar +{ + // As above, but both T and U ARE constrained. +} + +fn main() { } diff --git a/src/test/compile-fail/issue-12028.rs b/src/test/compile-fail/issue-12028.rs index 78502efdec520..24ffc5e9ee373 100644 --- a/src/test/compile-fail/issue-12028.rs +++ b/src/test/compile-fail/issue-12028.rs @@ -22,27 +22,28 @@ trait Stream { fn result(&self) -> u64; } -trait StreamHasher { - fn stream(&self) -> S; +trait StreamHasher { + type S : Stream; + fn stream(&self) -> Self::S; } ////////////////////////////////////////////////////////////////////////////// -trait StreamHash>: Hash { - fn input_stream(&self, stream: &mut S); +trait StreamHash: Hash { + fn input_stream(&self, stream: &mut H::S); } -impl> Hash for u8 { +impl Hash for u8 { fn hash2(&self, hasher: &H) -> u64 { let mut stream = hasher.stream(); self.input_stream(&mut stream); //~ ERROR type annotations required - stream.result() + Stream::result(&stream) } } -impl> StreamHash for u8 { - fn input_stream(&self, stream: &mut S) { - stream.input(&[*self]); +impl StreamHash for u8 { + fn input_stream(&self, stream: &mut H::S) { + Stream::input(&*stream, &[*self]); } } diff --git a/src/test/compile-fail/issue-13853-5.rs b/src/test/compile-fail/issue-13853-5.rs index b3a4f341f8448..6a017f7bb30c1 100644 --- a/src/test/compile-fail/issue-13853-5.rs +++ b/src/test/compile-fail/issue-13853-5.rs @@ -15,7 +15,7 @@ trait Deserializable { } impl<'a, T: Deserializable> Deserializable for &'a str { - //~^ ERROR unable to infer enough type information + //~^ ERROR type parameter `T` is not constrained fn deserialize_token>(_x: D, _y: &'a str) -> &'a str { } } diff --git a/src/test/compile-fail/issue-16562.rs b/src/test/compile-fail/issue-16562.rs index 3c784c3b770e4..626a442a2c355 100644 --- a/src/test/compile-fail/issue-16562.rs +++ b/src/test/compile-fail/issue-16562.rs @@ -18,7 +18,7 @@ struct Col { trait Collection { fn len(&self) -> uint; } impl Collection for Col { -//~^ ERROR unable to infer enough type information +//~^ ERROR type parameter `T` is not constrained fn len(&self) -> uint { unimplemented!() } diff --git a/src/test/run-pass/issue-3743.rs b/src/test/run-pass/issue-3743.rs index 43852fb332400..382ea0c575887 100644 --- a/src/test/run-pass/issue-3743.rs +++ b/src/test/run-pass/issue-3743.rs @@ -30,17 +30,23 @@ impl Vec2 { } // Right-hand-side operator visitor pattern -trait RhsOfVec2Mul { fn mul_vec2_by(&self, lhs: &Vec2) -> Result; } +trait RhsOfVec2Mul { + type Result; + + fn mul_vec2_by(&self, lhs: &Vec2) -> Self::Result; +} // Vec2's implementation of Mul "from the other side" using the above trait -impl> Mul for Vec2 { +impl> Mul for Vec2 { type Output = Res; fn mul(self, rhs: Rhs) -> Res { rhs.mul_vec2_by(&self) } } // Implementation of 'f64 as right-hand-side of Vec2::Mul' -impl RhsOfVec2Mul for f64 { +impl RhsOfVec2Mul for f64 { + type Result = Vec2; + fn mul_vec2_by(&self, lhs: &Vec2) -> Vec2 { lhs.vmul(*self) } }