From 856f77fb77ec97fb2b2cd442e70e67fa0a061dc2 Mon Sep 17 00:00:00 2001 From: jfh Date: Thu, 7 Oct 2021 12:34:30 +0300 Subject: [PATCH] Add ArgFloatLike. --- stdlib/src/array.rs | 12 +++-- stdlib/src/cmath.rs | 9 ++-- stdlib/src/math.rs | 102 ++++++++++++++++++------------------- vm/src/builtins/float.rs | 27 ---------- vm/src/builtins/mod.rs | 2 +- vm/src/cformat.rs | 7 +-- vm/src/function.rs | 2 +- vm/src/function/numlike.rs | 29 +++++++++++ 8 files changed, 97 insertions(+), 93 deletions(-) diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index ffb672a8f..6c3512430 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -12,11 +12,13 @@ mod array { }; use crate::vm::{ builtins::{ - IntoPyFloat, PyByteArray, PyBytes, PyBytesRef, PyIntRef, PyList, PyListRef, PySliceRef, - PyStr, PyStrRef, PyTypeRef, + PyByteArray, PyBytes, PyBytesRef, PyIntRef, PyList, PyListRef, PySliceRef, PyStr, + PyStrRef, PyTypeRef, }, class_or_notimplemented, - function::{ArgBytesLike, ArgIterable, IntoPyObject, IntoPyResult, OptionalArg}, + function::{ + ArgBytesLike, ArgFloatLike, ArgIterable, IntoPyObject, IntoPyResult, OptionalArg, + }, protocol::{ BufferInternal, BufferOptions, BufferResizeGuard, PyBuffer, PyIterReturn, PyMappingMethods, @@ -521,11 +523,11 @@ mod array { } fn f32_try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - IntoPyFloat::try_from_object(vm, obj).map(|x| x.to_f64() as f32) + ArgFloatLike::try_from_object(vm, obj).map(|x| x.to_f64() as f32) } fn f64_try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - IntoPyFloat::try_from_object(vm, obj).map(|x| x.to_f64()) + ArgFloatLike::try_from_object(vm, obj).map(|x| x.to_f64()) } impl ArrayElement for WideChar { diff --git a/stdlib/src/cmath.rs b/stdlib/src/cmath.rs index 1982269db..04a76cbea 100644 --- a/stdlib/src/cmath.rs +++ b/stdlib/src/cmath.rs @@ -6,8 +6,7 @@ pub(crate) use cmath::make_module; #[pymodule] mod cmath { use crate::vm::{ - builtins::IntoPyFloat, - function::{ArgComplexLike, OptionalArg}, + function::{ArgComplexLike, ArgFloatLike, OptionalArg}, PyResult, VirtualMachine, }; use num_complex::Complex64; @@ -38,7 +37,7 @@ mod cmath { /// Convert from polar coordinates to rectangular coordinates. #[pyfunction] - fn rect(r: IntoPyFloat, phi: IntoPyFloat) -> Complex64 { + fn rect(r: ArgFloatLike, phi: ArgFloatLike) -> Complex64 { Complex64::from_polar(r.to_f64(), phi.to_f64()) } @@ -172,9 +171,9 @@ mod cmath { #[pyarg(positional)] b: ArgComplexLike, #[pyarg(named, optional)] - rel_tol: OptionalArg, + rel_tol: OptionalArg, #[pyarg(named, optional)] - abs_tol: OptionalArg, + abs_tol: OptionalArg, } /// Determine whether two complex numbers are close in value. diff --git a/stdlib/src/math.rs b/stdlib/src/math.rs index a8933f17f..134c7743f 100644 --- a/stdlib/src/math.rs +++ b/stdlib/src/math.rs @@ -4,9 +4,9 @@ pub(crate) use math::make_module; mod math { use crate::vm::{ builtins::{ - try_bigint_to_f64, try_f64_to_bigint, IntoPyFloat, PyFloatRef, PyInt, PyIntRef, + try_bigint_to_f64, try_f64_to_bigint, PyFloatRef, PyInt, PyIntRef, }, - function::{ArgIterable, OptionalArg, PosArgs}, + function::{ArgFloatLike, ArgIterable, OptionalArg, PosArgs}, utils::Either, PyObjectRef, PyResult, PySequence, TypeProtocol, VirtualMachine, }; @@ -43,35 +43,35 @@ mod math { // Number theory functions: #[pyfunction] - fn fabs(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn fabs(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(abs, x, vm) } #[pyfunction] - fn isfinite(x: IntoPyFloat) -> bool { + fn isfinite(x: ArgFloatLike) -> bool { x.to_f64().is_finite() } #[pyfunction] - fn isinf(x: IntoPyFloat) -> bool { + fn isinf(x: ArgFloatLike) -> bool { x.to_f64().is_infinite() } #[pyfunction] - fn isnan(x: IntoPyFloat) -> bool { + fn isnan(x: ArgFloatLike) -> bool { x.to_f64().is_nan() } #[derive(FromArgs)] struct IsCloseArgs { #[pyarg(positional)] - a: IntoPyFloat, + a: ArgFloatLike, #[pyarg(positional)] - b: IntoPyFloat, + b: ArgFloatLike, #[pyarg(named, optional)] - rel_tol: OptionalArg, + rel_tol: OptionalArg, #[pyarg(named, optional)] - abs_tol: OptionalArg, + abs_tol: OptionalArg, } #[allow(clippy::float_cmp)] @@ -110,7 +110,7 @@ mod math { } #[pyfunction] - fn copysign(x: IntoPyFloat, y: IntoPyFloat) -> f64 { + fn copysign(x: ArgFloatLike, y: ArgFloatLike) -> f64 { let a = x.to_f64(); let b = y.to_f64(); if a.is_nan() || b.is_nan() { @@ -122,37 +122,37 @@ mod math { // Power and logarithmic functions: #[pyfunction] - fn exp(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn exp(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(exp, x, vm) } #[pyfunction] - fn expm1(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn expm1(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(exp_m1, x, vm) } #[pyfunction] - fn log(x: IntoPyFloat, base: OptionalArg) -> f64 { + fn log(x: ArgFloatLike, base: OptionalArg) -> f64 { base.map_or_else(|| x.to_f64().ln(), |base| x.to_f64().log(base.to_f64())) } #[pyfunction] - fn log1p(x: IntoPyFloat) -> f64 { + fn log1p(x: ArgFloatLike) -> f64 { (x.to_f64() + 1.0).ln() } #[pyfunction] - fn log2(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn log2(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(log2, x, vm) } #[pyfunction] - fn log10(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn log10(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(log10, x, vm) } #[pyfunction] - fn pow(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn pow(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); let y = y.to_f64(); @@ -170,7 +170,7 @@ mod math { } #[pyfunction] - fn sqrt(value: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn sqrt(value: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let value = value.to_f64(); if value.is_sign_negative() { return Err(vm.new_value_error("math domain error".to_owned())); @@ -191,7 +191,7 @@ mod math { // Trigonometric functions: #[pyfunction] - fn acos(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn acos(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); if x.is_nan() || (-1.0_f64..=1.0_f64).contains(&x) { Ok(x.acos()) @@ -201,7 +201,7 @@ mod math { } #[pyfunction] - fn asin(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn asin(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); if x.is_nan() || (-1.0_f64..=1.0_f64).contains(&x) { Ok(x.asin()) @@ -211,23 +211,23 @@ mod math { } #[pyfunction] - fn atan(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn atan(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(atan, x, vm) } #[pyfunction] - fn atan2(y: IntoPyFloat, x: IntoPyFloat) -> f64 { + fn atan2(y: ArgFloatLike, x: ArgFloatLike) -> f64 { y.to_f64().atan2(x.to_f64()) } #[pyfunction] - fn cos(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn cos(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(cos, x, vm) } #[pyfunction] - fn hypot(coordinates: PosArgs) -> f64 { - let mut coordinates = IntoPyFloat::vec_into_f64(coordinates.into_vec()); + fn hypot(coordinates: PosArgs) -> f64 { + let mut coordinates = ArgFloatLike::vec_into_f64(coordinates.into_vec()); let mut max = 0.0; let mut has_nan = false; for f in &mut coordinates { @@ -267,15 +267,15 @@ mod math { #[pyfunction] fn dist( - p: PySequence, - q: PySequence, + p: PySequence, + q: PySequence, vm: &VirtualMachine, ) -> PyResult { let mut max = 0.0; let mut has_nan = false; - let p = IntoPyFloat::vec_into_f64(p.into_vec()); - let q = IntoPyFloat::vec_into_f64(q.into_vec()); + let p = ArgFloatLike::vec_into_f64(p.into_vec()); + let q = ArgFloatLike::vec_into_f64(q.into_vec()); let mut diffs = vec![]; if p.len() != q.len() { @@ -309,29 +309,29 @@ mod math { } #[pyfunction] - fn sin(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn sin(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(sin, x, vm) } #[pyfunction] - fn tan(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn tan(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(tan, x, vm) } #[pyfunction] - fn degrees(x: IntoPyFloat) -> f64 { + fn degrees(x: ArgFloatLike) -> f64 { x.to_f64() * (180.0 / std::f64::consts::PI) } #[pyfunction] - fn radians(x: IntoPyFloat) -> f64 { + fn radians(x: ArgFloatLike) -> f64 { x.to_f64() * (std::f64::consts::PI / 180.0) } // Hyperbolic functions: #[pyfunction] - fn acosh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn acosh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); if x.is_sign_negative() || x.is_zero() { Err(vm.new_value_error("math domain error".to_owned())) @@ -341,33 +341,33 @@ mod math { } #[pyfunction] - fn asinh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn asinh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(asinh, x, vm) } #[pyfunction] - fn atanh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn atanh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(atanh, x, vm) } #[pyfunction] - fn cosh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn cosh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(cosh, x, vm) } #[pyfunction] - fn sinh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn sinh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(sinh, x, vm) } #[pyfunction] - fn tanh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn tanh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult { call_math_func!(tanh, x, vm) } // Special functions: #[pyfunction] - fn erf(x: IntoPyFloat) -> f64 { + fn erf(x: ArgFloatLike) -> f64 { let x = x.to_f64(); if x.is_nan() { x @@ -377,7 +377,7 @@ mod math { } #[pyfunction] - fn erfc(x: IntoPyFloat) -> f64 { + fn erfc(x: ArgFloatLike) -> f64 { let x = x.to_f64(); if x.is_nan() { x @@ -387,7 +387,7 @@ mod math { } #[pyfunction] - fn gamma(x: IntoPyFloat) -> f64 { + fn gamma(x: ArgFloatLike) -> f64 { let x = x.to_f64(); if x.is_finite() { puruspe::gamma(x) @@ -399,7 +399,7 @@ mod math { } #[pyfunction] - fn lgamma(x: IntoPyFloat) -> f64 { + fn lgamma(x: ArgFloatLike) -> f64 { let x = x.to_f64(); if x.is_finite() { puruspe::ln_gamma(x) @@ -451,7 +451,7 @@ mod math { } #[pyfunction] - fn frexp(x: IntoPyFloat) -> (f64, i32) { + fn frexp(x: ArgFloatLike) -> (f64, i32) { let value = x.to_f64(); if value.is_finite() { let (m, exp) = float_ops::ufrexp(value); @@ -509,7 +509,7 @@ mod math { } #[pyfunction] - fn fsum(seq: ArgIterable, vm: &VirtualMachine) -> PyResult { + fn fsum(seq: ArgIterable, vm: &VirtualMachine) -> PyResult { let mut partials = vec![]; let mut special_sum = 0.0; let mut inf_sum = 0.0; @@ -700,7 +700,7 @@ mod math { } #[pyfunction] - fn modf(x: IntoPyFloat) -> (f64, f64) { + fn modf(x: ArgFloatLike) -> (f64, f64) { let x = x.to_f64(); if !x.is_finite() { if x.is_infinite() { @@ -714,12 +714,12 @@ mod math { } #[pyfunction] - fn nextafter(x: IntoPyFloat, y: IntoPyFloat) -> f64 { + fn nextafter(x: ArgFloatLike, y: ArgFloatLike) -> f64 { float_ops::nextafter(x.to_f64(), y.to_f64()) } #[pyfunction] - fn ulp(x: IntoPyFloat) -> f64 { + fn ulp(x: ArgFloatLike) -> f64 { float_ops::ulp(x.to_f64()) } @@ -732,7 +732,7 @@ mod math { } #[pyfunction(name = "fmod")] - fn py_fmod(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn py_fmod(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); let y = y.to_f64(); @@ -746,7 +746,7 @@ mod math { } #[pyfunction] - fn remainder(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult { + fn remainder(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult { let x = x.to_f64(); let y = y.to_f64(); diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 807dae604..fd7959970 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -545,33 +545,6 @@ pub(crate) fn get_value(obj: &PyObjectRef) -> f64 { obj.payload::().unwrap().value } -#[derive(Debug, Copy, Clone, PartialEq)] -#[repr(transparent)] -pub struct IntoPyFloat { - value: f64, -} - -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 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) } - } -} - -impl TryFromObject for IntoPyFloat { - fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let value = try_float(&obj, vm)?; - Ok(IntoPyFloat { value }) - } -} - #[rustfmt::skip] // to avoid line splitting pub fn init(context: &PyContext) { PyFloat::extend_class(context, &context.types.float_type); diff --git a/vm/src/builtins/mod.rs b/vm/src/builtins/mod.rs index bc3bfb90b..42172478c 100644 --- a/vm/src/builtins/mod.rs +++ b/vm/src/builtins/mod.rs @@ -22,7 +22,7 @@ pub use enumerate::PyEnumerate; pub(crate) mod filter; pub use filter::PyFilter; pub(crate) mod float; -pub use float::{IntoPyFloat, PyFloat, PyFloatRef}; +pub use float::{PyFloat, PyFloatRef}; pub(crate) mod frame; pub(crate) mod function; pub use function::{PyBoundMethod, PyFunction}; diff --git a/vm/src/cformat.rs b/vm/src/cformat.rs index f3d5272bc..1420f9104 100644 --- a/vm/src/cformat.rs +++ b/vm/src/cformat.rs @@ -3,7 +3,8 @@ use crate::common::float_ops; use crate::{ - builtins::{try_f64_to_bigint, tuple, IntoPyFloat, PyBytes, PyFloat, PyInt, PyStr}, + builtins::{try_f64_to_bigint, tuple, PyBytes, PyFloat, PyInt, PyStr}, + function::ArgFloatLike, protocol::PyBuffer, ItemProtocol, PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject, TypeProtocol, VirtualMachine, @@ -431,7 +432,7 @@ impl CFormatSpec { } }, CFormatType::Float(_) => { - let value = IntoPyFloat::try_from_object(vm, obj)?.to_f64(); + let value = ArgFloatLike::try_from_object(vm, obj)?.to_f64(); Ok(self.format_float(value).into_bytes()) } CFormatType::Character => { @@ -504,7 +505,7 @@ impl CFormatSpec { } }, CFormatType::Float(_) => { - let value = IntoPyFloat::try_from_object(vm, obj)?.to_f64(); + let value = ArgFloatLike::try_from_object(vm, obj)?.to_f64(); Ok(self.format_float(value)) } CFormatType::Character => { diff --git a/vm/src/function.rs b/vm/src/function.rs index 92e0c74f1..c01f734b3 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -15,7 +15,7 @@ use std::ops::RangeInclusive; pub use argument::{ArgCallable, ArgIterable}; pub use byteslike::{ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike}; -pub use numlike::ArgComplexLike; +pub use numlike::{ArgComplexLike, ArgFloatLike}; /// Implemented by any type that can be returned from a built-in Python function. /// diff --git a/vm/src/function/numlike.rs b/vm/src/function/numlike.rs index 838c7ec40..9600dc2dd 100644 --- a/vm/src/function/numlike.rs +++ b/vm/src/function/numlike.rs @@ -22,3 +22,32 @@ impl TryFromObject for ArgComplexLike { Ok(ArgComplexLike { value }) } } + +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(transparent)] +pub struct ArgFloatLike { + value: f64, +} + +impl ArgFloatLike { + 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 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) } + } +} + +impl TryFromObject for ArgFloatLike { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let value = obj.try_to_f64(vm)?.ok_or_else(|| { + vm.new_type_error(format!("must be real number, not {}", obj.class().name())) + })?; + Ok(ArgFloatLike { value }) + } +}