From e772b42f36a495f253b829f959cff054bdb70b41 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Sun, 25 Oct 2020 13:43:33 -0500 Subject: [PATCH 1/3] Fix a few math tests and implement nextafter in rust --- Lib/test/test_math.py | 48 ++++++++++++++--------- common/src/float_ops.rs | 53 ++++++++++++++++++++----- vm/src/builtins/float.rs | 67 +++++++++++++++++++------------ vm/src/stdlib/math.rs | 85 +++++++++++++++++++--------------------- 4 files changed, 156 insertions(+), 97 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 7f744ba6b6..a401a4a872 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -260,7 +260,8 @@ def testAcos(self): self.assertRaises(ValueError, math.acos, -1 - eps) self.assertTrue(math.isnan(math.acos(NAN))) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testAcosh(self): self.assertRaises(TypeError, math.acosh) self.ftest('acosh(1)', math.acosh(1), 0) @@ -300,7 +301,7 @@ def testAtan(self): self.ftest('atan(-inf)', math.atan(NINF), -math.pi/2) self.assertTrue(math.isnan(math.atan(NAN))) - @unittest.skip('TODO: RUSTPYTHON') + @unittest.expectedFailure # TODO: RUSTPYTHON def testAtanh(self): self.assertRaises(TypeError, math.atan) self.ftest('atanh(0)', math.atanh(0), 0) @@ -373,7 +374,8 @@ def testAtan2(self): self.assertTrue(math.isnan(math.atan2(NAN, INF))) self.assertTrue(math.isnan(math.atan2(NAN, NAN))) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testCeil(self): self.assertRaises(TypeError, math.ceil) self.assertEqual(int, type(math.ceil(0.5))) @@ -473,7 +475,8 @@ def testDegrees(self): self.ftest('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0) self.ftest('degrees(0)', math.degrees(0), 0) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testExp(self): self.assertRaises(TypeError, math.exp) self.ftest('exp(-1)', math.exp(-1), 1/math.e) @@ -500,7 +503,8 @@ def testFactorial(self): self.assertRaises(ValueError, math.factorial, -1) self.assertRaises(ValueError, math.factorial, -10**100) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testFactorialNonIntegers(self): with self.assertWarns(DeprecationWarning): self.assertEqual(math.factorial(5.0), 120) @@ -523,7 +527,8 @@ def testFactorialHugeInputs(self): with self.assertWarns(DeprecationWarning): self.assertRaises(OverflowError, math.factorial, 1e100) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testFloor(self): self.assertRaises(TypeError, math.floor) self.assertEqual(int, type(math.floor(0.5))) @@ -737,7 +742,6 @@ def testGcd(self): self.assertRaises(TypeError, gcd, 120, 1, 84.0) #self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) # TODO: RUSTPYTHON - @unittest.skip('TODO: RUSTPYTHON float support') def testHypot(self): from decimal import Decimal from fractions import Fraction @@ -812,7 +816,8 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testDist(self): from decimal import Decimal as D from fractions import Fraction as F @@ -1024,7 +1029,8 @@ def testlcm(self): self.assertRaises(TypeError, lcm, 120, 0, 84.0) # self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) # TODO: RUSTPYTHON - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testLdexp(self): self.assertRaises(TypeError, math.ldexp) self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) @@ -1057,7 +1063,8 @@ def testLdexp(self): self.assertEqual(math.ldexp(NINF, n), NINF) self.assertTrue(math.isnan(math.ldexp(NAN, n))) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testLog(self): self.assertRaises(TypeError, math.log) self.ftest('log(1/e)', math.log(1/math.e), -1) @@ -1074,7 +1081,8 @@ def testLog(self): self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testLog1p(self): self.assertRaises(TypeError, math.log1p) for n in [2, 2**90, 2**300]: @@ -1142,7 +1150,8 @@ def testmodf(name, result, expected): self.assertTrue(math.isnan(modf_nan[0])) self.assertTrue(math.isnan(modf_nan[1])) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testPow(self): self.assertRaises(TypeError, math.pow) self.ftest('pow(0,1)', math.pow(0,1), 0) @@ -1564,7 +1573,8 @@ def testIsinf(self): # still fails this part of the test on some platforms. For now, we only # *run* test_exceptions() in verbose mode, so that this isn't normally # tested. - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure @unittest.skipUnless(verbose, 'requires verbose mode') def test_exceptions(self): try: @@ -1723,7 +1733,8 @@ def test_exceptions(self): # self.fail('Failures in test_mtestfile:\n ' + # '\n '.join(failures)) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_prod(self): prod = math.prod self.assertEqual(prod([]), 1) @@ -1810,7 +1821,8 @@ def _naive_prod(iterable, start=1): self.assertEqual(type(prod([1, decimal.Decimal(2.0), 3, 4, 5, 6])), decimal.Decimal) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testPerm(self): perm = math.perm factorial = math.factorial @@ -1875,7 +1887,8 @@ def testPerm(self): self.assertIs(type(perm(IntSubclass(5), IntSubclass(k))), int) self.assertIs(type(perm(MyIndexable(5), MyIndexable(k))), int) - @unittest.skip('TODO: RUSTPYTHON') + # TODO: RUSTPYTHON + @unittest.expectedFailure def testComb(self): comb = math.comb factorial = math.factorial @@ -2015,7 +2028,6 @@ def test_ulp(self): with self.subTest(x=x): self.assertEqual(math.ulp(-x), math.ulp(x)) - @unittest.skip('TODO: RUSTPYTHON') def test_issue39871(self): # A SystemError should not be raised if the first arg to atan2(), # copysign(), or remainder() cannot be converted to a float. @@ -2146,7 +2158,6 @@ def test_integers(self): self.assertAllClose(integer_examples, rel_tol=1e-8) self.assertAllNotClose(integer_examples, rel_tol=1e-9) - @unittest.skip('TODO: RUSTPYTHON') def test_decimals(self): # test with Decimal values from decimal import Decimal @@ -2158,7 +2169,6 @@ def test_decimals(self): self.assertAllClose(decimal_examples, rel_tol=1e-8) self.assertAllNotClose(decimal_examples, rel_tol=1e-9) - @unittest.skip('TODO: RUSTPYTHON') def test_fractions(self): # test with Fraction values from fractions import Fraction diff --git a/common/src/float_ops.rs b/common/src/float_ops.rs index e27ac71180..c820adb51f 100644 --- a/common/src/float_ops.rs +++ b/common/src/float_ops.rs @@ -108,15 +108,9 @@ pub fn from_hex(s: &str) -> Option { return Some(f); } match s.to_ascii_lowercase().as_str() { - "nan" => Some(f64::NAN), - "+nan" => Some(f64::NAN), - "-nan" => Some(f64::NAN), - "inf" => Some(f64::INFINITY), - "infinity" => Some(f64::INFINITY), - "+inf" => Some(f64::INFINITY), - "+infinity" => Some(f64::INFINITY), - "-inf" => Some(f64::NEG_INFINITY), - "-infinity" => Some(f64::NEG_INFINITY), + "nan" | "+nan" | "-nan" => Some(f64::NAN), + "inf" | "infinity" | "+inf" | "+infinity" => Some(f64::INFINITY), + "-inf" | "-infinity" => Some(f64::NEG_INFINITY), value => { let mut hex = String::with_capacity(value.len()); let has_0x = value.contains("0x"); @@ -216,6 +210,47 @@ pub fn divmod(v1: f64, v2: f64) -> Option<(f64, f64)> { } } +// nextafter algorithm based off of https://gitlab.com/bronsonbdevost/next_afterf +pub fn nextafter(x: f64, y: f64) -> f64 { + if x == y { + y + } else if x.is_nan() || y.is_nan() { + f64::NAN + } else if x >= f64::INFINITY { + f64::MAX + } else if x <= f64::NEG_INFINITY { + f64::MIN + } else if x == 0.0 { + f64::from_bits(1).copysign(y) + } else { + // next x after 0 if y is farther from 0 than x, otherwise next towards 0 + // the sign is a separate bit in floats, so bits+1 moves away from 0 no matter the float + let b = x.to_bits(); + let bits = if (y > x) == (x > 0.0) { b + 1 } else { b - 1 }; + let ret = f64::from_bits(bits); + if ret == 0.0 { + ret.copysign(x) + } else { + ret + } + } +} + +pub fn ulp(x: f64) -> f64 { + if x.is_nan() { + return x; + } + let x = x.abs(); + let x2 = nextafter(x, f64::INFINITY); + if x2.is_infinite() { + // special case: x is the largest positive representable float + let x2 = nextafter(x, f64::NEG_INFINITY); + x - x2 + } else { + x2 - x + } +} + #[test] fn test_to_hex() { use rand::Rng; diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 88e0d268b3..519a6352bf 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -142,10 +142,30 @@ impl PyFloat { vm: &VirtualMachine, ) -> PyResult> { let float_val = match arg { - OptionalArg::Present(val) => to_float(vm, &val), - OptionalArg::Missing => Ok(0f64), + OptionalArg::Present(val) => { + if let Some(f) = to_float(vm, &val)? { + f + } else if let Some(s) = val.payload_if_subclass::(vm) { + float_ops::parse_str(s.borrow_value().trim()).ok_or_else(|| { + vm.new_value_error(format!("could not convert string to float: '{}'", s)) + })? + } else if let Some(bytes) = val.payload_if_subclass::(vm) { + lexical_core::parse(bytes.borrow_value()).map_err(|_| { + vm.new_value_error(format!( + "could not convert string to float: '{}'", + bytes.repr() + )) + })? + } else { + return Err(vm.new_type_error(format!( + "float() argument must be a string or a number, not '{}'", + val.class().name + ))); + } + } + OptionalArg::Missing => 0.0, }; - PyFloat::from(float_val?).into_ref_with_type(vm, cls) + PyFloat::from(float_val).into_ref_with_type(vm, cls) } #[pymethod(name = "__format__")] @@ -491,33 +511,20 @@ impl Hashable for PyFloat { } } -fn to_float(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { +fn to_float(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult> { let value = if let Some(float) = obj.payload_if_subclass::(vm) { float.value } else if let Some(int) = obj.payload_if_subclass::(vm) { int::try_float(int.borrow_value(), vm)? - } else if let Some(s) = obj.payload_if_subclass::(vm) { - float_ops::parse_str(s.borrow_value().trim()).ok_or_else(|| { - vm.new_value_error(format!("could not convert string to float: '{}'", s)) - })? - } else if let Some(bytes) = obj.payload_if_subclass::(vm) { - lexical_core::parse(bytes.borrow_value()).map_err(|_| { - vm.new_value_error(format!( - "could not convert string to float: '{}'", - bytes.repr() - )) - })? } else { - let method = vm.get_method_or_type_error(obj.clone(), "__float__", || { - format!( - "float() argument must be a string or a number, not '{}'", - obj.class().name - ) - })?; + let method = match vm.get_method(obj.clone(), "__float__") { + Some(x) => x?, + None => return Ok(None), + }; let result = vm.invoke(&method, ())?; PyFloatRef::try_from_object(vm, result)?.to_f64() }; - Ok(value) + Ok(Some(value)) } pub type PyFloatRef = PyRef; @@ -528,6 +535,7 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { } #[derive(Debug, Copy, Clone, PartialEq)] +#[repr(transparent)] pub struct IntoPyFloat { value: f64, } @@ -536,13 +544,22 @@ impl IntoPyFloat { pub fn to_f64(self) -> f64 { self.value } + + pub fn vec_into_f64(v: Vec) -> Vec { + // TODO: Vec::into_raw_parts once stabilized + let mut m = std::mem::ManuallyDrop::new(v); + let (p, l, c) = (m.as_mut_ptr(), m.len(), m.capacity()); + // SAFETY: IntoPyFloat is repr(transparent) over f64 + unsafe { Vec::from_raw_parts(p.cast(), l, c) } + } } impl TryFromObject for IntoPyFloat { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - Ok(IntoPyFloat { - value: to_float(vm, &obj)?, - }) + let value = to_float(vm, &obj)?.ok_or_else(|| { + vm.new_type_error(format!("must be real number, not {}", obj.class().name)) + })?; + Ok(IntoPyFloat { value }) } } diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index e0ef3a14ed..ca383666fc 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -15,9 +15,6 @@ use crate::pyobject::{BorrowValue, Either, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; use rustpython_common::float_ops; -#[cfg(not(target_arch = "wasm32"))] -use libc::c_double; - use std::cmp::Ordering; // Helper macro: @@ -177,8 +174,43 @@ fn math_atan2(y: IntoPyFloat, x: IntoPyFloat) -> f64 { make_math_func!(math_cos, cos); -fn math_hypot(x: IntoPyFloat, y: IntoPyFloat) -> f64 { - x.to_f64().hypot(y.to_f64()) +fn math_hypot(coordinates: Args) -> f64 { + let mut coordinates = IntoPyFloat::vec_into_f64(coordinates.into_vec()); + let mut max = 0.0; + let mut has_nan = false; + for f in &mut coordinates { + *f = f.abs(); + if f.is_nan() { + has_nan = true; + } else if *f > max { + max = *f + } + } + // inf takes precedence over nan + if max.is_infinite() { + return max; + } + if has_nan { + return f64::NAN; + } + vector_norm(&coordinates, max) +} + +fn vector_norm(v: &[f64], max: f64) -> f64 { + if max == 0.0 || v.len() <= 1 { + return max; + } + let mut csum = 1.0; + let mut frac = 0.0; + for &f in v { + let f = f / max; + let f = f * f; + let old = csum; + csum += f; + // this seemingly redundant operation is to reduce float rounding errors/inaccuracy + frac += (old - csum) + f; + } + max * f64::sqrt(csum - 1.0 + frac) } make_math_func!(math_sin, sin); @@ -363,47 +395,12 @@ fn math_modf(x: IntoPyFloat) -> (f64, f64) { (x.fract(), x.trunc()) } -#[inline] -#[cfg(not(target_arch = "wasm32"))] -fn libc_nextafter(x: f64, y: f64) -> f64 { - extern "C" { - fn nextafter(x: c_double, y: c_double) -> c_double; - } - unsafe { nextafter(x, y) } -} - -#[cfg(not(target_arch = "wasm32"))] -fn math_nextafter(x: IntoPyFloat, y: IntoPyFloat) -> PyResult { - let x = x.to_f64(); - let y = y.to_f64(); - Ok(libc_nextafter(x, y)) -} - -#[cfg(target_arch = "wasm32")] -fn math_nextafter(_x: IntoPyFloat, _y: IntoPyFloat, vm: &VirtualMachine) -> PyResult { - Err(vm.new_not_implemented_error("not implemented for this platform".to_owned())) -} - -#[cfg(not(target_arch = "wasm32"))] -fn math_ulp(x: IntoPyFloat) -> PyResult { - let mut x = x.to_f64(); - if x.is_nan() { - return Ok(x); - } - x = x.abs(); - let mut x2 = libc_nextafter(x, f64::INFINITY); - Ok(if x2.is_infinite() { - // special case: x is the largest positive representable float - x2 = libc_nextafter(x, f64::NEG_INFINITY); - x - x2 - } else { - x2 - x - }) +fn math_nextafter(x: IntoPyFloat, y: IntoPyFloat) -> f64 { + float_ops::nextafter(x.to_f64(), y.to_f64()) } -#[cfg(target_arch = "wasm32")] -fn math_ulp(_x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { - Err(vm.new_not_implemented_error("not implemented for this platform".to_owned())) +fn math_ulp(x: IntoPyFloat) -> f64 { + float_ops::ulp(x.to_f64()) } fn fmod(x: f64, y: f64) -> f64 { From 72f8f20ab257dba1e88c8ebe59bb7379d4899201 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Sun, 25 Oct 2020 15:28:32 -0500 Subject: [PATCH 2/3] Move timing_safe_cmp to rustpython-common --- Cargo.lock | 2 +- common/Cargo.toml | 1 + common/src/cmp.rs | 52 ++++++++++++++++++++++++++++++++++++ common/src/lib.rs | 1 + vm/Cargo.toml | 1 - vm/src/stdlib/operator.rs | 55 +++------------------------------------ 6 files changed, 58 insertions(+), 54 deletions(-) create mode 100644 common/src/cmp.rs diff --git a/Cargo.lock b/Cargo.lock index 0da0a83d4d..e1389c688d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,6 +1651,7 @@ dependencies = [ "parking_lot", "rand", "siphasher", + "volatile", ] [[package]] @@ -1807,7 +1808,6 @@ dependencies = [ "unicode-casing", "unicode_names2", "utime", - "volatile", "wasm-bindgen", "winapi", "winreg", diff --git a/common/Cargo.toml b/common/Cargo.toml index fbf962a53a..e378480bac 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -20,3 +20,4 @@ once_cell = "1.4.1" siphasher = "0.3" rand = "0.7.3" derive_more = "0.99.9" +volatile = "0.3" diff --git a/common/src/cmp.rs b/common/src/cmp.rs new file mode 100644 index 0000000000..c148305cad --- /dev/null +++ b/common/src/cmp.rs @@ -0,0 +1,52 @@ +use volatile::Volatile; + +/// Compare 2 byte slices in a way that ensures that the timing of the operation can't be used to +/// glean any information about the data. +#[inline(never)] +#[cold] +pub fn timing_safe_cmp(a: &[u8], b: &[u8]) -> bool { + // we use raw pointers here to keep faithful to the C implementation and + // to try to avoid any optimizations rustc might do with slices + let len_a = a.len(); + let a = a.as_ptr(); + let len_b = b.len(); + let b = b.as_ptr(); + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + let length: Volatile; + let mut left: Volatile<*const u8>; + let mut right: Volatile<*const u8>; + let mut result: u8 = 0; + + /* loop count depends on length of b */ + length = Volatile::new(len_b); + left = Volatile::new(std::ptr::null()); + right = Volatile::new(b); + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if len_a == length.read() { + left.write(Volatile::new(a).read()); + result = 0; + } + if len_a != length.read() { + left.write(b); + result = 1; + } + + for _ in 0..length.read() { + let l = left.read(); + left.write(l.wrapping_add(1)); + let r = right.read(); + right.write(r.wrapping_add(1)); + // safety: the 0..length range will always be either: + // * as long as the length of both a and b, if len_a and len_b are equal + // * as long as b, and both `left` and `right` are b + result |= unsafe { l.read_volatile() ^ r.read_volatile() }; + } + + result == 0 +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 34c0116347..bf695b24fc 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,6 +1,7 @@ //! A crate to hold types and functions common to all rustpython components. pub mod borrow; +pub mod cmp; pub mod float_ops; pub mod hash; pub mod lock; diff --git a/vm/Cargo.toml b/vm/Cargo.toml index fa064f2984..43f488605e 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -27,7 +27,6 @@ sha-1 = "0.8" sha2 = "0.8" sha3 = "0.8" blake2 = "0.8" -volatile = "0.3" num-complex = { version = "0.3", features = ["serde"] } num-bigint = { version = "0.3", features = ["serde"] } diff --git a/vm/src/stdlib/operator.rs b/vm/src/stdlib/operator.rs index 8db93786b1..71150963d5 100644 --- a/vm/src/stdlib/operator.rs +++ b/vm/src/stdlib/operator.rs @@ -1,10 +1,10 @@ use crate::builtins::pystr::PyStrRef; use crate::byteslike::PyBytesLike; +use crate::common::cmp; use crate::function::OptionalArg; use crate::iterator; use crate::pyobject::{BorrowValue, Either, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; -use volatile::Volatile; fn _operator_length_hint(obj: PyObjectRef, default: OptionalArg, vm: &VirtualMachine) -> PyResult { let default = default.unwrap_or_else(|| vm.ctx.new_int(0)); @@ -20,55 +20,6 @@ fn _operator_length_hint(obj: PyObjectRef, default: OptionalArg, vm: &VirtualMac Ok(hint) } -#[inline(never)] -#[cold] -fn timing_safe_cmp(a: &[u8], b: &[u8]) -> bool { - // we use raw pointers here to keep faithful to the C implementation and - // to try to avoid any optimizations rustc might do with slices - let len_a = a.len(); - let a = a.as_ptr(); - let len_b = b.len(); - let b = b.as_ptr(); - /* The volatile type declarations make sure that the compiler has no - * chance to optimize and fold the code in any way that may change - * the timing. - */ - let length: Volatile; - let mut left: Volatile<*const u8>; - let mut right: Volatile<*const u8>; - let mut result: u8 = 0; - - /* loop count depends on length of b */ - length = Volatile::new(len_b); - left = Volatile::new(std::ptr::null()); - right = Volatile::new(b); - - /* don't use else here to keep the amount of CPU instructions constant, - * volatile forces re-evaluation - * */ - if len_a == length.read() { - left.write(Volatile::new(a).read()); - result = 0; - } - if len_a != length.read() { - left.write(b); - result = 1; - } - - for _ in 0..length.read() { - let l = left.read(); - left.write(l.wrapping_add(1)); - let r = right.read(); - right.write(r.wrapping_add(1)); - // safety: the 0..length range will always be either: - // * as long as the length of both a and b, if len_a and len_b are equal - // * as long as b, and both `left` and `right` are b - result |= unsafe { l.read_volatile() ^ r.read_volatile() }; - } - - result == 0 -} - fn _operator_compare_digest( a: Either, b: Either, @@ -81,9 +32,9 @@ fn _operator_compare_digest( "comparing strings with non-ASCII characters is not supported".to_owned(), )); } - timing_safe_cmp(a.borrow_value().as_bytes(), b.borrow_value().as_bytes()) + cmp::timing_safe_cmp(a.borrow_value().as_bytes(), b.borrow_value().as_bytes()) } - (Either::B(a), Either::B(b)) => a.with_ref(|a| b.with_ref(|b| timing_safe_cmp(a, b))), + (Either::B(a), Either::B(b)) => a.with_ref(|a| b.with_ref(|b| cmp::timing_safe_cmp(a, b))), _ => { return Err(vm .new_type_error("unsupported operand types(s) or combination of types".to_owned())) From 4e05d7d3e6ed9e16d03f49e565c27be03f695301 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 26 Oct 2020 00:05:56 -0500 Subject: [PATCH 3/3] Suppress clippy --- common/src/float_ops.rs | 1 + vm/src/builtins/float.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/float_ops.rs b/common/src/float_ops.rs index c820adb51f..f4089e0647 100644 --- a/common/src/float_ops.rs +++ b/common/src/float_ops.rs @@ -211,6 +211,7 @@ pub fn divmod(v1: f64, v2: f64) -> Option<(f64, f64)> { } // nextafter algorithm based off of https://gitlab.com/bronsonbdevost/next_afterf +#[allow(clippy::float_cmp)] pub fn nextafter(x: f64, y: f64) -> f64 { if x == y { y diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 519a6352bf..97689974df 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -547,8 +547,8 @@ impl IntoPyFloat { pub fn vec_into_f64(v: Vec) -> Vec { // TODO: Vec::into_raw_parts once stabilized - let mut m = std::mem::ManuallyDrop::new(v); - let (p, l, c) = (m.as_mut_ptr(), m.len(), m.capacity()); + let mut v = std::mem::ManuallyDrop::new(v); + let (p, l, c) = (v.as_mut_ptr(), v.len(), v.capacity()); // SAFETY: IntoPyFloat is repr(transparent) over f64 unsafe { Vec::from_raw_parts(p.cast(), l, c) } }