rollup merge of #20593: nikomatsakis/unused-tps-in-impl

Conflicts:
	src/libcollections/lib.rs
	src/librustc/lib.rs
	src/libserialize/lib.rs
	src/libstd/lib.rs
This commit is contained in:
Alex Crichton 2015-01-06 15:31:39 -08:00
commit 3892dd1eaa
22 changed files with 283 additions and 24 deletions

View File

@ -1417,7 +1417,7 @@ pub type MutTraversal<'a, K, V> = AbsTraversal<ElemsAndEdges<Zip<slice::Iter<'a,
/// An owning traversal over a node's entries and edges
pub type MoveTraversal<K, V> = AbsTraversal<MoveTraversalImpl<K, V>>;
#[old_impl_check]
impl<K, V, E, Impl: TraversalImpl<K, V, E>> Iterator for AbsTraversal<Impl> {
type Item = TraversalItem<K, V, E>;
@ -1433,6 +1433,7 @@ impl<K, V, E, Impl: TraversalImpl<K, V, E>> Iterator for AbsTraversal<Impl> {
}
}
#[old_impl_check]
impl<K, V, E, Impl: TraversalImpl<K, V, E>> DoubleEndedIterator for AbsTraversal<Impl> {
fn next_back(&mut self) -> Option<TraversalItem<K, V, E>> {
let tail_is_edge = self.tail_is_edge;

View File

@ -22,6 +22,7 @@
html_playground_url = "http://play.rust-lang.org/")]
#![feature(unsafe_destructor, slicing_syntax)]
#![feature(old_impl_check)]
#![no_std]
#[macro_use]

View File

@ -25,6 +25,7 @@
#![feature(quote)]
#![feature(slicing_syntax, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(old_impl_check)]
extern crate arena;
extern crate flate;

View File

@ -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] = &[

View File

@ -1350,6 +1350,7 @@ impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for ty::Binder<T> {
}
}
#[old_impl_check]
impl<'tcx, S, H, K, V> Repr<'tcx> for HashMap<K,V,H>
where K : Hash<S> + Eq + Repr<'tcx>,
V : Repr<'tcx>,

View File

@ -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(&param_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<ty::ParamTy> {
match ty.sty {
ty::ty_param(ref d) => Some(d.clone()),
_ => None,
}
}
}

View File

@ -156,6 +156,7 @@ impl<
}
}
#[old_impl_check]
impl<
K: Encodable + Hash<X> + Eq,
V: Encodable,
@ -175,6 +176,7 @@ impl<
}
}
#[old_impl_check]
impl<
K: Decodable + Hash<S> + Eq,
V: Decodable,
@ -195,6 +197,7 @@ impl<
}
}
#[old_impl_check]
impl<
T: Encodable + Hash<X> + Eq,
X,
@ -212,6 +215,7 @@ impl<
}
}
#[old_impl_check]
impl<
T: Decodable + Hash<S> + Eq,
S,

View File

@ -24,6 +24,7 @@ Core encoding and decoding interfaces.
html_playground_url = "http://play.rust-lang.org/")]
#![allow(unknown_features)]
#![feature(slicing_syntax)]
#![feature(old_impl_check)]
// test harness access
#[cfg(test)] extern crate test;

View File

@ -439,6 +439,7 @@ impl<K, V, M> SearchResult<K, V, M> {
}
}
#[old_impl_check]
impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
fn make_hash<X: ?Sized + Hash<S>>(&self, x: &X) -> SafeHash {
table::make_hash(&self.hasher, x)
@ -517,6 +518,7 @@ impl<K: Hash + Eq, V> HashMap<K, V, RandomSipHasher> {
}
}
#[old_impl_check]
impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
/// Creates an empty hashmap which will use the given hasher to hash keys.
///
@ -1188,6 +1190,7 @@ fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHas
}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S>, V: PartialEq, S, H: Hasher<S>> PartialEq for HashMap<K, V, H> {
fn eq(&self, other: &HashMap<K, V, H>) -> bool {
if self.len() != other.len() { return false; }
@ -1199,9 +1202,11 @@ impl<K: Eq + Hash<S>, V: PartialEq, S, H: Hasher<S>> PartialEq for HashMap<K, V,
}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S>, V: Eq, S, H: Hasher<S>> Eq for HashMap<K, V, H> {}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S> + Show, V: Show, S, H: Hasher<S>> Show for HashMap<K, V, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "HashMap {{"));
@ -1216,6 +1221,7 @@ impl<K: Eq + Hash<S> + Show, V: Show, S, H: Hasher<S>> Show for HashMap<K, V, H>
}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> Default for HashMap<K, V, H> {
#[stable]
fn default() -> HashMap<K, V, H> {
@ -1224,6 +1230,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> Default for HashMap<K, V, H>
}
#[stable]
#[old_impl_check]
impl<K: Hash<S> + Eq, Q: ?Sized, V, S, H: Hasher<S>> Index<Q> for HashMap<K, V, H>
where Q: BorrowFrom<K> + Hash<S> + Eq
{
@ -1236,6 +1243,7 @@ impl<K: Hash<S> + Eq, Q: ?Sized, V, S, H: Hasher<S>> Index<Q> for HashMap<K, V,
}
#[stable]
#[old_impl_check]
impl<K: Hash<S> + Eq, Q: ?Sized, V, S, H: Hasher<S>> IndexMut<Q> for HashMap<K, V, H>
where Q: BorrowFrom<K> + Hash<S> + Eq
{
@ -1465,6 +1473,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> FromIterator<(K, V)> for HashMap<K, V, H> {
fn from_iter<T: Iterator<Item=(K, V)>>(iter: T) -> HashMap<K, V, H> {
let lower = iter.size_hint().0;
@ -1475,6 +1484,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> FromIterator<(K, V)> for Has
}
#[stable]
#[old_impl_check]
impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> Extend<(K, V)> for HashMap<K, V, H> {
fn extend<T: Iterator<Item=(K, V)>>(&mut self, mut iter: T) {
for (k, v) in iter {

View File

@ -125,6 +125,7 @@ impl<T: Hash + Eq> HashSet<T, RandomSipHasher> {
}
}
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> {
/// Creates a new empty hash set which will use the given hasher to hash
/// keys.
@ -568,6 +569,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S>> PartialEq for HashSet<T, H> {
fn eq(&self, other: &HashSet<T, H>) -> bool {
if self.len() != other.len() { return false; }
@ -577,9 +579,11 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> PartialEq for HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S>> Eq for HashSet<T, H> {}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S> + fmt::Show, S, H: Hasher<S>> fmt::Show for HashSet<T, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "HashSet {{"));
@ -594,6 +598,7 @@ impl<T: Eq + Hash<S> + fmt::Show, S, H: Hasher<S>> fmt::Show for HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> FromIterator<T> for HashSet<T, H> {
fn from_iter<I: Iterator<Item=T>>(iter: I) -> HashSet<T, H> {
let lower = iter.size_hint().0;
@ -604,6 +609,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> FromIterator<T> for HashSet<T,
}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S>> Extend<T> for HashSet<T, H> {
fn extend<I: Iterator<Item=T>>(&mut self, mut iter: I) {
for k in iter {
@ -613,6 +619,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> Extend<T> for HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> Default for HashSet<T, H> {
#[stable]
fn default() -> HashSet<T, H> {
@ -621,6 +628,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> Default for HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default>
BitOr<&'b HashSet<T, H>> for &'a HashSet<T, H> {
type Output = HashSet<T, H>;
@ -651,6 +659,7 @@ BitOr<&'b HashSet<T, H>> for &'a HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default>
BitAnd<&'b HashSet<T, H>> for &'a HashSet<T, H> {
type Output = HashSet<T, H>;
@ -681,6 +690,7 @@ BitAnd<&'b HashSet<T, H>> for &'a HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default>
BitXor<&'b HashSet<T, H>> for &'a HashSet<T, H> {
type Output = HashSet<T, H>;
@ -711,6 +721,7 @@ BitXor<&'b HashSet<T, H>> for &'a HashSet<T, H> {
}
#[stable]
#[old_impl_check]
impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default>
Sub<&'b HashSet<T, H>> for &'a HashSet<T, H> {
type Output = HashSet<T, H>;
@ -813,6 +824,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<S>, H: Hasher<S>
{
@ -836,6 +848,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<S>, H: Hasher<S>
{
@ -859,6 +872,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<S>, H: Hasher<S>
{
@ -869,6 +883,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<S>, H: Hasher<S>
{

View File

@ -1603,6 +1603,7 @@ pub struct IncomingConnections<'a, A: ?Sized +'a> {
inc: &'a mut A,
}
#[old_impl_check]
impl<'a, T, A: ?Sized + Acceptor<T>> Iterator for IncomingConnections<'a, A> {
type Item = IoResult<T>;

View File

@ -107,6 +107,7 @@
#![feature(linkage, thread_local, asm)]
#![feature(phase, lang_items, unsafe_destructor)]
#![feature(slicing_syntax, unboxed_closures)]
#![feature(old_impl_check)]
// Don't link to std. We are std.
#![no_std]

View File

@ -979,8 +979,8 @@ pub enum Sign {
Plus
}
impl<T> Sign where T: Int {
pub fn new(n: T) -> Sign {
impl Sign {
pub fn new<T:Int>(n: T) -> Sign {
if n < Int::zero() {
Minus
} else {

View File

@ -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");
}
}
_ => {}

View File

@ -18,8 +18,8 @@ pub fn foo<T>() -> int {
// issue 8134
struct Foo;
impl<T> Foo {
pub fn foo(&self) {
impl Foo {
pub fn foo<T>(&self) {
static X: uint = 1;
}
}
@ -33,8 +33,8 @@ impl<T: std::iter::Iterator<Item=char>> Parser<T> {
}
struct Bar;
impl<T> Foo {
pub fn bar(&self) {
impl Foo {
pub fn bar<T>(&self) {
static X: uint = 1;
}
}

View File

@ -11,9 +11,9 @@
// aux-build:coherence-lib.rs
extern crate "coherence-lib" as lib;
use lib::Remote;
use lib::Remote1;
impl<T> Remote for int { }
impl<T> Remote1<T> for int { }
//~^ ERROR E0117
fn main() { }

View File

@ -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 <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.
struct MyType;
struct MyType1<T>(T);
trait Bar {
type Out;
}
impl<T> MyType {
//~^ ERROR the type parameter `T` is not constrained
}
impl<T> MyType1<T> {
// OK, T is used in `Foo<T>`.
}
impl<T,U> MyType1<T> {
//~^ ERROR the type parameter `U` is not constrained
}
impl<T,U> MyType1<T> where T: Bar<Out=U> {
// OK, T is used in `Foo<T>`.
}
fn main() { }

View File

@ -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 <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.
trait Foo<A> {
fn get(&self, A: &A) { }
}
trait Bar {
type Out;
}
impl<T> Foo<T> for [int;0] {
// OK, T is used in `Foo<T>`.
}
impl<T,U> Foo<T> for [int;1] {
//~^ ERROR the type parameter `U` is not constrained
}
impl<T,U> Foo<T> for [int;2] where T : Bar<Out=U> {
// OK, `U` is now constrained by the output type parameter.
}
impl<T:Bar<Out=U>,U> Foo<T> for [int;3] {
// OK, same as above but written differently.
}
impl<T,U> Foo<T> for U {
// OK, T, U are used everywhere. Note that the coherence check
// hasn't executed yet, so no errors about overlap.
}
impl<T,U> 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<T,U> Bar for T
where T : Bar<Out=U>
{
//~^^^ ERROR the type parameter `U` is not constrained
// This crafty self-referential attempt is still no good.
}
impl<T,U,V> Foo<T> for T
where (T,U): Bar<Out=V>
{
//~^^^ 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<T,U,V> Foo<(T,U)> for T
where (T,U): Bar<Out=V>
{
// As above, but both T and U ARE constrained.
}
fn main() { }

View File

@ -22,27 +22,28 @@ trait Stream {
fn result(&self) -> u64;
}
trait StreamHasher<S: Stream> {
fn stream(&self) -> S;
trait StreamHasher {
type S : Stream;
fn stream(&self) -> Self::S;
}
//////////////////////////////////////////////////////////////////////////////
trait StreamHash<S: Stream, H: StreamHasher<S>>: Hash<H> {
fn input_stream(&self, stream: &mut S);
trait StreamHash<H: StreamHasher>: Hash<H> {
fn input_stream(&self, stream: &mut H::S);
}
impl<S: Stream, H: StreamHasher<S>> Hash<H> for u8 {
impl<H: StreamHasher> Hash<H> 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<S: Stream, H: StreamHasher<S>> StreamHash<S, H> for u8 {
fn input_stream(&self, stream: &mut S) {
stream.input(&[*self]);
impl<H: StreamHasher> StreamHash<H> for u8 {
fn input_stream(&self, stream: &mut H::S) {
Stream::input(&*stream, &[*self]);
}
}

View File

@ -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<D: Deserializer<'a>>(_x: D, _y: &'a str) -> &'a str {
}
}

View File

@ -18,7 +18,7 @@ struct Col<D, C> {
trait Collection { fn len(&self) -> uint; }
impl<T, M: MatrixShape> Collection for Col<M, uint> {
//~^ ERROR unable to infer enough type information
//~^ ERROR type parameter `T` is not constrained
fn len(&self) -> uint {
unimplemented!()
}

View File

@ -30,17 +30,23 @@ impl Vec2 {
}
// Right-hand-side operator visitor pattern
trait RhsOfVec2Mul<Result> { 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<Res, Rhs: RhsOfVec2Mul<Res>> Mul<Rhs> for Vec2 {
impl<Res, Rhs: RhsOfVec2Mul<Result=Res>> Mul<Rhs> 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<Vec2> for f64 {
impl RhsOfVec2Mul for f64 {
type Result = Vec2;
fn mul_vec2_by(&self, lhs: &Vec2) -> Vec2 { lhs.vmul(*self) }
}