diff --git a/crates/rune-alloc/src/boxed.rs b/crates/rune-alloc/src/boxed.rs index 0258a1488..9b4d025d0 100644 --- a/crates/rune-alloc/src/boxed.rs +++ b/crates/rune-alloc/src/boxed.rs @@ -877,6 +877,43 @@ impl TryFrom<[T; N]> for Box<[T]> { } } +/// Casts a boxed slice to a boxed array. +/// +/// # Safety +/// +/// `boxed_slice.len()` must be exactly `N`. +unsafe fn boxed_slice_as_array_unchecked( + boxed_slice: Box<[T], A>, +) -> Box<[T; N], A> { + debug_assert_eq!(boxed_slice.len(), N); + + let (ptr, alloc) = Box::into_raw_with_allocator(boxed_slice); + // SAFETY: Pointer and allocator came from an existing box, + // and our safety condition requires that the length is exactly `N` + unsafe { Box::from_raw_in(ptr as *mut [T; N], alloc) } +} + +impl TryFrom> for Box<[T; N]> { + type Error = Box<[T]>; + + /// Attempts to convert a `Box<[T]>` into a `Box<[T; N]>`. + /// + /// The conversion occurs in-place and does not require a + /// new memory allocation. + /// + /// # Errors + /// + /// Returns the old `Box<[T]>` in the `Err` variant if + /// `boxed_slice.len()` does not equal `N`. + fn try_from(boxed_slice: Box<[T]>) -> Result { + if boxed_slice.len() == N { + Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) }) + } else { + Err(boxed_slice) + } + } +} + impl TryFrom> for Box<[T], A> { type Error = Error; diff --git a/crates/rune-alloc/src/hashbrown/set.rs b/crates/rune-alloc/src/hashbrown/set.rs index 22446fcae..d0e84005b 100644 --- a/crates/rune-alloc/src/hashbrown/set.rs +++ b/crates/rune-alloc/src/hashbrown/set.rs @@ -1732,6 +1732,7 @@ impl Iterator for Drain<'_, K, A> { None => None, } } + #[cfg_attr(feature = "inline-more", inline)] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() diff --git a/crates/rune-macros/src/const_value.rs b/crates/rune-macros/src/const_value.rs index 3a04de752..a1f95de63 100644 --- a/crates/rune-macros/src/const_value.rs +++ b/crates/rune-macros/src/const_value.rs @@ -179,7 +179,7 @@ where #(#members: #from_const_fields,)* }; - #result::Ok(Value::new(value)?) + #result::Ok(#value::new(value)?) } #[inline] @@ -192,7 +192,7 @@ where #(#members: #from_value_fields,)* }; - #result::Ok(Value::new(value)?) + #result::Ok(#value::new(value)?) } } diff --git a/crates/rune-modules/src/core.rs b/crates/rune-modules/src/core.rs index 08bcd9b48..e48ce78c3 100644 --- a/crates/rune-modules/src/core.rs +++ b/crates/rune-modules/src/core.rs @@ -5,7 +5,7 @@ //! Note: **this has been deprecated**, all functions in this module have been //! moved into the [`rune` crate][rune::modules]. -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the `std::core` module. #[deprecated = "all functions in this module have been included in the rune crate, see https://github.com/rune-rs/rune/issues/456"] diff --git a/crates/rune-modules/src/fmt.rs b/crates/rune-modules/src/fmt.rs index 782d7204d..c9f4b601c 100644 --- a/crates/rune-modules/src/fmt.rs +++ b/crates/rune-modules/src/fmt.rs @@ -5,7 +5,7 @@ //! Note: **this has been deprecated**, all functions in this module have been //! moved into the [`rune` crate][rune::modules]. -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the supplemental `std::io` module. #[deprecated = "all functions in this module have been included in the rune crate, see https://github.com/rune-rs/rune/issues/456"] diff --git a/crates/rune-modules/src/fs.rs b/crates/rune-modules/src/fs.rs index c52bfdfde..663bd23a6 100644 --- a/crates/rune-modules/src/fs.rs +++ b/crates/rune-modules/src/fs.rs @@ -27,9 +27,9 @@ //! } //! ``` +use rune::{ContextError, Module}; use std::io; use tokio::fs; -use rune::{Module, ContextError}; /// Construct the `fs` module. pub fn module(_stdio: bool) -> Result { diff --git a/crates/rune-modules/src/http.rs b/crates/rune-modules/src/http.rs index fcc962aad..7ceaed3be 100644 --- a/crates/rune-modules/src/http.rs +++ b/crates/rune-modules/src/http.rs @@ -48,13 +48,13 @@ //! } //! ``` -use core::hash::Hash; use core::cmp::Ordering; +use core::hash::Hash; use rune::alloc::fmt::TryWrite; -use rune::runtime::{Bytes, Formatter, Hasher, Ref, VmResult}; -use rune::{docstring, item, Any, ContextError, Module, Value, ToConstValue}; use rune::alloc::prelude::*; +use rune::runtime::{Bytes, Formatter, Hasher, Ref, VmResult}; +use rune::{docstring, item, Any, ContextError, Module, ToConstValue, Value}; /// A simple HTTP module for Rune. /// @@ -116,8 +116,6 @@ pub fn module(_stdio: bool) -> Result { module.function_meta(StatusCode::string_debug__meta)?; module.function_meta(StatusCode::string_display__meta)?; - - module .constant( "CONTINUE", @@ -1534,13 +1532,17 @@ impl Response { /// Get the status code of the response. #[rune::function(keep, instance)] fn status(&self) -> StatusCode { - StatusCode { inner: self.response.status() } + StatusCode { + inner: self.response.status(), + } } /// Get the version of the response. #[rune::function(keep, instance)] fn version(&self) -> Version { - Version { inner: self.response.version() } + Version { + inner: self.response.version(), + } } /// Get the content-length of this response, if known. @@ -1577,7 +1579,9 @@ impl StatusCode { /// # Example /// /// ```rune - /// let status = http::StatusCode::OK; + /// use http::StatusCode; + /// + /// let status = StatusCode::OK; /// assert_eq!(status.as_u16(), 200); /// ``` #[rune::function(keep, instance)] @@ -1594,7 +1598,9 @@ impl StatusCode { /// # Example /// /// ```rune - /// let status = http::StatusCode::OK; + /// use http::StatusCode; + /// + /// let status = StatusCode::OK; /// assert_eq!(status.as_str(), "200"); /// ``` #[rune::function(keep, instance, vm_result)] @@ -1618,7 +1624,9 @@ impl StatusCode { /// # Example /// /// ```rune - /// let status = http::StatusCode::OK; + /// use http::StatusCode; + /// + /// let status = StatusCode::OK; /// assert_eq!(status.canonical_reason(), Some("OK")); /// ``` #[inline] @@ -1662,13 +1670,12 @@ impl StatusCode { self.inner.is_server_error() } - /// Test two byte arrays for partial equality. + /// Test two status codes for partial equality. /// /// # Examples /// /// ```rune /// use std::ops::partial_eq; - /// /// use http::StatusCode; /// /// let ok = StatusCode::OK; @@ -1684,13 +1691,12 @@ impl StatusCode { PartialEq::eq(&self.inner, &rhs.inner) } - /// Test two byte arrays for total equality. + /// Test two status codes for total equality. /// /// # Examples /// /// ```rune /// use std::ops::eq; - /// /// use http::StatusCode; /// /// let ok = StatusCode::OK; @@ -1706,7 +1712,7 @@ impl StatusCode { PartialEq::eq(&self.inner, &rhs.inner) } - /// Perform a partial ordered comparison between two byte arrays. + /// Perform a partial ordered comparison between two status codes. /// /// # Examples /// @@ -1742,7 +1748,7 @@ impl StatusCode { PartialOrd::partial_cmp(&self.inner, &rhs.inner) } - /// Perform a totally ordered comparison between two byte arrays. + /// Perform a totally ordered comparison between two status codes. /// /// # Examples /// @@ -1765,7 +1771,7 @@ impl StatusCode { Ord::cmp(&self.inner, &rhs.inner) } - /// Hash the string. + /// Hash the status code. /// /// # Examples /// @@ -1844,7 +1850,7 @@ pub struct Version { } impl Version { - /// Test two byte arrays for partial equality. + /// Test two versions for partial equality. /// /// # Examples /// @@ -1866,7 +1872,7 @@ impl Version { PartialEq::eq(&self.inner, &rhs.inner) } - /// Test two byte arrays for total equality. + /// Test two versions for total equality. /// /// # Examples /// @@ -1888,7 +1894,7 @@ impl Version { PartialEq::eq(&self.inner, &rhs.inner) } - /// Perform a partial ordered comparison between two byte arrays. + /// Perform a partial ordered comparison between two versions. /// /// # Examples /// @@ -1924,7 +1930,7 @@ impl Version { PartialOrd::partial_cmp(&self.inner, &rhs.inner) } - /// Perform a totally ordered comparison between two byte arrays. + /// Perform a totally ordered comparison between two versions. /// /// # Examples /// @@ -1947,7 +1953,7 @@ impl Version { Ord::cmp(&self.inner, &rhs.inner) } - /// Hash the string. + /// Hash the version. /// /// # Examples /// @@ -2286,7 +2292,7 @@ async fn get(url: Ref) -> Result { } mod const_version { - use rune::runtime::{RuntimeError, ConstValue, Value}; + use rune::runtime::{ConstValue, RuntimeError, Value}; #[inline] pub(super) fn to_const_value(version: reqwest::Version) -> Result { @@ -2296,24 +2302,20 @@ mod const_version { reqwest::Version::HTTP_11 => Ok(ConstValue::from(3i64)), reqwest::Version::HTTP_2 => Ok(ConstValue::from(4i64)), reqwest::Version::HTTP_3 => Ok(ConstValue::from(5i64)), - version => Err(RuntimeError::panic(format!("Unsupported reqwest version {version:?}"))), + version => Err(RuntimeError::panic(format!( + "Unsupported reqwest version {version:?}" + ))), } } #[inline] pub(super) fn from_const_value(version: &ConstValue) -> Result { - let Some(value) = version.as_i64() else { - return Err(RuntimeError::panic(format!("Unsupported reqwest version {version:?}"))); - }; - - from_i64(value) + from_i64(rune::from_const_value(version)?) } #[inline] pub(super) fn from_value(version: Value) -> Result { - let value = version.as_integer()?; - - from_i64(value) + from_i64(rune::from_value(version)?) } #[inline] @@ -2324,13 +2326,15 @@ mod const_version { 3i64 => Ok(reqwest::Version::HTTP_11), 4i64 => Ok(reqwest::Version::HTTP_2), 5i64 => Ok(reqwest::Version::HTTP_3), - value => Err(RuntimeError::panic(format!("unsupported reqwest version {value}"))), + value => Err(RuntimeError::panic(format!( + "unsupported reqwest version {value}" + ))), } } } mod const_status_code { - use rune::runtime::{RuntimeError, ConstValue, Value}; + use rune::runtime::{ConstValue, RuntimeError, Value}; #[inline] pub(super) fn to_const_value(status: reqwest::StatusCode) -> Result { @@ -2338,12 +2342,10 @@ mod const_status_code { } #[inline] - pub(super) fn from_const_value(status: &ConstValue) -> Result { - let Some(value) = status.as_i64() else { - return Err(RuntimeError::panic(format!("Unsupported reqwest status {status:?}"))); - }; - - match reqwest::StatusCode::from_u16(value as u16) { + pub(super) fn from_const_value( + status: &ConstValue, + ) -> Result { + match reqwest::StatusCode::from_u16(rune::from_const_value(status)?) { Ok(status) => Ok(status), Err(error) => Err(RuntimeError::panic(error)), } diff --git a/crates/rune-modules/src/io.rs b/crates/rune-modules/src/io.rs index 71a67ff3f..b7480fca6 100644 --- a/crates/rune-modules/src/io.rs +++ b/crates/rune-modules/src/io.rs @@ -5,7 +5,7 @@ //! Note: **this has been deprecated**, all functions in this module have been //! moved into the [`rune` crate][rune::modules]. -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the supplemental `std::io` module. #[deprecated = "all functions in this module have been included in the rune crate, see https://github.com/rune-rs/rune/issues/456"] diff --git a/crates/rune-modules/src/json.rs b/crates/rune-modules/src/json.rs index 846141036..1348f45aa 100644 --- a/crates/rune-modules/src/json.rs +++ b/crates/rune-modules/src/json.rs @@ -29,10 +29,10 @@ //! } //! ``` -use rune::{ContextError, Module, vm_write, Any}; -use rune::runtime::{Bytes, Formatter, Value, VmResult}; -use rune::alloc::{Vec, String}; use rune::alloc::fmt::TryWrite; +use rune::alloc::{String, Vec}; +use rune::runtime::{Bytes, Formatter, Value, VmResult}; +use rune::{vm_write, Any, ContextError, Module}; #[rune::module(::json)] /// Module for processing JSON. @@ -82,9 +82,9 @@ impl From for Error { } /// Convert JSON bytes into a rune value. -/// +/// /// # Examples -/// +/// /// ```rune /// let object = json::from_bytes(b"{\"number\": 42, \"string\": \"Hello World\"}")?; /// assert_eq!(object, #{"number": 42, "string": "Hello World"}); @@ -95,9 +95,9 @@ fn from_bytes(bytes: &[u8]) -> Result { } /// Convert a JSON string into a rune value. -/// +/// /// # Examples -/// +/// /// ```rune /// let object = json::from_string("{\"number\": 42, \"string\": \"Hello World\"}")?; /// assert_eq!(object, #{"number": 42, "string": "Hello World"}); @@ -108,9 +108,9 @@ fn from_string(string: &str) -> Result { } /// Convert any value to a json string. -/// +/// /// # Examples -/// +/// /// ```rune /// let object = #{"number": 42, "string": "Hello World"}; /// let object = json::from_string(json::to_string(object)?)?; @@ -122,9 +122,9 @@ fn to_string(value: Value) -> Result { } /// Convert any value to json bytes. -/// +/// /// # Examples -/// +/// /// ```rune /// let object = #{"number": 42, "string": "Hello World"}; /// let object = json::from_bytes(json::to_bytes(object)?)?; @@ -132,5 +132,7 @@ fn to_string(value: Value) -> Result { /// ``` #[rune::function(vm_result)] fn to_bytes(value: Value) -> Result { - Ok(Bytes::from_vec(Vec::try_from(serde_json::to_vec(&value)?).vm?)) + Ok(Bytes::from_vec( + Vec::try_from(serde_json::to_vec(&value)?).vm?, + )) } diff --git a/crates/rune-modules/src/macros.rs b/crates/rune-modules/src/macros.rs index e233921d6..24ef35216 100644 --- a/crates/rune-modules/src/macros.rs +++ b/crates/rune-modules/src/macros.rs @@ -5,7 +5,7 @@ //! Note: **this has been deprecated**, all functions in this module have been //! moved into the [`rune` crate][rune::modules]. -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the supplemental `std::macros` module. #[deprecated = "all functions in this module have been included in the rune crate, see https://github.com/rune-rs/rune/issues/456"] diff --git a/crates/rune-modules/src/process.rs b/crates/rune-modules/src/process.rs index a4d4d9945..8a398c38a 100644 --- a/crates/rune-modules/src/process.rs +++ b/crates/rune-modules/src/process.rs @@ -29,11 +29,11 @@ //! } //! ``` -use rune::{Any, Module, ContextError, vm_try}; -use rune::runtime::{Bytes, Value, VmResult, Formatter}; use rune::alloc::clone::TryClone; use rune::alloc::fmt::TryWrite; use rune::alloc::Vec; +use rune::runtime::{Bytes, Formatter, Value, VmResult}; +use rune::{vm_try, Any, ContextError, Module}; use std::io; use tokio::process; @@ -121,7 +121,9 @@ impl Child { let output = inner.wait_with_output().await?; Ok(Output { - status: ExitStatus { status: output.status }, + status: ExitStatus { + status: output.status, + }, stdout: Bytes::from_vec(Vec::try_from(output.stdout).vm?), stderr: Bytes::from_vec(Vec::try_from(output.stderr).vm?), }) diff --git a/crates/rune-modules/src/rand.rs b/crates/rune-modules/src/rand.rs index 053a57706..7b1e78631 100644 --- a/crates/rune-modules/src/rand.rs +++ b/crates/rune-modules/src/rand.rs @@ -39,14 +39,22 @@ pub fn module(_stdio: bool) -> Result { let mut module = Module::with_crate("rand")?; module.ty::()?; - module.function("new", WyRand::new).build_associated::()?; - module.function("new_seed", WyRand::new_seed).build_associated::()?; + module + .function("new", WyRand::new) + .build_associated::()?; + module + .function("new_seed", WyRand::new_seed) + .build_associated::()?; module.associated_function("int", WyRand::int)?; module.associated_function("int_range", WyRand::int_range)?; module.ty::()?; - module.function("new", Pcg64::new).build_associated::()?; - module.function("new_seed", Pcg64::new_seed).build_associated::()?; + module + .function("new", Pcg64::new) + .build_associated::()?; + module + .function("new_seed", Pcg64::new_seed) + .build_associated::()?; module.associated_function("int", Pcg64::int)?; module.associated_function("int_range", Pcg64::int_range)?; @@ -120,15 +128,11 @@ impl Pcg64 { } fn int() -> rune::support::Result { - Ok( - nanorand::WyRand::new().generate::() as i64 - ) + Ok(nanorand::WyRand::new().generate::() as i64) } fn int_range(lower: i64, upper: i64) -> rune::support::Result { - Ok( - nanorand::WyRand::new().generate_range(0..(upper - lower) as u64) as i64 + lower, - ) + Ok(nanorand::WyRand::new().generate_range(0..(upper - lower) as u64) as i64 + lower) } #[cfg(test)] diff --git a/crates/rune-modules/src/signal.rs b/crates/rune-modules/src/signal.rs index f77859fe9..ecd9b4235 100644 --- a/crates/rune-modules/src/signal.rs +++ b/crates/rune-modules/src/signal.rs @@ -29,7 +29,7 @@ use std::io; -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the `signal` module. pub fn module(_stdio: bool) -> Result { @@ -49,10 +49,10 @@ pub fn module(_stdio: bool) -> Result { /// either `Future::poll` or `.await`. /// /// # Tokio -/// +/// /// This function is implemented using [Tokio], and requires the Tokio runtime /// to be in scope. -/// +/// /// [Tokio]: https://tokio.rs /// /// # Caveats diff --git a/crates/rune-modules/src/test.rs b/crates/rune-modules/src/test.rs index 9f8b64399..30fb16493 100644 --- a/crates/rune-modules/src/test.rs +++ b/crates/rune-modules/src/test.rs @@ -5,7 +5,7 @@ //! Note: **this has been deprecated**, all functions in this module have been //! moved into the [`rune` crate][rune::modules]. -use rune::{Module, ContextError}; +use rune::{ContextError, Module}; /// Construct the supplemental `std::test` module. #[deprecated = "all functions in this module have been included in the rune crate, see https://github.com/rune-rs/rune/issues/456"] diff --git a/crates/rune-modules/src/time.rs b/crates/rune-modules/src/time.rs index 428361932..ef73d7b11 100644 --- a/crates/rune-modules/src/time.rs +++ b/crates/rune-modules/src/time.rs @@ -29,53 +29,1127 @@ //! } //! ``` -use rune::{Any, ContextError, Module}; +use core::cmp::Ordering; +use core::hash::Hash; + +use rune::alloc::fmt::TryWrite; +use rune::runtime::{Formatter, Hasher, Mut, VmResult}; +use rune::{docstring, item, vm_panic, Any, ContextError, Module, ToConstValue}; + +const NANOS_PER_SEC: u32 = 1_000_000_000; /// Construct the `time` module. pub fn module(_stdio: bool) -> Result { - let mut module = Module::with_crate("time")?; - module.ty::()?; - module.function_meta(Duration::from_secs__meta)?; - module.function_meta(sleep)?; - Ok(module) + let mut m = Module::with_crate("time")?; + + m.function_meta(sleep)?; + m.function_meta(interval)?; + m.function_meta(interval_at)?; + + m.ty::()?; + + m.function_meta(Duration::new__meta)?; + m.function_meta(Duration::from_secs__meta)?; + m.function_meta(Duration::from_millis__meta)?; + m.function_meta(Duration::from_micros__meta)?; + m.function_meta(Duration::from_nanos__meta)?; + m.function_meta(Duration::as_secs_f64__meta)?; + m.function_meta(Duration::from_secs_f64__meta)?; + m.function_meta(Duration::add__meta)?; + m.function_meta(Duration::add_assign__meta)?; + m.function_meta(Duration::partial_eq__meta)?; + m.implement_trait::(item!(::std::cmp::PartialEq))?; + m.function_meta(Duration::eq__meta)?; + m.implement_trait::(item!(::std::cmp::Eq))?; + m.function_meta(Duration::partial_cmp__meta)?; + m.implement_trait::(item!(::std::cmp::PartialOrd))?; + m.function_meta(Duration::cmp__meta)?; + m.implement_trait::(item!(::std::cmp::Ord))?; + m.function_meta(Duration::hash__meta)?; + m.function_meta(Duration::string_debug__meta)?; + m.function_meta(Duration::clone__meta)?; + m.implement_trait::(item!(::std::clone::Clone))?; + + m + .constant( + "SECOND", + Duration { + inner: tokio::time::Duration::from_secs(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one second. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::SECOND; + /// ``` + })?; + + m + .constant( + "MILLISECOND", + Duration { + inner: tokio::time::Duration::from_millis(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one millisecond. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::MILLISECOND; + /// ``` + })?; + + m + .constant( + "MICROSECOND", + Duration { + inner: tokio::time::Duration::from_micros(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one microsecond. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::MICROSECOND; + /// ``` + })?; + + m + .constant( + "NANOSECOND", + Duration { + inner: tokio::time::Duration::from_nanos(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one nanosecond. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::NANOSECOND; + /// ``` + })?; + + m + .constant( + "ZERO", + Duration { + inner: tokio::time::Duration::ZERO, + }, + ) + .build_associated::()? + .docs(docstring! { + /// A duration of zero time. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::ZERO; + /// ``` + })?; + + m + .constant( + "MAX", + Duration { + inner: tokio::time::Duration::MAX, + }, + ) + .build_associated::()? + .docs(docstring! { + /// The maximum duration. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::MAX; + /// assert!(Duration::ZERO < Duration::MAX); + /// ``` + })?; + + m.ty::()?; + m.function_meta(Instant::now__meta)?; + m.function_meta(Instant::duration_since__meta)?; + m.function_meta(Instant::elapsed__meta)?; + m.function_meta(Instant::add__meta)?; + m.function_meta(Instant::add_assign__meta)?; + m.function_meta(Instant::partial_eq__meta)?; + m.implement_trait::(item!(::std::cmp::PartialEq))?; + m.function_meta(Instant::eq__meta)?; + m.implement_trait::(item!(::std::cmp::Eq))?; + m.function_meta(Instant::partial_cmp__meta)?; + m.implement_trait::(item!(::std::cmp::PartialOrd))?; + m.function_meta(Instant::cmp__meta)?; + m.implement_trait::(item!(::std::cmp::Ord))?; + m.function_meta(Instant::hash__meta)?; + m.function_meta(Instant::string_debug__meta)?; + m.function_meta(Instant::clone__meta)?; + m.implement_trait::(item!(::std::clone::Clone))?; + + m.ty::()?; + m + .function("tick", Interval::tick) + .build_associated::()?; + m.function_meta(Interval::reset__meta)?; + m.function_meta(Interval::reset_immediately__meta)?; + m.function_meta(Interval::reset_after__meta)?; + m.function_meta(Interval::reset_at__meta)?; + + Ok(m) +} + +/// Waits until duration has elapsed. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::Duration; +/// +/// let duration = Duration::from_secs(10); +/// time::sleep(duration).await; +/// println!("Surprise!"); +/// ``` +#[rune::function] +async fn sleep(duration: Duration) { + tokio::time::sleep(duration.inner).await; +} + +/// Creates new [`Interval`] that yields with interval of `period`. The first +/// tick completes immediately. +/// +/// An interval will tick indefinitely. At any time, the [`Interval`] value can +/// be dropped. This cancels the interval. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::Duration; +/// +/// let duration = Duration::from_millis(10); +/// let interval = time::interval(duration); +/// +/// interval.tick().await; // ticks immediately +/// interval.tick().await; // ticks after 10ms +/// interval.tick().await; // ticks after 10ms +/// +/// println!("approximately 20ms have elapsed..."); +/// ``` +#[rune::function] +async fn interval(period: Duration) -> Interval { + Interval { + inner: tokio::time::interval(period.inner), + } } -#[derive(Debug, Clone, Copy, Any)] +/// Creates new [`Interval`] that yields with interval of `period` with the +/// first tick completing at `start`. +/// +/// An interval will tick indefinitely. At any time, the [`Interval`] value can +/// be dropped. This cancels the interval. +/// +/// # Vm Panics +/// +/// This function panics if `period` is zero. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::{Duration, Instant}; +/// +/// let start = Instant::now() + Duration::from_millis(50); +/// let interval = time::interval_at(start, Duration::from_millis(10)); +/// +/// interval.tick().await; // ticks after 50ms +/// interval.tick().await; // ticks after 10ms +/// interval.tick().await; // ticks after 10ms +/// +/// println!("approximately 70ms have elapsed..."); +/// ``` +#[rune::function] +async fn interval_at(start: Instant, period: Duration) -> Interval { + Interval { + inner: tokio::time::interval_at(start.inner, period.inner), + } +} + +/// A `Duration` type to represent a span of time, typically used for system +/// timeouts. +/// +/// Each `Duration` is composed of a whole number of seconds and a fractional part +/// represented in nanoseconds. If the underlying system does not support +/// nanosecond-level precision, APIs binding a system timeout will typically round up +/// the number of nanoseconds. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::Duration; +/// +/// let five_seconds = Duration::new(5, 0); +/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); +/// +/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5); +/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5); +/// +/// let ten_millis = Duration::from_millis(10); +/// ``` +#[derive(Debug, Clone, Copy, Any, ToConstValue)] #[rune(item = ::time)] -struct Duration { +pub struct Duration { + #[const_value(with = self::const_duration)] inner: tokio::time::Duration, } impl Duration { - /// Construct a duration from the given number of seconds. - /// + /// Converts [`Duration`] into a [`std::time::Duration`]. + pub fn into_std(self) -> std::time::Duration { + std::time::Duration::new(self.inner.as_secs(), self.inner.subsec_nanos()) + } + + /// Creates a [`Duration`] from a [`std::time::Duration`]. + pub fn from_std(duration: std::time::Duration) -> Self { + Self { + inner: tokio::time::Duration::new(duration.as_secs(), duration.subsec_nanos()), + } + } + + /// Converts [`Duration`] into a [`tokio::time::Duration`]. + /// + /// # Example + /// + /// ``` + /// use time::Duration; + /// + /// let duration = Duration::from_secs(5); + /// let tokio_duration = duration.into_tokio(); + /// ``` + pub fn into_tokio(self) -> tokio::time::Duration { + self.inner + } + + /// Creates a [`Duration`] from a [`tokio::time::Duration`]. + /// + /// # Example + /// + /// ``` + /// use time::Duration; + /// + /// let tokio_duration = tokio::time::Duration::from_secs(5); + /// let duration = Duration::from_tokio(tokio_duration); + /// ``` + pub fn from_tokio(duration: tokio::time::Duration) -> Self { + Self { inner: duration } + } + + /// Creates a new `Duration` from the specified number of whole seconds and + /// additional nanoseconds. + /// + /// If the number of nanoseconds is greater than 1 billion (the number of + /// nanoseconds in a second), then it will carry over into the seconds provided. + /// + /// # Vm Panics + /// + /// This constructor will panic if the carry from the nanoseconds overflows + /// the seconds counter. + /// /// # Examples - /// - /// ```rune + /// + /// ```rune,no_run /// use time::Duration; /// - /// let d = Duration::from_secs(10); + /// let five_seconds = Duration::new(5, 0); + /// ``` + #[rune::function(keep, path = Self::new)] + pub fn new(secs: u64, nanos: u32) -> VmResult { + if nanos >= NANOS_PER_SEC { + if secs.checked_add((nanos / NANOS_PER_SEC) as u64).is_none() { + vm_panic!("overflow in Duration::new"); + } + } + + VmResult::Ok(Self { + inner: tokio::time::Duration::new(secs, nanos), + }) + } + + /// Creates a new `Duration` from the specified number of whole seconds. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_secs(5); /// ``` #[rune::function(keep, path = Self::from_secs)] - fn from_secs(secs: u64) -> Self { + pub const fn from_secs(secs: u64) -> Self { Self { inner: tokio::time::Duration::from_secs(secs), } } + + /// Creates a new `Duration` from the specified number of milliseconds. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_millis(2569); + /// ``` + #[rune::function(keep, path = Self::from_millis)] + pub const fn from_millis(millis: u64) -> Self { + Self { + inner: tokio::time::Duration::from_millis(millis), + } + } + + /// Creates a new `Duration` from the specified number of microseconds. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_micros(1_000_002); + /// ``` + #[rune::function(keep, path = Self::from_micros)] + pub const fn from_micros(micros: u64) -> Self { + Self { + inner: tokio::time::Duration::from_micros(micros), + } + } + + /// Creates a new `Duration` from the specified number of nanoseconds. + /// + /// Note: Using this on the return value of `as_nanos()` might cause unexpected behavior: + /// `as_nanos()` returns a u128, and can return values that do not fit in u64, e.g. 585 years. + /// Instead, consider using the pattern `Duration::new(d.as_secs(), d.subsec_nanos())` + /// if you cannot copy/clone the Duration directly. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_nanos(1_000_000_123); + /// ``` + #[rune::function(keep, path = Self::from_nanos)] + pub const fn from_nanos(nanos: u64) -> Self { + Self { + inner: tokio::time::Duration::from_nanos(nanos), + } + } + + /// Returns the number of seconds contained by this `Duration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_secs(60).as_secs_f64(); + /// ``` + #[rune::function(keep, path = Self::as_secs_f64)] + pub fn as_secs_f64(&self) -> f64 { + self.inner.as_secs_f64() + } + + /// Creates a new `Duration` from the specified number of seconds represented + /// as `f64`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let duration = Duration::from_secs_f64(0.0); + /// ``` + #[rune::function(keep, path = Self::from_secs_f64)] + pub fn from_secs_f64(secs: f64) -> VmResult { + match tokio::time::Duration::try_from_secs_f64(secs) { + Ok(duration) => VmResult::Ok(Self { inner: duration }), + Err(e) => vm_panic!(e), + } + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD)] + #[inline] + fn add(&self, rhs: &Duration) -> VmResult { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding durations") + }; + + VmResult::Ok(Self { + inner, + }) + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD_ASSIGN)] + #[inline] + fn add_assign(&mut self, rhs: &Duration) -> VmResult<()> { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + self.inner = inner; + VmResult::Ok(()) + } + + /// Test two durations for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(partial_eq(millis, millis), true); + /// assert_eq!(partial_eq(millis, second), false); + /// assert_eq!(partial_eq(second, millis), false); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_EQ)] + #[inline] + fn partial_eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Test two durations for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(eq(millis, millis), true); + /// assert_eq!(eq(millis, second), false); + /// assert_eq!(eq(second, millis), false); + /// ``` + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Perform a partial ordered comparison between two durations. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert!(millis < second); + /// assert!(second > millis); + /// assert!(millis == millis); + /// ``` + /// + /// Using explicit functions: + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::partial_cmp; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(partial_cmp(millis, second), Some(Ordering::Less)); + /// assert_eq!(partial_cmp(second, millis), Some(Ordering::Greater)); + /// assert_eq!(partial_cmp(millis, millis), Some(Ordering::Equal)); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_CMP)] + #[inline] + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.inner, &rhs.inner) + } + + /// Perform a totally ordered comparison between two durations. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(cmp(millis, second), Ordering::Less); + /// assert_eq!(cmp(second, millis), Ordering::Greater); + /// assert_eq!(cmp(millis, millis), Ordering::Equal); + /// ``` + #[rune::function(keep, instance, protocol = CMP)] + #[inline] + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.inner, &rhs.inner) + } + + /// Hash the duration. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::hash; + /// + /// use time::Duration; + /// + /// let second = Duration::SECOND; + /// + /// assert_eq!(hash(second), hash(second)); + /// ``` + #[rune::function(keep, instance, protocol = HASH)] + fn hash(&self, hasher: &mut Hasher) { + self.inner.hash(hasher); + } + + /// Write a debug representation of the duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let second = Duration::SECOND; + /// + /// println!("{second:?}"); + /// ``` + #[rune::function(keep, instance, protocol = STRING_DEBUG)] + fn string_debug(&self, f: &mut Formatter) -> VmResult<()> { + rune::vm_write!(f, "{:?}", self.inner) + } + + /// Clone the current duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = Duration::SECOND; + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = CLONE)] + fn clone(&self) -> Self { + Self { inner: self.inner } + } } -/// Sleep for the given [`Duration`]. -/// -/// # Examples -/// -/// ```rune,no_run -/// use time::Duration; +mod const_duration { + use rune::runtime::{ConstValue, RuntimeError, Value}; + use tokio::time::Duration; + + #[inline] + pub(super) fn to_const_value(duration: Duration) -> Result { + let secs = duration.as_secs(); + let nanos = duration.subsec_nanos(); + rune::to_const_value((secs, nanos)) + } + + #[inline] + pub(super) fn from_const_value(value: &ConstValue) -> Result { + let (secs, nanos) = rune::from_const_value::<(u64, u32)>(value)?; + Ok(Duration::new(secs, nanos)) + } + + #[inline] + pub(super) fn from_value(value: Value) -> Result { + let (secs, nanos) = rune::from_value::<(u64, u32)>(value)?; + Ok(Duration::new(secs, nanos)) + } +} + +/// Interval returned by [`interval`] and [`interval_at`]. /// -/// let d = Duration::from_secs(10); -/// time::sleep(d).await; -/// println!("Surprise!"); -/// ``` -#[rune::function] -async fn sleep(duration: Duration) { - tokio::time::sleep(duration.inner).await; +/// This type allows you to wait on a sequence of instants with a certain +/// duration between each instant. Unlike calling [`sleep`] in a loop, this lets +/// you count the time spent between the calls to [`sleep`] as well. +#[derive(Debug, Any)] +#[rune(item = ::time)] +pub struct Interval { + inner: tokio::time::Interval, +} + +impl Interval { + /// Completes when the next instant in the interval has been reached. + /// + /// # Cancel safety + /// + /// This method is cancellation safe. If `tick` is used as the branch in a `select` and + /// another branch completes first, then no tick has been consumed. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(10)); + /// + /// interval.tick().await; + /// println!("approximately 0ms have elapsed. The first tick completes immediately."); + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 20ms have elapsed..."); + /// ``` + pub async fn tick(mut internal: Mut) { + internal.inner.tick().await; + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset(&mut self) { + self.inner.reset(); + } + + /// Resets the interval immediately. + /// + /// This is equivalent to calling `reset_at(Instant::now())`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset_immediately(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 150ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_immediately(&mut self) { + self.inner.reset_immediately(); + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_after(&mut self, after: Duration) { + self.inner.reset_after(after.inner); + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_at(&mut self, deadline: Instant) { + self.inner.reset_at(deadline.inner); + } +} + +/// A measurement of a monotonically nondecreasing clock. +/// Opaque and useful only with `Duration`. +/// +/// Instants are always guaranteed to be no less than any previously measured +/// instant when created, and are often useful for tasks such as measuring +/// benchmarks or timing how long an operation takes. +/// +/// Note, however, that instants are not guaranteed to be **steady**. In other +/// words, each tick of the underlying clock may not be the same length (e.g. +/// some seconds may be longer than others). An instant may jump forwards or +/// experience time dilation (slow down or speed up), but it will never go +/// backwards. +/// +/// Instants are opaque types that can only be compared to one another. There is +/// no method to get "the number of seconds" from an instant. Instead, it only +/// allows measuring the duration between two instants (or comparing two +/// instants). +/// +/// The size of an `Instant` struct may vary depending on the target operating +/// system. +#[derive(Debug, Any)] +#[rune(item = ::time)] +pub struct Instant { + inner: tokio::time::Instant, +} + +impl Instant { + /// Returns an instant corresponding to `now`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// ``` + #[rune::function(keep, path = Self::now)] + pub fn now() -> Instant { + Instant { + inner: tokio::time::Instant::now(), + } + } + + /// Returns the amount of time elapsed from another instant to this one, or + /// zero duration if that instant is later than this one. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::{sleep, Duration, Instant}; + /// + /// let instant = Instant::now(); + /// + /// let three_secs = Duration::from_secs(3); + /// sleep(three_secs).await; + /// + /// let now = Instant::now(); + /// let duration_since = now.duration_since(instant); + /// ``` + #[rune::function(instance, keep)] + pub fn duration_since(&self, earlier: Instant) -> Duration { + Duration { + inner: tokio::time::Instant::duration_since(&self.inner, earlier.inner), + } + } + + /// Returns the amount of time elapsed since this instant was created, or + /// zero duration if that this instant is in the future. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::{sleep, Duration, Instant}; + /// + /// let instant = Instant::now(); + /// + /// let three_secs = Duration::from_secs(3); + /// sleep(three_secs).await; + /// + /// let elapsed = instant.elapsed(); + /// ``` + #[rune::function(instance, keep)] + pub fn elapsed(&self) -> Duration { + Duration { + inner: tokio::time::Instant::elapsed(&self.inner), + } + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD)] + #[inline] + fn add(&self, rhs: &Duration) -> VmResult { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + VmResult::Ok(Self { + inner, + }) + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD_ASSIGN)] + #[inline] + fn add_assign(&mut self, rhs: &Duration) -> VmResult<()> { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + self.inner = inner; + VmResult::Ok(()) + } + + /// Test two instants for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(partial_eq(first, first), true); + /// assert_eq!(partial_eq(first, second), false); + /// assert_eq!(partial_eq(second, first), false); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_EQ)] + #[inline] + fn partial_eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Test two instants for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(eq(first, first), true); + /// assert_eq!(eq(first, second), false); + /// assert_eq!(eq(second, first), false); + /// ``` + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Perform a partial ordered comparison between two instants. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// assert!(second > first); + /// assert!(first == first); + /// ``` + /// + /// Using explicit functions: + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::partial_cmp; + /// + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(partial_cmp(first, second), Some(Ordering::Less)); + /// assert_eq!(partial_cmp(second, first), Some(Ordering::Greater)); + /// assert_eq!(partial_cmp(first, first), Some(Ordering::Equal)); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_CMP)] + #[inline] + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.inner, &rhs.inner) + } + + /// Perform a totally ordered comparison between two instants. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(cmp(first, second), Ordering::Less); + /// assert_eq!(cmp(second, first), Ordering::Greater); + /// assert_eq!(cmp(first, second), Ordering::Equal); + /// ``` + #[rune::function(keep, instance, protocol = CMP)] + #[inline] + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.inner, &rhs.inner) + } + + /// Hash the instant. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::hash; + /// use time::{Duration, Instant}; + /// + /// let now = Instant::now(); + /// + /// assert_eq!(hash(now), hash(now)); + /// ``` + #[rune::function(keep, instance, protocol = HASH)] + fn hash(&self, hasher: &mut Hasher) { + self.inner.hash(hasher); + } + + /// Write a debug representation of the instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Instant; + /// + /// let now = Instant::now(); + /// + /// println!("{now:?}"); + /// ``` + #[rune::function(keep, instance, protocol = STRING_DEBUG)] + fn string_debug(&self, f: &mut Formatter) -> VmResult<()> { + rune::vm_write!(f, "{:?}", self.inner) + } + + /// Clone the current instant. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = CLONE)] + fn clone(&self) -> Self { + Self { inner: self.inner } + } } diff --git a/crates/rune-modules/src/toml.rs b/crates/rune-modules/src/toml.rs index dce6ad0f9..d39975362 100644 --- a/crates/rune-modules/src/toml.rs +++ b/crates/rune-modules/src/toml.rs @@ -29,9 +29,9 @@ //! } //! ``` -use rune::{ContextError, Module}; -use rune::runtime::{Bytes, Value}; use rune::alloc::String; +use rune::runtime::{Bytes, Value}; +use rune::{ContextError, Module}; /// Construct the `toml` module. pub fn module(_stdio: bool) -> Result { @@ -46,9 +46,9 @@ pub fn module(_stdio: bool) -> Result { pub mod de { //! Deserializer types for the toml module. - use rune::{Any, Module, ContextError, vm_write}; - use rune::runtime::{Formatter, VmResult}; use rune::alloc::fmt::TryWrite; + use rune::runtime::{Formatter, VmResult}; + use rune::{vm_write, Any, ContextError, Module}; pub fn module(_stdio: bool) -> Result { let mut module = Module::with_crate_item("toml", ["de"])?; @@ -86,9 +86,9 @@ pub mod de { pub mod ser { //! Serializer types for the toml module. - use rune::{Any, Module, ContextError, vm_write}; - use rune::runtime::{Formatter, VmResult}; use rune::alloc::fmt::TryWrite; + use rune::runtime::{Formatter, VmResult}; + use rune::{vm_write, Any, ContextError, Module}; pub fn module(_stdio: bool) -> Result { let mut module = Module::with_crate_item("toml", ["ser"])?; diff --git a/crates/rune/src/ast/lit_number.rs b/crates/rune/src/ast/lit_number.rs index ecc2025b6..fa5f2d9f4 100644 --- a/crates/rune/src/ast/lit_number.rs +++ b/crates/rune/src/ast/lit_number.rs @@ -93,7 +93,8 @@ impl<'a> Resolve<'a> for LitNumber { .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?; let suffix = match suffix { - "i64" => Some(ast::NumberSuffix::Int(text.suffix)), + "i64" => Some(ast::NumberSuffix::Signed(text.suffix)), + "u64" => Some(ast::NumberSuffix::Unsigned(text.suffix)), "f64" => Some(ast::NumberSuffix::Float(text.suffix)), "u8" => Some(ast::NumberSuffix::Byte(text.suffix)), "" => None, diff --git a/crates/rune/src/ast/token.rs b/crates/rune/src/ast/token.rs index bd705f281..ba5fb4e3f 100644 --- a/crates/rune/src/ast/token.rs +++ b/crates/rune/src/ast/token.rs @@ -207,7 +207,9 @@ pub enum NumberValue { #[non_exhaustive] pub enum NumberSuffix { /// The `i64` suffix. - Int(Span), + Signed(Span), + /// The `u64` suffix. + Unsigned(Span), /// The `f64` suffix. Float(Span), /// The `u8` suffix. diff --git a/crates/rune/src/cli/tests.rs b/crates/rune/src/cli/tests.rs index bbe3a9ef2..8102f06c1 100644 --- a/crates/rune/src/cli/tests.rs +++ b/crates/rune/src/cli/tests.rs @@ -537,10 +537,12 @@ impl TestCase { } } + let mut emitted = None; + match &self.outcome { Outcome::Panic(error) => { section.error("panicked")?; - error.emit(section.io, &self.sources)?; + emitted = Some(error); } Outcome::ExpectedPanic => { section.error("expected panic because of `should_panic`, but ran without issue")?; @@ -559,6 +561,10 @@ impl TestCase { section.close()?; + if let Some(error) = emitted { + error.emit(io.stdout, &self.sources)?; + } + if !self.outcome.is_ok() && !self.output.is_empty() { writeln!(io.stdout, "-- output --")?; io.stdout.write_all(&self.output)?; diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 0dfcf89cf..bfd87d16c 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -333,6 +333,7 @@ impl Context { this.install(crate::modules::fmt::module()?)?; this.install(crate::modules::future::module()?)?; this.install(crate::modules::i64::module()?)?; + this.install(crate::modules::u64::module()?)?; this.install(crate::modules::io::module(stdio)?)?; this.install(crate::modules::macros::module()?)?; this.install(crate::modules::macros::builtin::module()?)?; diff --git a/crates/rune/src/compile/context_error.rs b/crates/rune/src/compile/context_error.rs index 4a3c0016e..f9442ab14 100644 --- a/crates/rune/src/compile/context_error.rs +++ b/crates/rune/src/compile/context_error.rs @@ -2,6 +2,7 @@ use core::fmt; use crate::alloc; use crate::alloc::prelude::*; +use crate::compile::meta::AssociatedKind; use crate::runtime::{RuntimeError, TypeInfo}; use crate::{Hash, ItemBuf}; @@ -13,7 +14,13 @@ pub enum ContextError { AllocError { error: alloc::Error, }, - RuntimeError { + InvalidConstValue { + item: ItemBuf, + error: RuntimeError, + }, + InvalidAssociatedConstValue { + container: TypeInfo, + kind: AssociatedKind, error: RuntimeError, }, UnitAlreadyPresent, @@ -158,13 +165,6 @@ impl From for ContextError { } } -impl From for ContextError { - #[inline] - fn from(error: RuntimeError) -> Self { - ContextError::RuntimeError { error } - } -} - impl From for ContextError { #[inline] fn from(error: alloc::alloc::AllocError) -> Self { @@ -180,8 +180,18 @@ impl fmt::Display for ContextError { ContextError::AllocError { error } => { error.fmt(f)?; } - ContextError::RuntimeError { error } => { - error.fmt(f)?; + ContextError::InvalidConstValue { item, error } => { + write!(f, "Error when building constant {item}: {error}")?; + } + ContextError::InvalidAssociatedConstValue { + container, + kind, + error, + } => { + write!( + f, + "Error when building associated constant in {container}::{kind}: {error}" + )?; } ContextError::UnitAlreadyPresent {} => { write!(f, "Unit `()` type is already present")?; diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index 64a46becd..551b21e86 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -480,7 +480,8 @@ pub(crate) enum ErrorKind { PrecedenceGroupRequired, BadByteNeg, BadByteOutOfBounds, - BadNumberOutOfBounds, + BadSignedOutOfBounds, + BadUnsignedOutOfBounds, BadFieldAccess, ExpectedMacroCloseDelimiter { expected: ast::Kind, @@ -1033,12 +1034,18 @@ impl fmt::Display for ErrorKind { ErrorKind::BadByteOutOfBounds => { write!(f, "Byte literal out of bounds `0` to `255`")?; } - ErrorKind::BadNumberOutOfBounds => { + ErrorKind::BadSignedOutOfBounds => { write!( f, "Number literal out of bounds `-9223372036854775808` to `9223372036854775807`" )?; } + ErrorKind::BadUnsignedOutOfBounds => { + write!( + f, + "Number literal out of bounds `0` to `18446744073709551615`" + )?; + } ErrorKind::BadFieldAccess => { write!(f, "Unsupported field access")?; } @@ -1141,7 +1148,7 @@ impl fmt::Display for ErrorKind { ErrorKind::UnsupportedSuffix => { write!( f, - "Unsupported suffix, expected one of `u8`, `i64`, or `f64`" + "Unsupported suffix, expected one of `u8`, `i64`, `u64`, or `f64`" )?; } ErrorKind::ClosureInConst => { diff --git a/crates/rune/src/compile/ir.rs b/crates/rune/src/compile/ir.rs index 2a380e3c2..5335b355d 100644 --- a/crates/rune/src/compile/ir.rs +++ b/crates/rune/src/compile/ir.rs @@ -534,8 +534,8 @@ impl IrAssignOp { where S: Copy + Spanned, { - if let Some(Inline::Integer(target)) = target.as_inline_mut().with_span(spanned)? { - if let Some(Inline::Integer(operand)) = operand.as_inline().with_span(spanned)? { + if let Some(Inline::Signed(target)) = target.as_inline_mut().with_span(spanned)? { + if let Some(Inline::Signed(operand)) = operand.as_inline().with_span(spanned)? { return self.assign_int(spanned, target, *operand); } } diff --git a/crates/rune/src/compile/ir/compiler.rs b/crates/rune/src/compile/ir/compiler.rs index 5e1655f56..d65bb7619 100644 --- a/crates/rune/src/compile/ir/compiler.rs +++ b/crates/rune/src/compile/ir/compiler.rs @@ -208,7 +208,11 @@ fn lit(_c: &mut Ctxt<'_, '_>, span: Span, hir: hir::Lit<'_>) -> compile::Result< let value = Value::from(boolean); ir::Ir::new(span, value) } - hir::Lit::Integer(n) => { + hir::Lit::Unsigned(n) => { + let value = Value::from(n); + ir::Ir::new(span, value) + } + hir::Lit::Signed(n) => { let value = Value::from(n); ir::Ir::new(span, value) } diff --git a/crates/rune/src/compile/ir/eval.rs b/crates/rune/src/compile/ir/eval.rs index 15c72e460..2ddd91f07 100644 --- a/crates/rune/src/compile/ir/eval.rs +++ b/crates/rune/src/compile/ir/eval.rs @@ -79,21 +79,21 @@ fn eval_ir_binary( (BorrowRefRepr::Inline(a), BorrowRefRepr::Inline(b)) => { let out = 'out: { match (a, b) { - (Inline::Integer(a), Inline::Integer(b)) => match ir.op { + (Inline::Signed(a), Inline::Signed(b)) => match ir.op { ir::IrBinaryOp::Add => { - break 'out Inline::Integer(a.add(b)); + break 'out Inline::Signed(a.add(b)); } ir::IrBinaryOp::Sub => { - break 'out Inline::Integer(a.sub(b)); + break 'out Inline::Signed(a.sub(b)); } ir::IrBinaryOp::Mul => { - break 'out Inline::Integer(a.mul(b)); + break 'out Inline::Signed(a.mul(b)); } ir::IrBinaryOp::Div => { let number = a .checked_div(*b) .ok_or_else(|| compile::Error::msg(span, "division by zero"))?; - break 'out Inline::Integer(number); + break 'out Inline::Signed(number); } ir::IrBinaryOp::Shl => { let b = u32::try_from(*b).map_err(|_| { @@ -101,7 +101,7 @@ fn eval_ir_binary( })?; let n = a.shl(b); - break 'out Inline::Integer(n); + break 'out Inline::Signed(n); } ir::IrBinaryOp::Shr => { let b = u32::try_from(*b).map_err(|_| { @@ -109,7 +109,7 @@ fn eval_ir_binary( })?; let n = a.shr(b); - break 'out Inline::Integer(n); + break 'out Inline::Signed(n); } ir::IrBinaryOp::Lt => break 'out Inline::Bool(a < b), ir::IrBinaryOp::Lte => break 'out Inline::Bool(a <= b), @@ -357,7 +357,7 @@ fn eval_ir_template( match value { BorrowRefRepr::Inline(value) => match value { - Inline::Integer(integer) => { + Inline::Signed(integer) => { write!(buf, "{integer}")?; } Inline::Float(float) => { diff --git a/crates/rune/src/compile/named.rs b/crates/rune/src/compile/named.rs index e895af34d..56f5a167f 100644 --- a/crates/rune/src/compile/named.rs +++ b/crates/rune/src/compile/named.rs @@ -35,6 +35,12 @@ impl Named for i64 { impl InstallWith for i64 {} +impl Named for u64 { + const BASE_NAME: RawStr = RawStr::from_str("u64"); +} + +impl InstallWith for u64 {} + impl Named for f64 { const BASE_NAME: RawStr = RawStr::from_str("f64"); } diff --git a/crates/rune/src/compile/prelude.rs b/crates/rune/src/compile/prelude.rs index 49b27fd4e..bfeef7a97 100644 --- a/crates/rune/src/compile/prelude.rs +++ b/crates/rune/src/compile/prelude.rs @@ -23,6 +23,7 @@ impl Prelude { this.add_prelude("u8", ["u8"])?; this.add_prelude("f64", ["f64"])?; this.add_prelude("i64", ["i64"])?; + this.add_prelude("u64", ["u64"])?; this.add_prelude("char", ["char"])?; this.add_prelude("dbg", ["io", "dbg"])?; this.add_prelude("drop", ["mem", "drop"])?; diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index 16cd8afbc..1d23cfb35 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -620,7 +620,8 @@ fn pat_lit_inst( slot: cx.q.unit.new_static_bytes(hir, bytes)?, out, }, - hir::Lit::Integer(value) => Inst::EqInteger { addr, value, out }, + hir::Lit::Unsigned(value) => Inst::EqUnsigned { addr, value, out }, + hir::Lit::Signed(value) => Inst::EqSigned { addr, value, out }, hir::Lit::Bool(value) => Inst::EqBool { addr, value, out }, _ => return Ok(None), }; @@ -1110,8 +1111,11 @@ fn const_<'a, 'hir>( Inline::Char(v) => { cx.asm.push(Inst::char(v, out), span)?; } - Inline::Integer(v) => { - cx.asm.push(Inst::integer(v, out), span)?; + Inline::Signed(v) => { + cx.asm.push(Inst::signed(v, out), span)?; + } + Inline::Unsigned(v) => { + cx.asm.push(Inst::unsigned(v, out), span)?; } Inline::Float(v) => { cx.asm.push(Inst::float(v, out), span)?; @@ -3131,8 +3135,11 @@ fn lit<'a, 'hir>( hir::Lit::Char(v) => { cx.asm.push(Inst::char(v, out), span)?; } - hir::Lit::Integer(v) => { - cx.asm.push(Inst::integer(v, out), span)?; + hir::Lit::Unsigned(v) => { + cx.asm.push(Inst::unsigned(v, out), span)?; + } + hir::Lit::Signed(v) => { + cx.asm.push(Inst::signed(v, out), span)?; } hir::Lit::Float(v) => { cx.asm.push(Inst::float(v, out), span)?; diff --git a/crates/rune/src/hir/hir.rs b/crates/rune/src/hir/hir.rs index 72a0d74c4..5059452a4 100644 --- a/crates/rune/src/hir/hir.rs +++ b/crates/rune/src/hir/hir.rs @@ -190,7 +190,8 @@ pub(crate) struct Expr<'hir> { #[non_exhaustive] pub(crate) enum Lit<'hir> { Bool(bool), - Integer(i64), + Unsigned(u64), + Signed(i64), Float(f64), Byte(u8), Char(char), diff --git a/crates/rune/src/hir/lowering.rs b/crates/rune/src/hir/lowering.rs index fae7f3d7a..610b6d971 100644 --- a/crates/rune/src/hir/lowering.rs +++ b/crates/rune/src/hir/lowering.rs @@ -644,7 +644,8 @@ fn pat_const_value<'hir>( Inline::Bool(b) => hir::Lit::Bool(b), Inline::Byte(b) => hir::Lit::Byte(b), Inline::Char(ch) => hir::Lit::Char(ch), - Inline::Integer(integer) => hir::Lit::Integer(integer), + Inline::Unsigned(integer) => hir::Lit::Unsigned(integer), + Inline::Signed(integer) => hir::Lit::Signed(integer), _ => { return Err(compile::Error::msg( span, @@ -784,12 +785,19 @@ fn lit<'hir>(cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::Lit) -> compile::Result { + let Some(n) = int.to_u64() else { + return Err(compile::Error::new(ast, ErrorKind::BadUnsignedOutOfBounds)); + }; + + Ok(hir::Lit::Unsigned(n)) + } (ast::NumberValue::Integer(int), _) => { let Some(n) = int.to_i64() else { - return Err(compile::Error::new(ast, ErrorKind::BadNumberOutOfBounds)); + return Err(compile::Error::new(ast, ErrorKind::BadSignedOutOfBounds)); }; - Ok(hir::Lit::Integer(n)) + Ok(hir::Lit::Signed(n)) } } } @@ -849,14 +857,21 @@ fn expr_unary<'hir>( (ast::NumberValue::Float(n), Some(ast::NumberSuffix::Float(..)) | None) => { Ok(hir::ExprKind::Lit(hir::Lit::Float(-n))) } - (ast::NumberValue::Integer(int), Some(ast::NumberSuffix::Int(..)) | None) => { + (ast::NumberValue::Integer(int), Some(ast::NumberSuffix::Unsigned(..))) => { + let Some(n) = int.neg().to_u64() else { + return Err(compile::Error::new(ast, ErrorKind::BadUnsignedOutOfBounds)); + }; + + Ok(hir::ExprKind::Lit(hir::Lit::Unsigned(n))) + } + (ast::NumberValue::Integer(int), Some(ast::NumberSuffix::Signed(..)) | None) => { let Some(n) = int.neg().to_i64() else { - return Err(compile::Error::new(ast, ErrorKind::BadNumberOutOfBounds)); + return Err(compile::Error::new(ast, ErrorKind::BadSignedOutOfBounds)); }; - Ok(hir::ExprKind::Lit(hir::Lit::Integer(n))) + Ok(hir::ExprKind::Lit(hir::Lit::Signed(n))) } - _ => Err(compile::Error::new(ast, ErrorKind::BadNumberOutOfBounds)), + _ => Err(compile::Error::new(ast, ErrorKind::BadSignedOutOfBounds)), } } diff --git a/crates/rune/src/hir/lowering2.rs b/crates/rune/src/hir/lowering2.rs index f7698bcd2..ef71b5d7f 100644 --- a/crates/rune/src/hir/lowering2.rs +++ b/crates/rune/src/hir/lowering2.rs @@ -679,11 +679,11 @@ fn expr_expanded_macro<'hir>( Ok(hir::ExprKind::Lit(hir::Lit::Str(lit))) } query::BuiltInMacro2::Line(line) => { - let Some(n) = line.to_i64() else { - return Err(Error::new(&*p, ErrorKind::BadNumberOutOfBounds)); + let Some(n) = line.to_u64() else { + return Err(Error::new(&*p, ErrorKind::BadUnsignedOutOfBounds)); }; - Ok(hir::ExprKind::Lit(hir::Lit::Integer(n))) + Ok(hir::ExprKind::Lit(hir::Lit::Unsigned(n))) } query::BuiltInMacro2::Format(tree) => expr_format_macro(cx, p, tree), query::BuiltInMacro2::Template(tree, literal) => { @@ -2566,7 +2566,8 @@ fn pat_const_value<'hir>( Inline::Bool(b) => hir::Lit::Bool(b), Inline::Byte(b) => hir::Lit::Byte(b), Inline::Char(ch) => hir::Lit::Char(ch), - Inline::Integer(value) => hir::Lit::Integer(value), + Inline::Unsigned(value) => hir::Lit::Unsigned(value), + Inline::Signed(value) => hir::Lit::Signed(value), _ => return Err(Error::msg(span, "Unsupported constant value in pattern")), }, ConstValueKind::String(ref string) => hir::Lit::Str(alloc_str!(string.as_ref())), @@ -2760,14 +2761,23 @@ fn lit<'hir>(cx: &mut Ctxt<'hir, '_, '_>, p: &mut Stream<'_>) -> Result { + let int = if neg { int.neg() } else { int }; + + let Some(n) = int.to_u64() else { + return Err(Error::new(lit, ErrorKind::BadUnsignedOutOfBounds)); + }; + + Ok(hir::Lit::Unsigned(n)) + } (ast::NumberValue::Integer(int), _) => { let int = if neg { int.neg() } else { int }; let Some(n) = int.to_i64() else { - return Err(Error::new(lit, ErrorKind::BadNumberOutOfBounds)); + return Err(Error::new(lit, ErrorKind::BadSignedOutOfBounds)); }; - Ok(hir::Lit::Integer(n)) + Ok(hir::Lit::Signed(n)) } } } diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index b449ad3d6..c03746ab7 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -255,7 +255,8 @@ pub mod query; pub mod runtime; #[doc(inline)] pub use self::runtime::{ - from_value, to_value, FromValue, ToConstValue, ToValue, TypeHash, Unit, Value, Vm, + from_const_value, from_value, to_const_value, to_value, FromValue, ToConstValue, ToValue, + TypeHash, Unit, Value, Vm, }; mod shared; diff --git a/crates/rune/src/macros/format_args.rs b/crates/rune/src/macros/format_args.rs index f0de49298..d616ceb76 100644 --- a/crates/rune/src/macros/format_args.rs +++ b/crates/rune/src/macros/format_args.rs @@ -573,7 +573,7 @@ fn expand_format_spec<'a>( let value = cx.eval(expr)?; let number = match value.as_inline().with_span(expr)? { - Some(Inline::Integer(n)) => n.to_usize(), + Some(Inline::Signed(n)) => n.to_usize(), _ => None, }; diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index a3a744c6e..2bacd21e7 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -516,7 +516,7 @@ impl Module { /// ``` pub fn constant(&mut self, name: N, value: V) -> ModuleConstantBuilder<'_, N, V> where - V: ToConstValue, + V: TypeHash + TypeOf + ToConstValue, { ModuleConstantBuilder { module: self, @@ -536,7 +536,11 @@ impl Module { { let item = self.item.join([name])?; let hash = Hash::type_hash(&item); - let value = value.to_const_value()?; + + let value = match value.to_const_value() { + Ok(value) => value, + Err(error) => return Err(ContextError::InvalidConstValue { item, error }), + }; if !self.names.try_insert(Name::Item(hash))? { return Err(ContextError::ConflictingConstantName { item, hash }); @@ -570,7 +574,16 @@ impl Module { where V: TypeHash + TypeOf + ToConstValue, { - let value = value.to_const_value()?; + let value = match value.to_const_value() { + Ok(value) => value, + Err(error) => { + return Err(ContextError::InvalidAssociatedConstValue { + container: associated.container_type_info, + kind: associated.name.kind, + error, + }) + } + }; self.insert_associated_name(&associated)?; diff --git a/crates/rune/src/modules/core.rs b/crates/rune/src/modules/core.rs index 8eae237fe..8b7aa5fc1 100644 --- a/crates/rune/src/modules/core.rs +++ b/crates/rune/src/modules/core.rs @@ -21,7 +21,8 @@ pub fn module() -> Result { .docs(["The primitive character type."])?; module.ty::()?.docs(["The primitive byte type."])?; module.ty::()?.docs(["The primitive float type."])?; - module.ty::()?.docs(["The primitive integer type."])?; + module.ty::()?.docs(["The signed integer type."])?; + module.ty::()?.docs(["The unsigned integer type."])?; module.function_meta(panic)?; module.function_meta(is_readable)?; diff --git a/crates/rune/src/modules/i64.rs b/crates/rune/src/modules/i64.rs index 0c40f0a1e..2787f7be3 100644 --- a/crates/rune/src/modules/i64.rs +++ b/crates/rune/src/modules/i64.rs @@ -9,633 +9,14 @@ use crate::alloc::string::TryToString; use crate::runtime::{VmErrorKind, VmResult}; use crate::{ContextError, Module}; -/// Integers. +/// Signed integers. /// -/// This provides methods for computing over and parsing 64-bit integers. +/// This provides methods for computing over and parsing 64-bit signed integers. #[rune::module(::std::i64)] pub fn module() -> Result { let mut m = Module::from_meta(self::module_meta)?; - - m.function("parse", parse).build()?; - m.function_meta(to_float)?; - - m.function_meta(max)?; - m.function_meta(min)?; - m.function_meta(abs)?; - m.function_meta(pow)?; - - m.function_meta(checked_add)?; - m.function_meta(checked_sub)?; - m.function_meta(checked_div)?; - m.function_meta(checked_mul)?; - m.function_meta(checked_rem)?; - - m.function_meta(wrapping_add)?; - m.function_meta(wrapping_sub)?; - m.function_meta(wrapping_div)?; - m.function_meta(wrapping_mul)?; - m.function_meta(wrapping_rem)?; - - m.function_meta(saturating_add)?; - m.function_meta(saturating_sub)?; - m.function_meta(saturating_mul)?; - m.function_meta(saturating_abs)?; - m.function_meta(saturating_pow)?; - - m.function_meta(signum)?; - m.function_meta(is_positive)?; - m.function_meta(is_negative)?; - m.function_meta(to_string)?; - - m.function_meta(clone__meta)?; - m.implement_trait::(rune::item!(::std::clone::Clone))?; - - m.function_meta(partial_eq__meta)?; - m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; - - m.function_meta(eq__meta)?; - m.implement_trait::(rune::item!(::std::cmp::Eq))?; - - m.function_meta(partial_cmp__meta)?; - m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; - - m.function_meta(cmp__meta)?; - m.implement_trait::(rune::item!(::std::cmp::Ord))?; - - m.constant("MIN", i64::MIN).build()?.docs(docstring! { - /// The smallest value that can be represented by this integer type - /// (−263). - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```rune - /// assert_eq!(i64::MIN, -9223372036854775808); - /// ``` - })?; - - m.constant("MAX", i64::MAX).build()?.docs(docstring! { - /// The largest value that can be represented by this integer type - /// (263 − 1). - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```rune - /// assert_eq!(i64::MAX, 9223372036854775807); - /// ``` - })?; - + signed!(m, i64); Ok(m) } -/// Parse an `int`. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!(i64::parse("10")?, 10); -/// ``` -fn parse(s: &str) -> Result { - str::parse::(s) -} - -/// Convert an `int` to a `float`. -/// -/// # Examples -/// -/// ```rune -/// assert!(10.to::() is f64); -/// ``` -#[rune::function(instance, path = to::)] -#[inline] -fn to_float(value: i64) -> f64 { - value as f64 -} - -/// Compares and returns the maximum of two values. -/// -/// Returns the second argument if the comparison determines them to be equal. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!(2, 1.max(2)); -/// assert_eq!(2, 2.max(2)); -/// ``` -#[rune::function(instance)] -#[inline] -fn max(this: i64, other: i64) -> i64 { - i64::max(this, other) -} - -/// Compares and returns the minimum of two values. -/// -/// Returns the first argument if the comparison determines them to be equal. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!(1, 1.min(2)); -/// assert_eq!(2, 2.min(2)); -/// ``` -#[rune::function(instance)] -#[inline] -fn min(this: i64, other: i64) -> i64 { - i64::min(this, other) -} - -/// Computes the absolute value of `self`. -/// -/// # Overflow behavior -/// -/// The absolute value of `i64::MIN` cannot be represented as an `int`, and -/// attempting to calculate it will cause an overflow. This means that such code -/// will wrap to `i64::MIN` without a panic. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(10.abs(), 10); -/// assert_eq!((-10).abs(), 10); -/// ``` -#[rune::function(instance)] -#[inline] -fn abs(this: i64) -> i64 { - i64::wrapping_abs(this) -} - -/// Raises self to the power of `exp`, using exponentiation by squaring. -/// -/// # Overflow behavior -/// -/// This function will wrap on overflow. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let x = 2; -/// -/// assert_eq!(x.pow(5), 32); -/// ``` -#[rune::function(instance)] -#[inline] -fn pow(this: i64, pow: u32) -> i64 { - i64::wrapping_pow(this, pow) -} - -/// Checked integer addition. Computes `self + rhs`, returning `None` if -/// overflow occurred. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!((i64::MAX - 2).checked_add(1), Some(i64::MAX - 1)); -/// assert_eq!((i64::MAX - 2).checked_add(3), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn checked_add(this: i64, rhs: i64) -> Option { - i64::checked_add(this, rhs) -} - -/// Checked integer subtraction. Computes `self - rhs`, returning `None` if -/// overflow occurred. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!((i64::MIN + 2).checked_sub(1), Some(i64::MIN + 1)); -/// assert_eq!((i64::MIN + 2).checked_sub(3), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn checked_sub(this: i64, rhs: i64) -> Option { - i64::checked_sub(this, rhs) -} - -/// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == -/// 0` or the division results in overflow. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!((i64::MIN + 1).checked_div(-1), Some(i64::MAX)); -/// assert_eq!(i64::MIN.checked_div(-1), None); -/// assert_eq!((1).checked_div(0), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn checked_div(this: i64, rhs: i64) -> Option { - i64::checked_div(this, rhs) -} - -/// Checked integer multiplication. Computes `self * rhs`, returning `None` if -/// overflow occurred. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(i64::MAX.checked_mul(1), Some(i64::MAX)); -/// assert_eq!(i64::MAX.checked_mul(2), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn checked_mul(this: i64, rhs: i64) -> Option { - i64::checked_mul(this, rhs) -} - -/// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs -/// == 0` or the division results in overflow. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(5.checked_rem(2), Some(1)); -/// assert_eq!(5.checked_rem(0), None); -/// assert_eq!(i64::MIN.checked_rem(-1), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn checked_rem(this: i64, rhs: i64) -> Option { - i64::checked_rem(this, rhs) -} - -/// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the -/// boundary of the type. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.wrapping_add(27), 127); -/// assert_eq!(i64::MAX.wrapping_add(2), i64::MIN + 1); -/// ``` -#[rune::function(instance)] -#[inline] -fn wrapping_add(this: i64, rhs: i64) -> i64 { - i64::wrapping_add(this, rhs) -} - -/// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at -/// the boundary of the type. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -// assert_eq!(0.wrapping_sub(127), -127); -// assert_eq!((-2).wrapping_sub(i64::MAX), i64::MAX); -/// ``` -#[rune::function(instance)] -#[inline] -fn wrapping_sub(this: i64, rhs: i64) -> i64 { - i64::wrapping_sub(this, rhs) -} - -/// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the -/// boundary of the type. -/// -/// The only case where such wrapping can occur is when one divides `MIN / -1` -/// on a signed type (where `MIN` is the negative minimal value for the type); -/// this is equivalent to `-MIN`, a positive value that is too large to -/// represent in the type. In such a case, this function returns `MIN` itself. -/// -/// # Panics -/// -/// This function will panic if `rhs` is 0. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.wrapping_div(10), 10); -/// ``` -#[rune::function(instance)] -#[inline] -fn wrapping_div(this: i64, rhs: i64) -> VmResult { - if rhs == 0 { - return VmResult::err(VmErrorKind::DivideByZero); - } - - VmResult::Ok(i64::wrapping_div(this, rhs)) -} - -/// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at -/// the boundary of the type. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(10.wrapping_mul(12), 120); -/// ``` -#[rune::function(instance)] -#[inline] -fn wrapping_mul(this: i64, rhs: i64) -> i64 { - i64::wrapping_mul(this, rhs) -} - -/// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the -/// boundary of the type. -/// -/// Such wrap-around never actually occurs mathematically; implementation -/// artifacts make `x % y` invalid for `MIN / -1` on a signed type (where `MIN` -/// is the negative minimal value). In such a case, this function returns `0`. -/// -/// # Panics -/// -/// This function will panic if `rhs` is 0. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.wrapping_rem(10), 0); -/// ``` -#[rune::function(instance)] -#[inline] -fn wrapping_rem(this: i64, rhs: i64) -> VmResult { - if rhs == 0 { - return VmResult::err(VmErrorKind::DivideByZero); - } - - VmResult::Ok(i64::wrapping_rem(this, rhs)) -} - -/// Saturating integer addition. Computes `self + rhs`, saturating at the -/// numeric bounds instead of overflowing. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.saturating_add(1), 101); -/// assert_eq!(i64::MAX.saturating_add(100), i64::MAX); -/// assert_eq!(i64::MIN.saturating_add(-1), i64::MIN); -/// ``` -#[rune::function(instance)] -#[inline] -fn saturating_add(this: i64, rhs: i64) -> i64 { - i64::saturating_add(this, rhs) -} - -/// Saturating integer subtraction. Computes `self - rhs`, saturating at the -/// numeric bounds instead of overflowing. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.saturating_sub(127), -27); -/// assert_eq!(i64::MIN.saturating_sub(100), i64::MIN); -/// assert_eq!(i64::MAX.saturating_sub(-1), i64::MAX); -/// ``` -#[rune::function(instance)] -#[inline] -fn saturating_sub(this: i64, rhs: i64) -> i64 { - i64::saturating_sub(this, rhs) -} - -/// Saturating integer multiplication. Computes `self * rhs`, saturating at the -/// numeric bounds instead of overflowing. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(10.saturating_mul(12), 120); -/// assert_eq!(i64::MAX.saturating_mul(10), i64::MAX); -/// assert_eq!(i64::MIN.saturating_mul(10), i64::MIN); -/// ``` -#[rune::function(instance)] -#[inline] -fn saturating_mul(this: i64, rhs: i64) -> i64 { - i64::saturating_mul(this, rhs) -} - -/// Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self -/// == MIN` instead of overflowing. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(100.saturating_abs(), 100); -/// assert_eq!((-100).saturating_abs(), 100); -/// assert_eq!(i64::MIN.saturating_abs(), i64::MAX); -/// assert_eq!((i64::MIN + 1).saturating_abs(), i64::MAX); -/// ``` -#[rune::function(instance)] -#[inline] -fn saturating_abs(this: i64) -> i64 { - i64::saturating_abs(this) -} - -/// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at -/// the numeric bounds instead of overflowing. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!((-4).saturating_pow(3), -64); -/// assert_eq!(i64::MIN.saturating_pow(2), i64::MAX); -/// assert_eq!(i64::MIN.saturating_pow(3), i64::MIN); -/// ``` -#[rune::function(instance)] -#[inline] -fn saturating_pow(this: i64, rhs: u32) -> i64 { - i64::saturating_pow(this, rhs) -} - -/// Returns a number representing sign of `self`. -/// -/// - `0` if the number is zero -/// - `1` if the number is positive -/// - `-1` if the number is negative -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!(10.signum(), 1); -/// assert_eq!(0.signum(), 0); -/// assert_eq!((-10).signum(), -1); -/// ``` -#[rune::function(instance)] -#[inline] -fn signum(this: i64) -> i64 { - i64::signum(this) -} - -/// Returns `true` if `self` is positive and `false` if the number is zero or -/// negative. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert!(10.is_positive()); -/// assert!(!(-10).is_positive()); -/// ``` -#[rune::function(instance)] -#[inline] -fn is_positive(this: i64) -> bool { - i64::is_positive(this) -} - -/// Returns `true` if `self` is negative and `false` if the number is zero or -/// positive. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert!((-10).is_negative()); -/// assert!(!10.is_negative()); -/// ``` -#[rune::function(instance)] -#[inline] -fn is_negative(this: i64) -> bool { - i64::is_negative(this) -} - -/// Clone a `i64`. -/// -/// Note that since the type is copy, cloning has the same effect as assigning -/// it. -/// -/// # Examples -/// -/// ```rune -/// let a = 5; -/// let b = a; -/// let c = a.clone(); -/// -/// a += 1; -/// -/// assert_eq!(a, 6); -/// assert_eq!(b, 5); -/// assert_eq!(c, 5); -/// ``` -#[rune::function(keep, instance, protocol = CLONE)] -#[inline] -fn clone(this: i64) -> i64 { - this -} - -/// Test two integers for partial equality. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::partial_eq; -/// -/// assert_eq!(partial_eq(5, 5), true); -/// assert_eq!(partial_eq(5, 10), false); -/// assert_eq!(partial_eq(10, 5), false); -/// ``` -#[rune::function(keep, instance, protocol = PARTIAL_EQ)] -#[inline] -fn partial_eq(this: i64, rhs: i64) -> bool { - this.eq(&rhs) -} - -/// Test two integers for total equality. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::eq; -/// -/// assert_eq!(eq(5, 5), true); -/// assert_eq!(eq(5, 10), false); -/// assert_eq!(eq(10, 5), false); -/// ``` -#[rune::function(keep, instance, protocol = EQ)] -#[inline] -fn eq(this: i64, rhs: i64) -> bool { - this.eq(&rhs) -} - -/// Perform a partial ordered comparison between two integers. -/// -/// # Examples -/// -/// ```rune -/// use std::cmp::Ordering; -/// use std::ops::partial_cmp; -/// -/// assert_eq!(partial_cmp(5, 10), Some(Ordering::Less)); -/// assert_eq!(partial_cmp(10, 5), Some(Ordering::Greater)); -/// assert_eq!(partial_cmp(5, 5), Some(Ordering::Equal)); -/// ``` -#[rune::function(keep, instance, protocol = PARTIAL_CMP)] -#[inline] -fn partial_cmp(this: i64, rhs: i64) -> Option { - this.partial_cmp(&rhs) -} - -/// Perform a totally ordered comparison between two integers. -/// -/// # Examples -/// -/// ```rune -/// use std::cmp::Ordering; -/// use std::ops::cmp; -/// -/// assert_eq!(cmp(5, 10), Ordering::Less); -/// assert_eq!(cmp(10, 5), Ordering::Greater); -/// assert_eq!(cmp(5, 5), Ordering::Equal); -/// ``` -#[rune::function(keep, instance, protocol = CMP)] -#[inline] -fn cmp(this: i64, rhs: i64) -> Ordering { - this.cmp(&rhs) -} - -/// Returns the number as a string. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// assert_eq!((-10).to_string(), "-10"); -/// assert_eq!(10.to_string(), "10"); -/// ``` -#[rune::function(instance)] -#[inline] -fn to_string(this: i64) -> VmResult { - VmResult::Ok(vm_try!(this.try_to_string())) -} +signed_fns!(i64); diff --git a/crates/rune/src/modules/inner_macros.rs b/crates/rune/src/modules/inner_macros.rs new file mode 100644 index 000000000..d9e477859 --- /dev/null +++ b/crates/rune/src/modules/inner_macros.rs @@ -0,0 +1,641 @@ +macro_rules! unsigned { + ($m:ident, $ty:ty) => { + $m.function("parse", parse).build()?; + $m.function_meta(to_float)?; + + $m.function_meta(max)?; + $m.function_meta(min)?; + $m.function_meta(pow)?; + + $m.function_meta(checked_add)?; + $m.function_meta(checked_sub)?; + $m.function_meta(checked_div)?; + $m.function_meta(checked_mul)?; + $m.function_meta(checked_rem)?; + + $m.function_meta(wrapping_add)?; + $m.function_meta(wrapping_sub)?; + $m.function_meta(wrapping_div)?; + $m.function_meta(wrapping_mul)?; + $m.function_meta(wrapping_rem)?; + + $m.function_meta(saturating_add)?; + $m.function_meta(saturating_sub)?; + $m.function_meta(saturating_mul)?; + $m.function_meta(saturating_pow)?; + + $m.function_meta(to_string)?; + + $m.function_meta(clone__meta)?; + $m.implement_trait::<$ty>(rune::item!(::std::clone::Clone))?; + + $m.function_meta(partial_eq__meta)?; + $m.implement_trait::<$ty>(rune::item!(::std::cmp::PartialEq))?; + + $m.function_meta(eq__meta)?; + $m.implement_trait::<$ty>(rune::item!(::std::cmp::Eq))?; + + $m.function_meta(partial_cmp__meta)?; + $m.implement_trait::<$ty>(rune::item!(::std::cmp::PartialOrd))?; + + $m.function_meta(cmp__meta)?; + $m.implement_trait::<$ty>(rune::item!(::std::cmp::Ord))?; + + $m.constant("MIN", <$ty>::MIN).build()?.docs(docstring! { + /// The smallest value that can be represented by this integer type + /// (−263). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN, -9223372036854775808);")] + /// ``` + })?; + + $m.constant("MAX", <$ty>::MAX).build()?.docs(docstring! { + /// The largest value that can be represented by this integer type + /// (263 − 1). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX, 9223372036854775807);")] + /// ``` + })?; + }; +} + +macro_rules! unsigned_fns { + ($ty:ty) => { + #[doc = concat!(" Parse an `", stringify!($ty), "`.")] + /// + /// # Examples + /// + /// ```rune + #[doc = concat!(" assert_eq!(", stringify!($ty), "::parse(\"10\")?, 10);")] + /// ``` + fn parse(s: &str) -> Result<$ty, ParseIntError> { + str::parse::<$ty>(s) + } + + #[doc = concat!(" Converts an `", stringify!($ty), "` to a `f64`.")] + /// + /// # Examples + /// + /// ```rune + /// assert!(10.to::() is f64); + /// ``` + #[rune::function(instance, path = to::)] + #[inline] + fn to_float(value: $ty) -> f64 { + value as f64 + } + + /// Compares and returns the maximum of two values. + /// + /// Returns the second argument if the comparison determines them to be equal. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!(2, 1.max(2)); + /// assert_eq!(2, 2.max(2)); + /// ``` + #[rune::function(instance)] + #[inline] + fn max(this: $ty, other: $ty) -> $ty { + <$ty>::max(this, other) + } + + /// Compares and returns the minimum of two values. + /// + /// Returns the first argument if the comparison determines them to be equal. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!(1, 1.min(2)); + /// assert_eq!(2, 2.min(2)); + /// ``` + #[rune::function(instance)] + #[inline] + fn min(this: $ty, other: $ty) -> $ty { + <$ty>::min(this, other) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Overflow behavior + /// + /// This function will wrap on overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let x = 2; + /// + /// assert_eq!(x.pow(5), 32); + /// ``` + #[rune::function(instance)] + #[inline] + fn pow(this: $ty, pow: u32) -> $ty { + <$ty>::wrapping_pow(this, pow) + } + + /// Checked integer addition. Computes `self + rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MAX - 2).checked_add(1), Some(", stringify!($ty), "::MAX - 1));")] + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MAX - 2).checked_add(3), None);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn checked_add(this: $ty, rhs: $ty) -> Option<$ty> { + <$ty>::checked_add(this, rhs) + } + + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MIN + 2).checked_sub(1), Some(", stringify!($ty), "::MIN + 1));")] + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MIN + 2).checked_sub(3), None);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn checked_sub(this: $ty, rhs: $ty) -> Option<$ty> { + <$ty>::checked_sub(this, rhs) + } + + /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == + /// 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MIN + 1).checked_div(-1), Some(", stringify!($ty), "::MAX));")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.checked_div(-1), None);")] + /// assert_eq!((1).checked_div(0), None); + /// ``` + #[rune::function(instance)] + #[inline] + fn checked_div(this: $ty, rhs: $ty) -> Option<$ty> { + <$ty>::checked_div(this, rhs) + } + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.checked_mul(1), Some(", stringify!($ty), "::MAX));")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.checked_mul(2), None);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn checked_mul(this: $ty, rhs: $ty) -> Option<$ty> { + <$ty>::checked_mul(this, rhs) + } + + /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs + /// == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(5.checked_rem(2), Some(1)); + /// assert_eq!(5.checked_rem(0), None); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.checked_rem(-1), None);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn checked_rem(this: $ty, rhs: $ty) -> Option<$ty> { + <$ty>::checked_rem(this, rhs) + } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the + /// boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.wrapping_add(27), 127); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.wrapping_add(2), ", stringify!($ty), "::MIN + 1);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn wrapping_add(this: $ty, rhs: $ty) -> $ty { + <$ty>::wrapping_add(this, rhs) + } + + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(0.wrapping_sub(127), -127); + #[doc = concat!(" assert_eq!((-2).wrapping_sub(", stringify!($ty), "::MAX), ", stringify!($ty), "::MAX);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn wrapping_sub(this: $ty, rhs: $ty) -> $ty { + <$ty>::wrapping_sub(this, rhs) + } + + /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the + /// boundary of the type. + /// + /// The only case where such wrapping can occur is when one divides `MIN / -1` + /// on a signed type (where `MIN` is the negative minimal value for the type); + /// this is equivalent to `-MIN`, a positive value that is too large to + /// represent in the type. In such a case, this function returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.wrapping_div(10), 10); + /// ``` + #[rune::function(instance)] + #[inline] + fn wrapping_div(this: $ty, rhs: $ty) -> VmResult<$ty> { + if rhs == 0 { + return VmResult::err(VmErrorKind::DivideByZero); + } + + VmResult::Ok(<$ty>::wrapping_div(this, rhs)) + } + + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(10.wrapping_mul(12), 120); + /// ``` + #[rune::function(instance)] + #[inline] + fn wrapping_mul(this: $ty, rhs: $ty) -> $ty { + <$ty>::wrapping_mul(this, rhs) + } + + /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the + /// boundary of the type. + /// + /// Such wrap-around never actually occurs mathematically; implementation + /// artifacts make `x % y` invalid for `MIN / -1` on a signed type (where `MIN` + /// is the negative minimal value). In such a case, this function returns `0`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.wrapping_rem(10), 0); + /// ``` + #[rune::function(instance)] + #[inline] + fn wrapping_rem(this: $ty, rhs: $ty) -> VmResult<$ty> { + if rhs == 0 { + return VmResult::err(VmErrorKind::DivideByZero); + } + + VmResult::Ok(<$ty>::wrapping_rem(this, rhs)) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.saturating_add(1), 101); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.saturating_add(100), ", stringify!($ty), "::MAX);")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_add(-1), ", stringify!($ty), "::MIN);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn saturating_add(this: $ty, rhs: $ty) -> $ty { + <$ty>::saturating_add(this, rhs) + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.saturating_sub(127), -27); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_sub(100), ", stringify!($ty), "::MIN);")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.saturating_sub(-1), ", stringify!($ty), "::MAX);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn saturating_sub(this: $ty, rhs: $ty) -> $ty { + <$ty>::saturating_sub(this, rhs) + } + + /// Saturating integer multiplication. Computes `self * rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(10.saturating_mul(12), 120); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MAX.saturating_mul(10), ", stringify!($ty), "::MAX);")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_mul(10), ", stringify!($ty), "::MIN);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn saturating_mul(this: $ty, rhs: $ty) -> $ty { + <$ty>::saturating_mul(this, rhs) + } + + /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!((-4).saturating_pow(3), -64); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_pow(2), ", stringify!($ty), "::MAX);")] + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_pow(3), ", stringify!($ty), "::MIN);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn saturating_pow(this: $ty, rhs: u32) -> $ty { + <$ty>::saturating_pow(this, rhs) + } + + #[doc = concat!(" Clone a `", stringify!($ty), "`.")] + /// + /// Note that since the type is copy, cloning has the same effect as assigning + /// it. + /// + /// # Examples + /// + /// ```rune + /// let a = 5; + /// let b = a; + /// let c = a.clone(); + /// + /// a += 1; + /// + /// assert_eq!(a, 6); + /// assert_eq!(b, 5); + /// assert_eq!(c, 5); + /// ``` + #[rune::function(keep, instance, protocol = CLONE)] + #[inline] + fn clone(this: $ty) -> $ty { + this + } + + /// Test two integers for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// + /// assert_eq!(partial_eq(5, 5), true); + /// assert_eq!(partial_eq(5, 10), false); + /// assert_eq!(partial_eq(10, 5), false); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_EQ)] + #[inline] + fn partial_eq(this: $ty, rhs: $ty) -> bool { + this.eq(&rhs) + } + + /// Test two integers for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// assert_eq!(eq(5, 5), true); + /// assert_eq!(eq(5, 10), false); + /// assert_eq!(eq(10, 5), false); + /// ``` + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(this: $ty, rhs: $ty) -> bool { + this.eq(&rhs) + } + + /// Perform a partial ordered comparison between two integers. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::partial_cmp; + /// + /// assert_eq!(partial_cmp(5, 10), Some(Ordering::Less)); + /// assert_eq!(partial_cmp(10, 5), Some(Ordering::Greater)); + /// assert_eq!(partial_cmp(5, 5), Some(Ordering::Equal)); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_CMP)] + #[inline] + fn partial_cmp(this: $ty, rhs: $ty) -> Option { + this.partial_cmp(&rhs) + } + + /// Perform a totally ordered comparison between two integers. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// + /// assert_eq!(cmp(5, 10), Ordering::Less); + /// assert_eq!(cmp(10, 5), Ordering::Greater); + /// assert_eq!(cmp(5, 5), Ordering::Equal); + /// ``` + #[rune::function(keep, instance, protocol = CMP)] + #[inline] + fn cmp(this: $ty, rhs: $ty) -> Ordering { + this.cmp(&rhs) + } + + /// Returns the number as a string. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!((-10).to_string(), "-10"); + /// assert_eq!(10.to_string(), "10"); + /// ``` + #[rune::function(instance)] + #[inline] + fn to_string(this: $ty) -> VmResult { + VmResult::Ok(vm_try!(this.try_to_string())) + } + }; +} + +macro_rules! signed { + ($m:ident, $ty:ty) => { + unsigned!($m, $ty); + + $m.function_meta(abs)?; + $m.function_meta(saturating_abs)?; + $m.function_meta(signum)?; + $m.function_meta(is_positive)?; + $m.function_meta(is_negative)?; + }; +} + +macro_rules! signed_fns { + ($ty:ty) => { + unsigned_fns!($ty); + + /// Computes the absolute value of `self`. + /// + /// # Overflow behavior + /// + #[doc = concat!(" The absolute value of `", stringify!($ty), "::MIN` cannot be represented as an `int`,")] + /// and attempting to calculate it will cause an overflow. This means + #[doc = concat!(" that such code will wrap to `", stringify!($ty), "::MIN` without a panic.")] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(10.abs(), 10); + /// assert_eq!((-10).abs(), 10); + /// ``` + #[rune::function(instance)] + #[inline] + fn abs(this: $ty) -> $ty { + <$ty>::wrapping_abs(this) + } + + /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self + /// == MIN` instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(100.saturating_abs(), 100); + /// assert_eq!((-100).saturating_abs(), 100); + #[doc = concat!(" assert_eq!(", stringify!($ty), "::MIN.saturating_abs(), ", stringify!($ty), "::MAX);")] + #[doc = concat!(" assert_eq!((", stringify!($ty), "::MIN + 1).saturating_abs(), ", stringify!($ty), "::MAX);")] + /// ``` + #[rune::function(instance)] + #[inline] + fn saturating_abs(this: $ty) -> $ty { + <$ty>::saturating_abs(this) + } + + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(10.signum(), 1); + /// assert_eq!(0.signum(), 0); + /// assert_eq!((-10).signum(), -1); + /// ``` + #[rune::function(instance)] + #[inline] + fn signum(this: $ty) -> $ty { + <$ty>::signum(this) + } + + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert!(10.is_positive()); + /// assert!(!(-10).is_positive()); + /// ``` + #[rune::function(instance)] + #[inline] + fn is_positive(this: $ty) -> bool { + <$ty>::is_positive(this) + } + + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert!((-10).is_negative()); + /// assert!(!10.is_negative()); + /// ``` + #[rune::function(instance)] + #[inline] + fn is_negative(this: $ty) -> bool { + <$ty>::is_negative(this) + } + } +} diff --git a/crates/rune/src/modules/iter.rs b/crates/rune/src/modules/iter.rs index 82263832b..70f9b69c8 100644 --- a/crates/rune/src/modules/iter.rs +++ b/crates/rune/src/modules/iter.rs @@ -150,7 +150,7 @@ pub fn module() -> Result { /// // a finite range knows exactly how many times it will iterate /// let five = (0..5).iter(); /// - /// assert_eq!(5, five.len()); + /// assert_eq!(five.len(), 5); /// ``` })?; @@ -188,9 +188,9 @@ pub fn module() -> Result { /// // a finite range knows exactly how many times it will iterate /// let range = (0..5).iter(); /// - /// assert_eq!(5, range.len()); + /// assert_eq!(range.len(), 5); /// let _ = range.next(); - /// assert_eq!(4, range.len()); + /// assert_eq!(range.len(), 4); /// ``` })?; } @@ -681,9 +681,9 @@ pub fn module() -> Result { /// let a = [1, 2, 3]; /// let iter = a.iter(); /// - /// assert_eq!((3, Some(3)), iter.size_hint()); + /// assert_eq!(iter.size_hint(), (3u64, Some(3))); /// let _ = iter.next(); - /// assert_eq!((2, Some(2)), iter.size_hint()); + /// assert_eq!(iter.size_hint(), (2u64, Some(2))); /// ``` /// /// A more complex example: @@ -694,13 +694,13 @@ pub fn module() -> Result { /// /// // We might iterate from zero to ten times. Knowing that it's five /// // exactly wouldn't be possible without executing filter(). - /// assert_eq!((0, Some(10)), iter.size_hint()); + /// assert_eq!(iter.size_hint(), (0, Some(10))); /// /// // Let's add five more numbers with chain() /// let iter = (0..10).iter().filter(|x| x % 2 == 0).chain(15..20); /// /// // now both bounds are increased by five - /// assert_eq!((5, Some(15)), iter.size_hint()); + /// assert_eq!(iter.size_hint(), (5, Some(15))); /// ``` /// /// Returning `None` for an upper bound: @@ -710,7 +710,7 @@ pub fn module() -> Result { /// // and the maximum possible lower bound /// let iter = (0..).iter(); /// - /// assert_eq!((i64::MAX, None), iter.size_hint()); + /// assert_eq!(iter.size_hint(), (u64::MAX, None)); /// ``` })?; @@ -1094,9 +1094,9 @@ pub fn module() -> Result { /// /// let iter = a.iter().enumerate(); /// - /// assert_eq!(iter.next(), Some((0, 'a'))); - /// assert_eq!(iter.next(), Some((1, 'b'))); - /// assert_eq!(iter.next(), Some((2, 'c'))); + /// assert_eq!(iter.next(), Some((0u64, 'a'))); + /// assert_eq!(iter.next(), Some((1u64, 'b'))); + /// assert_eq!(iter.next(), Some((2u64, 'c'))); /// assert_eq!(iter.next(), None); /// ``` })?; @@ -2488,6 +2488,21 @@ impl CheckedOps for i64 { } } +impl CheckedOps for u64 { + const ONE: Self = 1; + const ZERO: Self = 0; + + #[inline] + fn checked_add(self, value: Self) -> Option { + u64::checked_add(self, value) + } + + #[inline] + fn checked_mul(self, value: Self) -> Option { + u64::checked_mul(self, value) + } +} + impl CheckedOps for f64 { const ONE: Self = 1.0; const ZERO: Self = 0.0; diff --git a/crates/rune/src/modules/mod.rs b/crates/rune/src/modules/mod.rs index 4dc6d917c..0297686b2 100644 --- a/crates/rune/src/modules/mod.rs +++ b/crates/rune/src/modules/mod.rs @@ -11,6 +11,9 @@ // // Copyright 2014-2024 The Rust Project Developers +#[macro_use] +mod inner_macros; + pub mod any; pub mod bytes; #[cfg(feature = "capture-io")] @@ -42,4 +45,5 @@ pub mod stream; pub mod string; pub mod test; pub mod tuple; +pub mod u64; pub mod vec; diff --git a/crates/rune/src/modules/ops.rs b/crates/rune/src/modules/ops.rs index b51055214..8a086139f 100644 --- a/crates/rune/src/modules/ops.rs +++ b/crates/rune/src/modules/ops.rs @@ -36,6 +36,11 @@ pub fn module() -> Result { m.function_meta($ty::::size_hint__meta)?; m.implement_trait::<$ty>(rune::item!(::std::iter::Iterator))?; + m.ty::<$ty>()?; + m.function_meta($ty::::next__meta)?; + m.function_meta($ty::::size_hint__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::Iterator))?; + m.ty::<$ty>()?; m.function_meta($ty::::next__meta)?; m.function_meta($ty::::size_hint__meta)?; @@ -58,6 +63,12 @@ pub fn module() -> Result { m.function_meta($ty::::len__meta)?; m.implement_trait::<$ty>(rune::item!(::std::iter::ExactSizeIterator))?; + m.function_meta($ty::::next_back__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.function_meta($ty::::len__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::ExactSizeIterator))?; + m.function_meta($ty::::next_back__meta)?; m.implement_trait::<$ty>(rune::item!(::std::iter::DoubleEndedIterator))?; }; @@ -333,7 +344,7 @@ fn cmp(lhs: Value, rhs: Value) -> VmResult { /// assert_eq!(hash([1, 2]), hash((1, 2))); /// ``` #[rune::function] -fn hash(value: Value) -> VmResult { +fn hash(value: Value) -> VmResult { let state = STATE.get_or_init(RandomState::new); let mut hasher = Hasher::new_with(state); @@ -343,5 +354,5 @@ fn hash(value: Value) -> VmResult { &mut EnvProtocolCaller )); - VmResult::Ok(hasher.finish() as i64) + VmResult::Ok(hasher.finish()) } diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index c27acaa40..92d929a39 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -222,7 +222,7 @@ fn expect(result: Result, message: Value) -> VmResult { /// } /// /// assert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4)); -/// assert_eq!(Ok(i64::MAX).and_then(sq_then_to_string), Err("overflowed")); +/// assert_eq!(Ok(u64::MAX).and_then(sq_then_to_string), Err("overflowed")); /// assert_eq!(Err("not a number").and_then(sq_then_to_string), Err("not a number")); /// ``` #[rune::function(instance)] diff --git a/crates/rune/src/modules/u64.rs b/crates/rune/src/modules/u64.rs new file mode 100644 index 000000000..f44705f36 --- /dev/null +++ b/crates/rune/src/modules/u64.rs @@ -0,0 +1,22 @@ +//! Integers. + +use core::cmp::Ordering; +use core::num::ParseIntError; + +use crate as rune; +use crate::alloc; +use crate::alloc::string::TryToString; +use crate::runtime::{VmErrorKind, VmResult}; +use crate::{ContextError, Module}; + +/// Unsigned integers. +/// +/// This provides methods for computing over and parsing 64-bit unsigned integers. +#[rune::module(::std::u64)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + unsigned!(m, u64); + Ok(m) +} + +unsigned_fns!(u64); diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index f48c74fac..7a7f0e6c3 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -1,4 +1,10 @@ +#[macro_use] +mod macros; + +use core::any; +use core::cmp::Ordering; use core::fmt; + use rust_alloc::sync::Arc; use serde::{Deserialize, Serialize}; @@ -6,44 +12,74 @@ use serde::{Deserialize, Serialize}; use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, HashMap}; -use crate::runtime::{ - self, BorrowRefRepr, Bytes, FromValue, Inline, Mutable, Object, OwnedTuple, RawStr, ToValue, - TypeInfo, Value, VmErrorKind, -}; use crate::{Hash, TypeHash}; +use super::{ + BorrowRefRepr, Bytes, FromValue, Inline, Mutable, Object, OwnedTuple, RawStr, ToValue, Tuple, + Type, TypeInfo, Value, VmErrorKind, +}; + /// Derive for the [`ToConstValue`](trait@ToConstValue) trait. pub use rune_macros::ToConstValue; -use super::{AnyTypeInfo, RuntimeError}; - -/// Implementation of a constant constructor. -/// -/// Do not implement manually, this is provided when deriving -/// [`ToConstValue`](derive@ToConstValue). -pub trait ConstConstruct: 'static + Send + Sync { - /// Construct from values. - #[doc(hidden)] - fn const_construct(&self, fields: &[ConstValue]) -> Result; +use super::{AnyTypeInfo, RuntimeError, VmIntegerRepr}; - /// Construct from values. +/// Cheap conversion trait to convert something infallibly into a [`ConstValue`]. +pub trait IntoConstValue { + /// Convert into a dynamic [`ConstValue`]. #[doc(hidden)] - fn runtime_construct(&self, fields: &mut [Value]) -> Result; + fn into_const_value(self) -> alloc::Result; } -pub(crate) trait ConstContext { - fn get(&self, hash: Hash) -> Option<&dyn ConstConstruct>; +impl IntoConstValue for ConstValue { + #[inline] + fn into_const_value(self) -> alloc::Result { + Ok(self) + } } -pub(crate) struct EmptyConstContext; - -impl ConstContext for EmptyConstContext { +impl IntoConstValue for &ConstValue { #[inline] - fn get(&self, _: Hash) -> Option<&dyn ConstConstruct> { - None + fn into_const_value(self) -> alloc::Result { + self.try_clone() } } +/// Convert something into a [`ConstValue`]. +/// +/// # Examples +/// +/// ``` +/// let value = rune::to_const_value((1u32, 2u64))?; +/// let (a, b) = rune::from_const_value::<(1u32, 2u64)>(value)?; +/// +/// assert_eq!(a, 1); +/// assert_eq!(b, 2); +/// # Ok::<_, rune::support::Error>(()) +/// ``` +pub fn from_const_value(value: impl IntoConstValue) -> Result +where + T: FromConstValue, +{ + T::from_const_value(value.into_const_value()?) +} + +/// Convert something into a [`ConstValue`]. +/// +/// # Examples +/// +/// ``` +/// let value = rune::to_const_value((1u32, 2u64))?; +/// let (a, b) = rune::from_const_value::<(1u32, 2u64)>(value)?; +/// +/// assert_eq!(a, 1); +/// assert_eq!(b, 2); +/// # Ok::<_, rune::support::Error>(()) +/// ``` +pub fn to_const_value(value: impl ToConstValue) -> Result { + value.to_const_value() +} + /// Convert a value into a constant value. pub trait ToConstValue: Sized { /// Convert into a constant value. @@ -90,6 +126,30 @@ pub(crate) enum ConstValueKind { Struct(Hash, Box<[ConstValue]>), } +impl ConstValueKind { + fn type_info(&self) -> TypeInfo { + match self { + ConstValueKind::Inline(value) => value.type_info(), + ConstValueKind::String(..) => { + TypeInfo::static_type(crate::runtime::static_type::STRING) + } + ConstValueKind::Bytes(..) => TypeInfo::static_type(crate::runtime::static_type::BYTES), + ConstValueKind::Vec(..) => TypeInfo::static_type(crate::runtime::static_type::VEC), + ConstValueKind::Tuple(..) => TypeInfo::static_type(crate::runtime::static_type::TUPLE), + ConstValueKind::Object(..) => { + TypeInfo::static_type(crate::runtime::static_type::OBJECT) + } + ConstValueKind::Option(..) => { + TypeInfo::static_type(crate::runtime::static_type::OPTION) + } + ConstValueKind::Struct(hash, ..) => TypeInfo::any_type_info(AnyTypeInfo::new( + RawStr::from_str("constant struct"), + *hash, + )), + } + } +} + /// A constant value. #[derive(Deserialize, Serialize)] #[serde(transparent)] @@ -98,6 +158,13 @@ pub struct ConstValue { } impl ConstValue { + /// Construct a new tuple constant value. + pub fn tuple(values: Box<[ConstValue]>) -> ConstValue { + ConstValue { + kind: ConstValueKind::Tuple(values), + } + } + /// Construct a constant value for a struct. pub fn for_struct( hash: Hash, @@ -110,6 +177,60 @@ impl ConstValue { }) } + /// Try to coerce the current value as the specified integer `T`. + /// + /// # Examples + /// + /// ``` + /// let value = rune::to_const_value(u32::MAX)?; + /// + /// assert_eq!(value.try_as_integer::()?, u32::MAX as u64); + /// assert!(value.try_as_integer::().is_err()); + /// + /// # Ok::<(), rune::support::Error>(()) + /// ``` + pub fn try_as_integer(&self) -> Result + where + T: TryFrom + TryFrom, + { + match self.kind { + ConstValueKind::Inline(Inline::Signed(value)) => match value.try_into() { + Ok(number) => Ok(number), + Err(..) => Err(RuntimeError::new( + VmErrorKind::ValueToIntegerCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + }, + )), + }, + ConstValueKind::Inline(Inline::Unsigned(value)) => match value.try_into() { + Ok(number) => Ok(number), + Err(..) => Err(RuntimeError::new( + VmErrorKind::ValueToIntegerCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + }, + )), + }, + ref kind => { + return Err(RuntimeError::new(VmErrorKind::ExpectedNumber { + actual: kind.type_info(), + })) + } + } + } + + inline_macros!(inline_into); + + /// Coerce into tuple. + #[inline] + pub fn into_tuple(self) -> Result, RuntimeError> { + match self.kind { + ConstValueKind::Tuple(tuple) => Ok(tuple), + kind => Err(RuntimeError::expected::(kind.type_info())), + } + } + /// Access the interior value. pub(crate) fn as_kind(&self) -> &ConstValueKind { &self.kind @@ -200,7 +321,7 @@ impl ConstValue { None => None, })?), ConstValueKind::Vec(vec) => { - let mut v = runtime::Vec::with_capacity(vec.len())?; + let mut v = super::Vec::with_capacity(vec.len())?; for value in vec { v.push(Self::to_value_with(value, cx)?)?; @@ -238,43 +359,9 @@ impl ConstValue { } } - /// Try to coerce into boolean. - pub fn as_bool(&self) -> Option { - match self.kind { - ConstValueKind::Inline(Inline::Bool(value)) => Some(value), - _ => None, - } - } - - /// Try to coerce into an integer. - pub fn as_i64(&self) -> Option { - match self.kind { - ConstValueKind::Inline(Inline::Integer(value)) => Some(value), - _ => None, - } - } - /// Get the type information of the value. pub(crate) fn type_info(&self) -> TypeInfo { - match &self.kind { - ConstValueKind::Inline(value) => value.type_info(), - ConstValueKind::String(..) => { - TypeInfo::static_type(crate::runtime::static_type::STRING) - } - ConstValueKind::Bytes(..) => TypeInfo::static_type(crate::runtime::static_type::BYTES), - ConstValueKind::Vec(..) => TypeInfo::static_type(crate::runtime::static_type::VEC), - ConstValueKind::Tuple(..) => TypeInfo::static_type(crate::runtime::static_type::TUPLE), - ConstValueKind::Object(..) => { - TypeInfo::static_type(crate::runtime::static_type::OBJECT) - } - ConstValueKind::Option(..) => { - TypeInfo::static_type(crate::runtime::static_type::OPTION) - } - ConstValueKind::Struct(hash, ..) => TypeInfo::any_type_info(AnyTypeInfo::new( - RawStr::from_str("constant struct"), - *hash, - )), - } + self.kind.type_info() } } @@ -366,3 +453,43 @@ impl fmt::Debug for ConstValue { self.kind.fmt(f) } } + +/// Convert a value from a constant value. +pub trait FromConstValue: Sized { + /// Convert from a constant value. + fn from_const_value(value: ConstValue) -> Result; +} + +impl FromConstValue for ConstValue { + #[inline] + fn from_const_value(value: ConstValue) -> Result { + Ok(value) + } +} + +/// Implementation of a constant constructor. +/// +/// Do not implement manually, this is provided when deriving +/// [`ToConstValue`](derive@ToConstValue). +pub trait ConstConstruct: 'static + Send + Sync { + /// Construct from values. + #[doc(hidden)] + fn const_construct(&self, fields: &[ConstValue]) -> Result; + + /// Construct from values. + #[doc(hidden)] + fn runtime_construct(&self, fields: &mut [Value]) -> Result; +} + +pub(crate) trait ConstContext { + fn get(&self, hash: Hash) -> Option<&dyn ConstConstruct>; +} + +pub(crate) struct EmptyConstContext; + +impl ConstContext for EmptyConstContext { + #[inline] + fn get(&self, _: Hash) -> Option<&dyn ConstConstruct> { + None + } +} diff --git a/crates/rune/src/runtime/const_value/macros.rs b/crates/rune/src/runtime/const_value/macros.rs new file mode 100644 index 000000000..e31faa2c7 --- /dev/null +++ b/crates/rune/src/runtime/const_value/macros.rs @@ -0,0 +1,38 @@ +macro_rules! inline_into { + ( + $(#[$($meta:meta)*])* + $kind:ident($ty:ty), + $as:ident, + $as_mut:ident, + ) => { + $(#[$($meta)*])* + /// + /// This gets a copy of the value. + #[inline] + pub fn $as(&self) -> Result<$ty, RuntimeError> { + match &self.kind { + ConstValueKind::Inline(Inline::$kind(value)) => { + Ok(*value) + } + value => { + Err(RuntimeError::expected::<$ty>(value.type_info())) + } + } + } + + $(#[$($meta)*])* + /// + /// This gets the value by mutable reference. + #[inline] + pub fn $as_mut(&mut self) -> Result<&mut $ty, RuntimeError> { + match &mut self.kind { + ConstValueKind::Inline(Inline::$kind(value)) => { + Ok(value) + } + value => { + Err(RuntimeError::expected::<$ty>(value.type_info())) + } + } + } + } +} diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index 1ddf1d7cc..319942646 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -214,7 +214,7 @@ impl FormatSpec { vm_try!(f.buf_mut().try_push(*c)); vm_try!(self.format_fill(f, self.align, self.fill, None)); } - Inline::Integer(n) => { + Inline::Signed(n) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(self.format_number(f.buf_mut(), n)); vm_try!(self.format_fill(f, align, fill, sign)); @@ -258,7 +258,7 @@ impl FormatSpec { 'fallback: { match vm_try!(value.as_ref_repr()) { RefRepr::Inline(value) => match value { - Inline::Integer(n) => { + Inline::Signed(n) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(self.format_number(f.buf_mut(), n)); vm_try!(self.format_fill(f, align, fill, sign)); @@ -294,7 +294,7 @@ impl FormatSpec { fn format_upper_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { match vm_try!(value.as_inline()) { - Some(Inline::Integer(n)) => { + Some(Inline::Signed(n)) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(vm_write!(f.buf_mut(), "{:X}", n)); vm_try!(self.format_fill(f, align, fill, sign)); @@ -309,7 +309,7 @@ impl FormatSpec { fn format_lower_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { match vm_try!(value.as_inline()) { - Some(Inline::Integer(n)) => { + Some(Inline::Signed(n)) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(vm_write!(f.buf_mut(), "{:x}", n)); vm_try!(self.format_fill(f, align, fill, sign)); @@ -324,7 +324,7 @@ impl FormatSpec { fn format_binary(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { match vm_try!(value.as_inline()) { - Some(Inline::Integer(n)) => { + Some(Inline::Signed(n)) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(vm_write!(f.buf_mut(), "{:b}", n)); vm_try!(self.format_fill(f, align, fill, sign)); @@ -339,7 +339,7 @@ impl FormatSpec { fn format_pointer(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { match vm_try!(value.as_inline()) { - Some(Inline::Integer(n)) => { + Some(Inline::Signed(n)) => { let (n, align, fill, sign) = self.int_traits(*n); vm_try!(vm_write!(f.buf_mut(), "{:p}", n as *const ())); vm_try!(self.format_fill(f, align, fill, sign)); diff --git a/crates/rune/src/runtime/from_value.rs b/crates/rune/src/runtime/from_value.rs index 653d4bbdb..73ccc6ef5 100644 --- a/crates/rune/src/runtime/from_value.rs +++ b/crates/rune/src/runtime/from_value.rs @@ -3,28 +3,9 @@ use core::cmp::Ordering; use crate::alloc::{self, String}; use crate::Any; -use super::{AnyObj, Mut, RawAnyGuard, Ref, RuntimeError, Value, VmResult}; - -/// Cheap conversion trait to convert something infallibly into a dynamic [`Value`]. -pub trait IntoValue { - /// Convert into a dynamic [`Value`]. - #[doc(hidden)] - fn into_value(self) -> Value; -} - -impl IntoValue for Value { - #[inline] - fn into_value(self) -> Value { - self - } -} - -impl IntoValue for &Value { - #[inline] - fn into_value(self) -> Value { - self.clone() - } -} +use super::{ + AnyObj, ConstValue, FromConstValue, Mut, RawAnyGuard, Ref, RuntimeError, Value, VmResult, +}; /// Derive macro for the [`FromValue`] trait for converting types from the /// dynamic `Value` container. @@ -59,6 +40,27 @@ impl IntoValue for &Value { /// ``` pub use rune_macros::FromValue; +/// Cheap conversion trait to convert something infallibly into a dynamic [`Value`]. +pub trait IntoValue { + /// Convert into a dynamic [`Value`]. + #[doc(hidden)] + fn into_value(self) -> Value; +} + +impl IntoValue for Value { + #[inline] + fn into_value(self) -> Value { + self + } +} + +impl IntoValue for &Value { + #[inline] + fn into_value(self) -> Value { + self.clone() + } +} + /// Convert something into the dynamic [`Value`]. /// /// # Examples @@ -349,6 +351,13 @@ impl FromValue for u8 { } } +impl FromConstValue for u8 { + #[inline] + fn from_const_value(value: ConstValue) -> Result { + value.as_byte() + } +} + impl FromValue for bool { #[inline] fn from_value(value: Value) -> Result { @@ -356,6 +365,13 @@ impl FromValue for bool { } } +impl FromConstValue for bool { + #[inline] + fn from_const_value(value: ConstValue) -> Result { + value.as_bool() + } +} + impl FromValue for char { #[inline] fn from_value(value: Value) -> Result { @@ -363,34 +379,34 @@ impl FromValue for char { } } -impl FromValue for i64 { +impl FromConstValue for char { #[inline] - fn from_value(value: Value) -> Result { - value.as_integer() + fn from_const_value(value: ConstValue) -> Result { + value.as_char() } } macro_rules! impl_number { - ($ty:ty) => { - impl FromValue for $ty { - #[inline] - fn from_value(value: Value) -> Result { - value.try_as_integer() + ($($ty:ty),* $(,)?) => { + $( + impl FromValue for $ty { + #[inline] + fn from_value(value: Value) -> Result { + value.try_as_integer() + } } - } + + impl FromConstValue for $ty { + #[inline] + fn from_const_value(value: ConstValue) -> Result { + value.try_as_integer() + } + } + )* }; } -impl_number!(u16); -impl_number!(u32); -impl_number!(u64); -impl_number!(u128); -impl_number!(usize); -impl_number!(i8); -impl_number!(i16); -impl_number!(i32); -impl_number!(i128); -impl_number!(isize); +impl_number!(u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); impl FromValue for f64 { #[inline] diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index 5db10fa93..9bbc8263a 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -831,16 +831,9 @@ pub enum Inst { /// Where to store the result of the comparison. out: Output, }, - /// Test if the top of the stack is a specific integer. - /// - /// # Operation - /// - /// ```text - /// - /// => - /// ``` + /// Test if the specified value is a specific signed integer. #[musli(packed)] - EqInteger { + EqSigned { /// Address of the value to compare. addr: InstAddress, /// The value to test against. @@ -848,7 +841,16 @@ pub enum Inst { /// Where to store the result of the comparison. out: Output, }, - + /// Test if the specified value is a specific unsigned integer. + #[musli(packed)] + EqUnsigned { + /// Address of the value to compare. + addr: InstAddress, + /// The value to test against. + value: u64, + /// Where to store the result of the comparison. + out: Output, + }, /// Test if the top of the stack is a specific boolean. /// /// # Operation @@ -1147,13 +1149,21 @@ impl Inst { } /// Construct an instruction to push an integer. - pub fn integer(v: i64, out: Output) -> Self { + pub fn signed(v: i64, out: Output) -> Self { Self::Store { value: InstValue::Integer(v), out, } } + /// Construct an instruction to push an unsigned integer. + pub fn unsigned(v: u64, out: Output) -> Self { + Self::Store { + value: InstValue::Unsigned(v), + out, + } + } + /// Construct an instruction to push a float. pub fn float(v: f64, out: Output) -> Self { Self::Store { @@ -1749,6 +1759,9 @@ pub enum InstValue { /// An integer. #[musli(packed)] Integer(i64), + /// An unsigned integer. + #[musli(packed)] + Unsigned(u64), /// A float. #[musli(packed)] Float(f64), @@ -1772,6 +1785,7 @@ impl InstValue { Self::Byte(v) => Value::from(v), Self::Char(v) => Value::from(v), Self::Integer(v) => Value::from(v), + Self::Unsigned(v) => Value::from(v), Self::Float(v) => Value::from(v), Self::Type(v) => Value::from(v), Self::Ordering(v) => Value::from(v), @@ -1791,9 +1805,10 @@ impl fmt::Display for InstValue { write!(f, "b'\\x{:02x}'", v)? } } - Self::Char(v) => write!(f, "{:?}", v)?, - Self::Integer(v) => write!(f, "{}", v)?, - Self::Float(v) => write!(f, "{}", v)?, + Self::Char(v) => write!(f, "{v:?}")?, + Self::Integer(v) => write!(f, "{v}i64")?, + Self::Unsigned(v) => write!(f, "{v}u64")?, + Self::Float(v) => write!(f, "{v}")?, Self::Type(v) => write!(f, "{}", v.into_hash())?, Self::Ordering(v) => write!(f, "{v:?}")?, } diff --git a/crates/rune/src/runtime/macros.rs b/crates/rune/src/runtime/macros.rs index 2c40dc71b..99e43c067 100644 --- a/crates/rune/src/runtime/macros.rs +++ b/crates/rune/src/runtime/macros.rs @@ -1,3 +1,63 @@ +macro_rules! inline_macros { + ($path:path) => { + $path! { + /// Coerce into [`Ordering`]. + Ordering(Ordering), + as_ordering, + as_ordering_mut, + } + + $path! { + /// Coerce into [`bool`]. + Bool(bool), + as_bool, + as_bool_mut, + } + + $path! { + /// Coerce into [`u8`] byte. + Byte(u8), + as_byte, + as_byte_mut, + } + + $path! { + /// Coerce into [`char`]. + Char(char), + as_char, + as_char_mut, + } + + $path! { + /// Coerce into [`i64`] signed integer. + Signed(i64), + as_signed, + as_signed_mut, + } + + $path! { + /// Coerce into [`u64`] unsigned integer. + Unsigned(u64), + as_unsigned, + as_unsigned_mut, + } + + $path! { + /// Coerce into [`f64`] float. + Float(f64), + as_float, + as_float_mut, + } + + $path! { + /// Coerce into [`Type`]. + Type(Type), + as_type, + as_type_mut, + } + }; +} + macro_rules! range_iter { ($range:ident, $name:ident<$ty:ident> $(, { $($item:tt)* })?) => { #[derive(Any)] diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index a46931603..0bbcb992b 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -40,7 +40,9 @@ mod call; pub use self::call::Call; mod const_value; -pub use self::const_value::{ConstConstruct, ConstValue, ToConstValue}; +pub use self::const_value::{ + from_const_value, to_const_value, ConstConstruct, ConstValue, FromConstValue, ToConstValue, +}; pub(crate) use self::const_value::{ConstContext, ConstValueKind, EmptyConstContext}; pub mod debug; diff --git a/crates/rune/src/runtime/range.rs b/crates/rune/src/runtime/range.rs index f18a33cf9..384c7f47e 100644 --- a/crates/rune/src/runtime/range.rs +++ b/crates/rune/src/runtime/range.rs @@ -98,13 +98,19 @@ impl Range { &vm_try!(self.start.as_ref_repr()), vm_try!(self.end.as_ref_repr()), ) { - (RefRepr::Inline(Inline::Byte(start)), RefRepr::Inline(Inline::Byte(end))) => { - vm_try!(rune::to_value(RangeIter::new(*start..*end))) + (RefRepr::Inline(Inline::Byte(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeIter::new(*start..end))) } - (RefRepr::Inline(Inline::Char(start)), RefRepr::Inline(Inline::Char(end))) => { - vm_try!(rune::to_value(RangeIter::new(*start..*end))) + (RefRepr::Inline(Inline::Unsigned(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeIter::new(*start..end))) } - (RefRepr::Inline(Inline::Integer(start)), RefRepr::Inline(Inline::Integer(end))) => { + (RefRepr::Inline(Inline::Signed(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeIter::new(*start..end))) + } + (RefRepr::Inline(Inline::Char(start)), RefRepr::Inline(Inline::Char(end))) => { vm_try!(rune::to_value(RangeIter::new(*start..*end))) } (start, end) => { diff --git a/crates/rune/src/runtime/range_from.rs b/crates/rune/src/runtime/range_from.rs index f04604454..eda417e5c 100644 --- a/crates/rune/src/runtime/range_from.rs +++ b/crates/rune/src/runtime/range_from.rs @@ -90,10 +90,13 @@ impl RangeFrom { RefRepr::Inline(Inline::Byte(start)) => { vm_try!(crate::to_value(RangeFromIter::new(*start..))) } - RefRepr::Inline(Inline::Char(start)) => { + RefRepr::Inline(Inline::Unsigned(start)) => { + vm_try!(crate::to_value(RangeFromIter::new(*start..))) + } + RefRepr::Inline(Inline::Signed(start)) => { vm_try!(crate::to_value(RangeFromIter::new(*start..))) } - RefRepr::Inline(Inline::Integer(start)) => { + RefRepr::Inline(Inline::Char(start)) => { vm_try!(crate::to_value(RangeFromIter::new(*start..))) } start => { @@ -296,7 +299,7 @@ where range_iter!(RangeFrom, RangeFromIter, { #[rune::function(instance, keep, protocol = SIZE_HINT)] #[inline] - pub(crate) fn size_hint(&self) -> (i64, Option) { - (i64::MAX, None) + pub(crate) fn size_hint(&self) -> (u64, Option) { + (u64::MAX, None) } }); diff --git a/crates/rune/src/runtime/range_inclusive.rs b/crates/rune/src/runtime/range_inclusive.rs index 0f16666d3..dcbe17ff4 100644 --- a/crates/rune/src/runtime/range_inclusive.rs +++ b/crates/rune/src/runtime/range_inclusive.rs @@ -99,13 +99,19 @@ impl RangeInclusive { vm_try!(self.start.as_ref_repr()), vm_try!(self.end.as_ref_repr()), ) { - (RefRepr::Inline(Inline::Byte(start)), RefRepr::Inline(Inline::Byte(end))) => { - vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) + (RefRepr::Inline(Inline::Byte(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=end))) } - (RefRepr::Inline(Inline::Char(start)), RefRepr::Inline(Inline::Char(end))) => { - vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) + (RefRepr::Inline(Inline::Unsigned(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=end))) } - (RefRepr::Inline(Inline::Integer(start)), RefRepr::Inline(Inline::Integer(end))) => { + (RefRepr::Inline(Inline::Signed(start)), RefRepr::Inline(end)) => { + let end = vm_try!(end.try_as_integer::()); + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=end))) + } + (RefRepr::Inline(Inline::Char(start)), RefRepr::Inline(Inline::Char(end))) => { vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } (start, end) => { diff --git a/crates/rune/src/runtime/static_type.rs b/crates/rune/src/runtime/static_type.rs index 83b1b6b06..e6e2fd6a1 100644 --- a/crates/rune/src/runtime/static_type.rs +++ b/crates/rune/src/runtime/static_type.rs @@ -78,26 +78,36 @@ pub(crate) static CHAR: &StaticType = &StaticType { impl_static_type!(char, CHAR, CHAR_HASH); /// Hash for `::std::i64`. -pub(crate) const INTEGER_HASH: Hash = ::rune_macros::hash!(::std::i64); +pub(crate) const SIGNED_HASH: Hash = ::rune_macros::hash!(::std::i64); /// The specialized type information for a integer type. -pub(crate) static INTEGER: &StaticType = &StaticType { +pub(crate) static SIGNED: &StaticType = &StaticType { name: RawStr::from_str("i64"), - hash: INTEGER_HASH, + hash: SIGNED_HASH, +}; + +impl_static_type!(i8, SIGNED, SIGNED_HASH); +impl_static_type!(i16, SIGNED, SIGNED_HASH); +impl_static_type!(i32, SIGNED, SIGNED_HASH); +impl_static_type!(i64, SIGNED, SIGNED_HASH); +impl_static_type!(i128, SIGNED, SIGNED_HASH); +impl_static_type!(isize, SIGNED, SIGNED_HASH); + +/// Hash for `::std::u64`. +pub(crate) const UNSIGNED_HASH: Hash = ::rune_macros::hash!(::std::u64); + +/// The specialized type information for an unsigned integer type. +pub(crate) static UNSIGNED: &StaticType = &StaticType { + name: RawStr::from_str("u64"), + hash: UNSIGNED_HASH, }; -impl_static_type!(i8, INTEGER, INTEGER_HASH); // NB: u8 is its own type BYTE. -impl_static_type!(u16, INTEGER, INTEGER_HASH); -impl_static_type!(i16, INTEGER, INTEGER_HASH); -impl_static_type!(u32, INTEGER, INTEGER_HASH); -impl_static_type!(i32, INTEGER, INTEGER_HASH); -impl_static_type!(u64, INTEGER, INTEGER_HASH); -impl_static_type!(i64, INTEGER, INTEGER_HASH); -impl_static_type!(u128, INTEGER, INTEGER_HASH); -impl_static_type!(i128, INTEGER, INTEGER_HASH); -impl_static_type!(usize, INTEGER, INTEGER_HASH); -impl_static_type!(isize, INTEGER, INTEGER_HASH); +impl_static_type!(u16, UNSIGNED, UNSIGNED_HASH); +impl_static_type!(u32, UNSIGNED, UNSIGNED_HASH); +impl_static_type!(u64, UNSIGNED, UNSIGNED_HASH); +impl_static_type!(u128, UNSIGNED, UNSIGNED_HASH); +impl_static_type!(usize, UNSIGNED, UNSIGNED_HASH); /// Hash for `::std::f64`. pub(crate) const FLOAT_HASH: Hash = ::rune_macros::hash!(::std::f64); diff --git a/crates/rune/src/runtime/steps_between.rs b/crates/rune/src/runtime/steps_between.rs index 73fb16497..24fd5f802 100644 --- a/crates/rune/src/runtime/steps_between.rs +++ b/crates/rune/src/runtime/steps_between.rs @@ -9,6 +9,13 @@ impl StepsBetween for i64 { } } +impl StepsBetween for u64 { + #[inline] + fn steps_between(start: Self, end: Self) -> Option { + usize::try_from(end.checked_sub(start)?).ok() + } +} + impl StepsBetween for u8 { #[inline] fn steps_between(start: Self, end: Self) -> Option { diff --git a/crates/rune/src/runtime/tests.rs b/crates/rune/src/runtime/tests.rs index f821918fd..608747a32 100644 --- a/crates/rune/src/runtime/tests.rs +++ b/crates/rune/src/runtime/tests.rs @@ -306,7 +306,7 @@ fn ensure_future_dropped_poll() -> crate::support::Result<()> { panic!("expected ready"); }; - assert_eq!(ok.unwrap().as_integer().unwrap(), 10); + assert_eq!(ok.unwrap().as_signed().unwrap(), 10); assert!(future.is_completed()); Ok(()) } @@ -328,7 +328,7 @@ fn ensure_future_dropped_explicitly() -> crate::support::Result<()> { panic!("expected ready"); }; - assert_eq!(ok.unwrap().as_integer().unwrap(), 0); + assert_eq!(ok.unwrap().as_signed().unwrap(), 0); assert!(future.is_completed()); Ok(()) } diff --git a/crates/rune/src/runtime/to_value.rs b/crates/rune/src/runtime/to_value.rs index 91e29cb73..dde413454 100644 --- a/crates/rune/src/runtime/to_value.rs +++ b/crates/rune/src/runtime/to_value.rs @@ -1,8 +1,9 @@ use crate::alloc::prelude::*; use crate::alloc::{self, HashMap}; -use crate::runtime::{AnyObj, Object, RuntimeError, Value, VmResult}; use crate::Any; +use super::{AnyObj, Object, RuntimeError, Value, VmResult}; + /// Derive macro for the [`ToValue`] trait for converting types into the dynamic /// `Value` container. /// @@ -66,11 +67,8 @@ pub use rune_macros::ToValue; /// assert_eq!(foo, 43); /// # Ok::<_, rune::support::Error>(()) /// ``` -pub fn to_value(value: T) -> Result -where - T: ToValue, -{ - T::to_value(value) +pub fn to_value(value: impl ToValue) -> Result { + value.to_value() } /// Trait for converting types into the dynamic [`Value`] container. diff --git a/crates/rune/src/runtime/tuple.rs b/crates/rune/src/runtime/tuple.rs index 66c36b57d..a668eec8c 100644 --- a/crates/rune/src/runtime/tuple.rs +++ b/crates/rune/src/runtime/tuple.rs @@ -5,15 +5,15 @@ use core::slice; use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::{self, Box}; -use crate::runtime::{ - ConstValue, EmptyConstContext, FromValue, Mut, Mutable, OwnedRepr, RawAnyGuard, Ref, - RuntimeError, ToValue, UnsafeToMut, UnsafeToRef, Value, ValueShared, VmErrorKind, VmResult, -}; -#[cfg(feature = "alloc")] -use crate::runtime::{Hasher, ProtocolCaller}; use crate::Any; -use super::Inline; +use super::{ + ConstValue, EmptyConstContext, FromConstValue, FromValue, Inline, Mut, Mutable, OwnedRepr, + RawAnyGuard, Ref, RuntimeError, ToConstValue, ToValue, UnsafeToMut, UnsafeToRef, Value, + ValueShared, VmErrorKind, VmResult, +}; +#[cfg(feature = "alloc")] +use super::{Hasher, ProtocolCaller}; /// The type of a tuple slice. #[derive(Any)] @@ -329,6 +329,27 @@ macro_rules! impl_tuple { } } + impl <$($ty,)*> FromConstValue for ($($ty,)*) + where + $($ty: FromConstValue,)* + { + fn from_const_value(value: ConstValue) -> Result { + let tuple = value.into_tuple()?; + + let [$($var,)*] = match >::try_from(tuple) { + Ok(tuple) => Box::into_inner(tuple), + Err(tuple) => { + return Err(RuntimeError::new(VmErrorKind::ExpectedTupleLength { + actual: tuple.len(), + expected: $count, + })); + } + }; + + Ok(($(<$ty as FromConstValue>::from_const_value($var)?,)*)) + } + } + impl <$($ty,)*> ToValue for ($($ty,)*) where $($ty: ToValue,)* @@ -342,6 +363,20 @@ macro_rules! impl_tuple { Ok(Value::try_from(tuple)?) } } + + impl <$($ty,)*> ToConstValue for ($($ty,)*) + where + $($ty: ToConstValue,)* + { + fn to_const_value(self) -> Result { + let ($($var,)*) = self; + $(let $var = $var.to_const_value()?;)* + let mut vec = alloc::Vec::try_with_capacity($count)?; + $(vec.try_push($var)?;)* + let tuple = Box::<[ConstValue]>::try_from(vec)?; + Ok(ConstValue::tuple(tuple)) + } + } }; } diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index b77a94f57..612cda17f 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -361,7 +361,7 @@ impl Value { Inline::Char(c) => { vm_try!(f.push(*c)); } - Inline::Integer(integer) => { + Inline::Signed(integer) => { let mut buffer = itoa::Buffer::new(); vm_try!(f.push_str(buffer.format(*integer))); } @@ -776,9 +776,16 @@ impl Value { inline_into! { /// Coerce into [`i64`] integer. - Integer(i64), - as_integer, - as_integer_mut, + Signed(i64), + as_signed, + as_signed_mut, + } + + inline_into! { + /// Coerce into [`u64`] unsigned integer. + Unsigned(u64), + as_unsigned, + as_unsigned_mut, } inline_into! { @@ -1127,7 +1134,7 @@ impl Value { let a = match (&a, vm_try!(b.borrow_ref_repr())) { (BorrowRefRepr::Inline(a), BorrowRefRepr::Inline(b)) => { - return a.partial_eq(b); + return VmResult::Ok(vm_try!(a.partial_eq(b))); } (BorrowRefRepr::Inline(lhs), rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { @@ -1232,7 +1239,7 @@ impl Value { ) -> VmResult<()> { match vm_try!(self.borrow_ref_repr()) { BorrowRefRepr::Inline(value) => match value { - Inline::Integer(value) => { + Inline::Signed(value) => { hasher.write_i64(*value); return VmResult::Ok(()); } @@ -1410,7 +1417,9 @@ impl Value { vm_try!(self.borrow_ref_repr()), vm_try!(b.borrow_ref_repr()), ) { - (BorrowRefRepr::Inline(a), BorrowRefRepr::Inline(b)) => return a.partial_cmp(b), + (BorrowRefRepr::Inline(a), BorrowRefRepr::Inline(b)) => { + return VmResult::Ok(vm_try!(a.partial_cmp(b))) + } (BorrowRefRepr::Inline(lhs), rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { op: Protocol::PARTIAL_CMP.name, @@ -1592,8 +1601,6 @@ impl Value { /// # Examples /// /// ``` - /// use rune::runtime::{Value, VmResult}; - /// /// let value = rune::to_value(u32::MAX)?; /// /// assert_eq!(value.try_as_integer::()?, u32::MAX as u64); @@ -1603,19 +1610,21 @@ impl Value { /// ``` pub fn try_as_integer(&self) -> Result where - T: TryFrom, - VmIntegerRepr: From, + T: TryFrom + TryFrom + TryFrom, { - let integer = self.as_integer()?; - - match integer.try_into() { - Ok(number) => Ok(number), - Err(..) => Err(RuntimeError::new( - VmErrorKind::ValueToIntegerCoercionError { - from: VmIntegerRepr::from(integer), - to: any::type_name::(), - }, - )), + match self.repr { + Repr::Empty => Err(RuntimeError::from(AccessError::empty())), + Repr::Inline(value) => value.try_as_integer(), + Repr::Mutable(ref value) => { + return Err(RuntimeError::new(VmErrorKind::ExpectedNumber { + actual: value.borrow_ref()?.type_info(), + })) + } + Repr::Any(ref value) => { + return Err(RuntimeError::new(VmErrorKind::ExpectedNumber { + actual: value.type_info(), + })) + } } } @@ -1920,7 +1929,8 @@ inline_from! { Byte => u8, Bool => bool, Char => char, - Integer => i64, + Signed => i64, + Unsigned => u64, Float => f64, Type => Type, Ordering => Ordering, @@ -1953,13 +1963,9 @@ from_container! { Result => Result, } -number_value_trait! { - u16, u32, u64, u128, usize, i8, i16, i32, i128, isize, -} - -float_value_trait! { - f32, -} +signed_value_trait!(i8, i16, i32, i128, isize); +unsigned_value_trait!(u16, u32, u128, usize); +float_value_trait!(f32); impl MaybeTypeOf for Value { #[inline] diff --git a/crates/rune/src/runtime/value/inline.rs b/crates/rune/src/runtime/value/inline.rs index 23a9d26b0..bded5baa9 100644 --- a/crates/rune/src/runtime/value/inline.rs +++ b/crates/rune/src/runtime/value/inline.rs @@ -1,3 +1,4 @@ +use core::any; use core::cmp::Ordering; use core::fmt; @@ -5,7 +6,9 @@ use musli::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::hash::Hash; -use crate::runtime::{static_type, Protocol, Type, TypeInfo, VmErrorKind, VmResult}; +use crate::runtime::{ + static_type, Protocol, RuntimeError, Type, TypeInfo, VmErrorKind, VmIntegerRepr, VmResult, +}; use super::err; @@ -21,7 +24,9 @@ pub enum Inline { /// A character. Char(char), /// A number. - Integer(i64), + Signed(i64), + /// An unsigned number. + Unsigned(u64), /// A float. Float(f64), /// A type hash. Describes a type in the virtual machine. @@ -35,22 +40,65 @@ pub enum Inline { } impl Inline { + pub(crate) fn try_as_integer(self) -> Result + where + T: TryFrom + TryFrom + TryFrom, + { + match self { + Inline::Byte(value) => match value.try_into() { + Ok(number) => Ok(number), + Err(..) => Err(RuntimeError::new( + VmErrorKind::ValueToIntegerCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + }, + )), + }, + Inline::Unsigned(value) => match value.try_into() { + Ok(number) => Ok(number), + Err(..) => Err(RuntimeError::new( + VmErrorKind::ValueToIntegerCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + }, + )), + }, + Inline::Signed(value) => match value.try_into() { + Ok(number) => Ok(number), + Err(..) => Err(RuntimeError::new( + VmErrorKind::ValueToIntegerCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + }, + )), + }, + ref value => Err(RuntimeError::new(VmErrorKind::ExpectedNumber { + actual: value.type_info(), + })), + } + } + /// Perform a partial equality check over two inline values. - pub(crate) fn partial_eq(&self, other: &Self) -> VmResult { + pub(crate) fn partial_eq(&self, other: &Self) -> Result { match (self, other) { - (Inline::Unit, Inline::Unit) => VmResult::Ok(true), - (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(*a == *b), - (Inline::Byte(a), Inline::Byte(b)) => VmResult::Ok(*a == *b), - (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(*a == *b), - (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(*a == *b), - (Inline::Float(a), Inline::Float(b)) => VmResult::Ok(*a == *b), - (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(*a == *b), - (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(*a == *b), - (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_EQ.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }), + (Inline::Unit, Inline::Unit) => Ok(true), + (Inline::Bool(a), Inline::Bool(b)) => Ok(*a == *b), + (Inline::Byte(a), Inline::Byte(b)) => Ok(*a == *b), + (Inline::Char(a), Inline::Char(b)) => Ok(*a == *b), + (Inline::Signed(a), Inline::Signed(b)) => Ok(*a == *b), + (Inline::Signed(a), rhs) => Ok(*a == rhs.try_as_integer::()?), + (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(*a == *b), + (Inline::Unsigned(a), rhs) => Ok(*a == rhs.try_as_integer::()?), + (Inline::Float(a), Inline::Float(b)) => Ok(*a == *b), + (Inline::Type(a), Inline::Type(b)) => Ok(*a == *b), + (Inline::Ordering(a), Inline::Ordering(b)) => Ok(*a == *b), + (lhs, rhs) => Err(RuntimeError::from( + VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_EQ.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }, + )), } } @@ -68,7 +116,7 @@ impl Inline { VmResult::Ok(matches!(ordering, Ordering::Equal)) } - (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(*a == *b), + (Inline::Signed(a), Inline::Signed(b)) => VmResult::Ok(*a == *b), (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(*a == *b), (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(*a == *b), (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { @@ -80,21 +128,32 @@ impl Inline { } /// Partial comparison implementation for inline. - pub(crate) fn partial_cmp(&self, other: &Self) -> VmResult> { + pub(crate) fn partial_cmp(&self, other: &Self) -> Result, RuntimeError> { match (self, other) { - (Inline::Unit, Inline::Unit) => VmResult::Ok(Some(Ordering::Equal)), - (Inline::Bool(lhs), Inline::Bool(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Byte(lhs), Inline::Byte(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Char(lhs), Inline::Char(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Float(lhs), Inline::Float(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Integer(lhs), Inline::Integer(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Type(lhs), Inline::Type(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (Inline::Ordering(lhs), Inline::Ordering(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), - (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_CMP.name, - lhs: lhs.type_info(), - rhs: rhs.type_info(), - }), + (Inline::Unit, Inline::Unit) => Ok(Some(Ordering::Equal)), + (Inline::Bool(lhs), Inline::Bool(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Byte(lhs), Inline::Byte(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Char(lhs), Inline::Char(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Float(lhs), Inline::Float(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Signed(lhs), Inline::Signed(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Signed(lhs), rhs) => { + let rhs = rhs.try_as_integer::()?; + Ok(lhs.partial_cmp(&rhs)) + } + (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Unsigned(lhs), rhs) => { + let rhs = rhs.try_as_integer::()?; + Ok(lhs.partial_cmp(&rhs)) + } + (Inline::Type(lhs), Inline::Type(rhs)) => Ok(lhs.partial_cmp(rhs)), + (Inline::Ordering(lhs), Inline::Ordering(rhs)) => Ok(lhs.partial_cmp(rhs)), + (lhs, rhs) => Err(RuntimeError::from( + VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_CMP.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }, + )), } } @@ -112,7 +171,8 @@ impl Inline { VmResult::Ok(ordering) } - (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Signed(a), Inline::Signed(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Unsigned(a), Inline::Unsigned(b)) => VmResult::Ok(a.cmp(b)), (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(a.cmp(b)), (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(a.cmp(b)), (lhs, rhs) => VmResult::err(VmErrorKind::UnsupportedBinaryOperation { @@ -131,7 +191,8 @@ impl fmt::Debug for Inline { Inline::Bool(value) => value.fmt(f), Inline::Byte(value) => value.fmt(f), Inline::Char(value) => value.fmt(f), - Inline::Integer(value) => value.fmt(f), + Inline::Signed(value) => value.fmt(f), + Inline::Unsigned(value) => value.fmt(f), Inline::Float(value) => value.fmt(f), Inline::Type(value) => value.fmt(f), Inline::Ordering(value) => value.fmt(f), @@ -146,7 +207,8 @@ impl Inline { Inline::Bool(..) => TypeInfo::static_type(static_type::BOOL), Inline::Byte(..) => TypeInfo::static_type(static_type::BYTE), Inline::Char(..) => TypeInfo::static_type(static_type::CHAR), - Inline::Integer(..) => TypeInfo::static_type(static_type::INTEGER), + Inline::Signed(..) => TypeInfo::static_type(static_type::SIGNED), + Inline::Unsigned(..) => TypeInfo::static_type(static_type::UNSIGNED), Inline::Float(..) => TypeInfo::static_type(static_type::FLOAT), Inline::Type(..) => TypeInfo::static_type(static_type::TYPE), Inline::Ordering(..) => TypeInfo::static_type(static_type::ORDERING), @@ -163,7 +225,8 @@ impl Inline { Inline::Bool(..) => static_type::BOOL.hash, Inline::Byte(..) => static_type::BYTE.hash, Inline::Char(..) => static_type::CHAR.hash, - Inline::Integer(..) => static_type::INTEGER.hash, + Inline::Signed(..) => static_type::SIGNED.hash, + Inline::Unsigned(..) => static_type::UNSIGNED.hash, Inline::Float(..) => static_type::FLOAT.hash, Inline::Type(..) => static_type::TYPE.hash, Inline::Ordering(..) => static_type::ORDERING.hash, diff --git a/crates/rune/src/runtime/value/macros.rs b/crates/rune/src/runtime/value/macros.rs index d5378c9ec..272c641bb 100644 --- a/crates/rune/src/runtime/value/macros.rs +++ b/crates/rune/src/runtime/value/macros.rs @@ -382,7 +382,7 @@ macro_rules! from_container { }; } -macro_rules! number_value_trait { +macro_rules! signed_value_trait { ($($ty:ty),* $(,)?) => { $( impl $crate::runtime::ToValue for $ty { @@ -434,6 +434,58 @@ macro_rules! number_value_trait { }; } +macro_rules! unsigned_value_trait { + ($($ty:ty),* $(,)?) => { + $( + impl $crate::runtime::ToValue for $ty { + #[inline] + fn to_value(self) -> Result { + Value::try_from(self) + } + } + + impl TryFrom<$ty> for Value { + type Error = $crate::runtime::RuntimeError; + + #[inline] + fn try_from(value: $ty) -> Result { + match ::try_from(value) { + Ok(number) => Ok(Value::from(number)), + #[allow(unreachable_patterns)] + Err(..) => Err($crate::runtime::RuntimeError::from(VmErrorKind::IntegerToValueCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + })), + } + } + } + + impl $crate::runtime::ToConstValue for $ty { + #[inline] + fn to_const_value(self) -> Result<$crate::runtime::ConstValue, $crate::runtime::RuntimeError> { + $crate::runtime::ConstValue::try_from(self) + } + } + + impl TryFrom<$ty> for ConstValue { + type Error = $crate::runtime::RuntimeError; + + #[inline] + fn try_from(value: $ty) -> Result { + match ::try_from(value) { + Ok(number) => Ok($crate::runtime::ConstValue::from(number)), + #[allow(unreachable_patterns)] + Err(..) => Err($crate::runtime::RuntimeError::from(VmErrorKind::IntegerToValueCoercionError { + from: VmIntegerRepr::from(value), + to: any::type_name::(), + })), + } + } + } + )* + }; +} + macro_rules! float_value_trait { ($($ty:ty),* $(,)?) => { $( diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index b851dbf85..46ae705a4 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -27,13 +27,14 @@ impl ser::Serialize for Value { S: ser::Serializer, { match self.borrow_ref_repr().map_err(S::Error::custom)? { - BorrowRefRepr::Inline(value) => match value { + BorrowRefRepr::Inline(value) => match *value { Inline::Unit => serializer.serialize_unit(), - Inline::Bool(b) => serializer.serialize_bool(*b), - Inline::Char(c) => serializer.serialize_char(*c), - Inline::Byte(c) => serializer.serialize_u8(*c), - Inline::Integer(integer) => serializer.serialize_i64(*integer), - Inline::Float(float) => serializer.serialize_f64(*float), + Inline::Bool(value) => serializer.serialize_bool(value), + Inline::Char(value) => serializer.serialize_char(value), + Inline::Byte(value) => serializer.serialize_u8(value), + Inline::Signed(value) => serializer.serialize_i64(value), + Inline::Unsigned(value) => serializer.serialize_u64(value), + Inline::Float(value) => serializer.serialize_f64(value), Inline::Type(..) => Err(ser::Error::custom("cannot serialize types")), Inline::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), }, diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index afdc4e999..b16a753c8 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -1238,7 +1238,8 @@ impl Vm { match ty.into_hash() { static_type::FLOAT_HASH => Value::from($value as f64), static_type::BYTE_HASH => Value::from($value as u8), - static_type::INTEGER_HASH => Value::from($value as i64), + static_type::SIGNED_HASH => Value::from($value as i64), + static_type::UNSIGNED_HASH => Value::from($value as u64), ty => { return err(VmErrorKind::UnsupportedAs { value: <$from as TypeOf>::type_info(), @@ -1250,7 +1251,8 @@ impl Vm { } let value = match vm_try!(a.as_ref_repr()) { - RefRepr::Inline(Inline::Integer(a)) => convert!(i64, *a), + RefRepr::Inline(Inline::Signed(a)) => convert!(i64, *a), + RefRepr::Inline(Inline::Unsigned(a)) => convert!(u64, *a), RefRepr::Inline(Inline::Float(a)) => convert!(f64, *a), RefRepr::Inline(Inline::Byte(a)) => convert!(u8, *a), value => { @@ -1421,7 +1423,8 @@ impl Vm { target: InstTarget, protocol: Protocol, error: fn() -> VmErrorKind, - integer_op: fn(i64, i64) -> Option, + signed_op: fn(i64, i64) -> Option, + unsigned_op: fn(u64, u64) -> Option, float_op: fn(f64, f64) -> f64, rhs: InstAddress, ) -> VmResult<()> { @@ -1431,8 +1434,13 @@ impl Vm { let fallback = match target_value!(self, target, guard, lhs, rhs) { TargetValue::Same(value) => { match vm_try!(value.as_mut_repr()) { - MutRepr::Inline(Inline::Integer(value)) => { - let out = vm_try!(integer_op(*value, *value).ok_or_else(error)); + MutRepr::Inline(Inline::Signed(value)) => { + let out = vm_try!(signed_op(*value, *value).ok_or_else(error)); + *value = out; + return VmResult::Ok(()); + } + MutRepr::Inline(Inline::Unsigned(value)) => { + let out = vm_try!(unsigned_op(*value, *value).ok_or_else(error)); *value = out; return VmResult::Ok(()); } @@ -1456,8 +1464,13 @@ impl Vm { TargetValue::Pair(lhs, rhs) => { match (vm_try!(lhs.as_mut_repr()), vm_try!(rhs.as_ref_repr())) { (MutRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + (Inline::Signed(lhs), Inline::Signed(rhs)) => { + let out = vm_try!(signed_op(*lhs, *rhs).ok_or_else(error)); + *lhs = out; + return VmResult::Ok(()); + } + (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => { + let out = vm_try!(unsigned_op(*lhs, *rhs).ok_or_else(error)); *lhs = out; return VmResult::Ok(()); } @@ -1563,7 +1576,8 @@ impl Vm { &mut self, protocol: Protocol, error: fn() -> VmErrorKind, - integer_op: fn(i64, i64) -> Option, + signed_op: fn(i64, i64) -> Option, + unsigned_op: fn(u64, u64) -> Option, float_op: fn(f64, f64) -> f64, lhs: InstAddress, rhs: InstAddress, @@ -1575,8 +1589,11 @@ impl Vm { 'fallback: { let inline = match (vm_try!(lhs.as_ref_repr()), vm_try!(rhs.as_ref_repr())) { (RefRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - Inline::Integer(vm_try!(integer_op(*lhs, *rhs).ok_or_else(error))) + (Inline::Signed(lhs), Inline::Signed(rhs)) => { + Inline::Signed(vm_try!(signed_op(*lhs, *rhs).ok_or_else(error))) + } + (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => { + Inline::Unsigned(vm_try!(unsigned_op(*lhs, *rhs).ok_or_else(error))) } (Inline::Float(lhs), Inline::Float(rhs)) => Inline::Float(float_op(*lhs, *rhs)), (lhs, rhs) => { @@ -1626,7 +1643,8 @@ impl Vm { fn internal_infallible_bitwise_bool( &mut self, protocol: Protocol, - integer_op: fn(i64, i64) -> i64, + signed_op: fn(i64, i64) -> i64, + unsigned_op: fn(u64, u64) -> u64, byte_op: fn(u8, u8) -> u8, bool_op: fn(bool, bool) -> bool, lhs: InstAddress, @@ -1639,8 +1657,11 @@ impl Vm { 'fallback: { let inline = match (vm_try!(lhs.as_ref_repr()), vm_try!(rhs.as_ref_repr())) { (RefRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - Inline::Integer(integer_op(*lhs, *rhs)) + (Inline::Signed(lhs), Inline::Signed(rhs)) => { + Inline::Signed(signed_op(*lhs, *rhs)) + } + (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => { + Inline::Unsigned(unsigned_op(*lhs, *rhs)) } (Inline::Byte(lhs), Inline::Byte(rhs)) => Inline::Byte(byte_op(*lhs, *rhs)), (Inline::Bool(lhs), Inline::Bool(rhs)) => Inline::Bool(bool_op(*lhs, *rhs)), @@ -1690,7 +1711,8 @@ impl Vm { &mut self, target: InstTarget, protocol: Protocol, - integer_op: fn(&mut i64, i64), + signed_op: fn(&mut i64, i64), + unsigned_op: fn(&mut u64, u64), byte_op: fn(&mut u8, u8), bool_op: fn(&mut bool, bool), rhs: InstAddress, @@ -1701,9 +1723,14 @@ impl Vm { let fallback = match target_value!(self, target, guard, lhs, rhs) { TargetValue::Same(value) => { match vm_try!(value.as_mut_repr()) { - MutRepr::Inline(Inline::Integer(value)) => { + MutRepr::Inline(Inline::Signed(value)) => { let rhs = *value; - integer_op(value, rhs); + signed_op(value, rhs); + return VmResult::Ok(()); + } + MutRepr::Inline(Inline::Unsigned(value)) => { + let rhs = *value; + unsigned_op(value, rhs); return VmResult::Ok(()); } MutRepr::Inline(Inline::Byte(value)) => { @@ -1731,8 +1758,12 @@ impl Vm { TargetValue::Pair(lhs, rhs) => { match (vm_try!(lhs.as_mut_repr()), vm_try!(rhs.as_ref_repr())) { (MutRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - integer_op(lhs, *rhs); + (Inline::Signed(lhs), Inline::Signed(rhs)) => { + signed_op(lhs, *rhs); + return VmResult::Ok(()); + } + (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => { + unsigned_op(lhs, *rhs); return VmResult::Ok(()); } (Inline::Byte(lhs), Inline::Byte(rhs)) => { @@ -1773,8 +1804,9 @@ impl Vm { &mut self, protocol: Protocol, error: fn() -> VmErrorKind, - integer_op: fn(i64, i64) -> Option, - byte_op: fn(u8, i64) -> Option, + signed_op: fn(i64, u32) -> Option, + unsigned_op: fn(u64, u32) -> Option, + byte_op: fn(u8, u32) -> Option, lhs: InstAddress, rhs: InstAddress, out: Output, @@ -1785,11 +1817,18 @@ impl Vm { Pair::Same(value) => { if let MutRepr::Inline(lhs) = vm_try!(value.as_mut_repr()) { match lhs { - Inline::Integer(value) => { - break 'inline Inline::Integer(vm_try!(integer_op( - *value, *value - ) - .ok_or_else(error))); + Inline::Signed(value) => { + let shift = + vm_try!(u32::try_from(*value).ok().ok_or_else(error)); + let value = vm_try!(signed_op(*value, shift).ok_or_else(error)); + break 'inline Inline::Signed(value); + } + Inline::Unsigned(value) => { + let shift = + vm_try!(u32::try_from(*value).ok().ok_or_else(error)); + let value = + vm_try!(unsigned_op(*value, shift).ok_or_else(error)); + break 'inline Inline::Unsigned(value); } value => { return err(VmErrorKind::UnsupportedBinaryOperation { @@ -1806,15 +1845,20 @@ impl Vm { Pair::Pair(lhs, rhs) => { match (vm_try!(lhs.as_mut_repr()), vm_try!(rhs.as_ref_repr())) { (MutRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - break 'inline Inline::Integer(vm_try!( - integer_op(*lhs, *rhs).ok_or_else(error) - )); + (Inline::Signed(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let value = vm_try!(signed_op(*lhs, rhs).ok_or_else(error)); + break 'inline Inline::Signed(value); + } + (Inline::Unsigned(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let value = vm_try!(unsigned_op(*lhs, rhs).ok_or_else(error)); + break 'inline Inline::Unsigned(value); } - (Inline::Byte(lhs), Inline::Integer(rhs)) => { - break 'inline Inline::Byte(vm_try!( - byte_op(*lhs, *rhs).ok_or_else(error) - )); + (Inline::Byte(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let value = vm_try!(byte_op(*lhs, rhs).ok_or_else(error)); + break 'inline Inline::Byte(value); } (lhs, rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { @@ -1863,8 +1907,9 @@ impl Vm { target: InstTarget, protocol: Protocol, error: fn() -> VmErrorKind, - integer_op: fn(i64, i64) -> Option, - byte_op: fn(u8, i64) -> Option, + signed_op: fn(i64, u32) -> Option, + unsigned_op: fn(u64, u32) -> Option, + byte_op: fn(u8, u32) -> Option, rhs: InstAddress, ) -> VmResult<()> { let lhs; @@ -1873,8 +1918,15 @@ impl Vm { let fallback = match target_value!(self, target, guard, lhs, rhs) { TargetValue::Same(value) => { match vm_try!(value.as_mut_repr()) { - MutRepr::Inline(Inline::Integer(value)) => { - let out = vm_try!(integer_op(*value, *value).ok_or_else(error)); + MutRepr::Inline(Inline::Signed(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(error)); + let out = vm_try!(signed_op(*value, shift).ok_or_else(error)); + *value = out; + return VmResult::Ok(()); + } + MutRepr::Inline(Inline::Unsigned(value)) => { + let shift = vm_try!(u32::try_from(*value).ok().ok_or_else(error)); + let out = vm_try!(unsigned_op(*value, shift).ok_or_else(error)); *value = out; return VmResult::Ok(()); } @@ -1893,13 +1945,21 @@ impl Vm { TargetValue::Pair(lhs, rhs) => { match (vm_try!(lhs.as_mut_repr()), vm_try!(rhs.as_ref_repr())) { (MutRepr::Inline(lhs), RefRepr::Inline(rhs)) => match (lhs, rhs) { - (Inline::Integer(lhs), Inline::Integer(rhs)) => { - let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + (Inline::Signed(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let out = vm_try!(signed_op(*lhs, rhs).ok_or_else(error)); + *lhs = out; + return VmResult::Ok(()); + } + (Inline::Unsigned(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let out = vm_try!(unsigned_op(*lhs, rhs).ok_or_else(error)); *lhs = out; return VmResult::Ok(()); } - (Inline::Byte(lhs), Inline::Integer(rhs)) => { - let out = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); + (Inline::Byte(lhs), rhs) => { + let rhs = vm_try!(rhs.try_as_integer()); + let out = vm_try!(byte_op(*lhs, rhs).ok_or_else(error)); *lhs = out; return VmResult::Ok(()); } @@ -2108,7 +2168,7 @@ impl Vm { let value = match vm_try!(value.borrow_ref_repr()) { BorrowRefRepr::Inline(value) => match value { Inline::Bool(value) => Value::from(!value), - Inline::Integer(value) => Value::from(!value), + Inline::Signed(value) => Value::from(!value), Inline::Byte(value) => Value::from(!value), actual => { let operand = actual.type_info(); @@ -2132,7 +2192,7 @@ impl Vm { let value = match vm_try!(value.borrow_ref_repr()) { BorrowRefRepr::Inline(value) => match value { Inline::Float(value) => Value::from(-value), - Inline::Integer(value) => Value::from(-value), + Inline::Signed(value) => Value::from(-value), actual => { let operand = actual.type_info(); return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); @@ -2162,6 +2222,7 @@ impl Vm { Protocol::ADD, || VmErrorKind::Overflow, i64::checked_add, + u64::checked_add, ops::Add::add, lhs, rhs, @@ -2173,6 +2234,7 @@ impl Vm { Protocol::SUB, || VmErrorKind::Underflow, i64::checked_sub, + u64::checked_sub, ops::Sub::sub, lhs, rhs, @@ -2184,6 +2246,7 @@ impl Vm { Protocol::MUL, || VmErrorKind::Overflow, i64::checked_mul, + u64::checked_mul, ops::Mul::mul, lhs, rhs, @@ -2195,6 +2258,7 @@ impl Vm { Protocol::DIV, || VmErrorKind::DivideByZero, i64::checked_div, + u64::checked_div, ops::Div::div, lhs, rhs, @@ -2206,6 +2270,7 @@ impl Vm { Protocol::REM, || VmErrorKind::DivideByZero, i64::checked_rem, + u64::checked_rem, ops::Rem::rem, lhs, rhs, @@ -2217,6 +2282,7 @@ impl Vm { vm_try!(self.internal_infallible_bitwise_bool( Protocol::BIT_AND, i64::bitand, + u64::bitand, u8::bitand, bool::bitand, lhs, @@ -2229,6 +2295,7 @@ impl Vm { vm_try!(self.internal_infallible_bitwise_bool( Protocol::BIT_XOR, i64::bitxor, + u64::bitxor, u8::bitxor, bool::bitxor, lhs, @@ -2241,6 +2308,7 @@ impl Vm { vm_try!(self.internal_infallible_bitwise_bool( Protocol::BIT_OR, i64::bitor, + u64::bitor, u8::bitor, bool::bitor, lhs, @@ -2252,8 +2320,9 @@ impl Vm { vm_try!(self.internal_bitwise( Protocol::SHL, || VmErrorKind::Overflow, - |a, b| a.checked_shl(u32::try_from(b).ok()?), - |a, b| a.checked_shl(u32::try_from(b).ok()?), + i64::checked_shl, + u64::checked_shl, + u8::checked_shl, lhs, rhs, out, @@ -2263,8 +2332,9 @@ impl Vm { vm_try!(self.internal_bitwise( Protocol::SHR, || VmErrorKind::Underflow, - |a, b| a.checked_shr(u32::try_from(b).ok()?), - |a, b| a.checked_shr(u32::try_from(b).ok()?), + i64::checked_shr, + u64::checked_shr, + u8::checked_shr, lhs, rhs, out @@ -2366,6 +2436,7 @@ impl Vm { Protocol::ADD_ASSIGN, || VmErrorKind::Overflow, i64::checked_add, + u64::checked_add, ops::Add::add, value, )); @@ -2376,6 +2447,7 @@ impl Vm { Protocol::SUB_ASSIGN, || VmErrorKind::Underflow, i64::checked_sub, + u64::checked_sub, ops::Sub::sub, value, )); @@ -2386,6 +2458,7 @@ impl Vm { Protocol::MUL_ASSIGN, || VmErrorKind::Overflow, i64::checked_mul, + u64::checked_mul, ops::Mul::mul, value, )); @@ -2396,6 +2469,7 @@ impl Vm { Protocol::DIV_ASSIGN, || VmErrorKind::DivideByZero, i64::checked_div, + u64::checked_div, ops::Div::div, value, )); @@ -2406,6 +2480,7 @@ impl Vm { Protocol::REM_ASSIGN, || VmErrorKind::DivideByZero, i64::checked_rem, + u64::checked_rem, ops::Rem::rem, value, )); @@ -2417,6 +2492,7 @@ impl Vm { ops::BitAndAssign::bitand_assign, ops::BitAndAssign::bitand_assign, ops::BitAndAssign::bitand_assign, + ops::BitAndAssign::bitand_assign, value, )); } @@ -2427,6 +2503,7 @@ impl Vm { ops::BitXorAssign::bitxor_assign, ops::BitXorAssign::bitxor_assign, ops::BitXorAssign::bitxor_assign, + ops::BitXorAssign::bitxor_assign, value, )); } @@ -2437,6 +2514,7 @@ impl Vm { ops::BitOrAssign::bitor_assign, ops::BitOrAssign::bitor_assign, ops::BitOrAssign::bitor_assign, + ops::BitOrAssign::bitor_assign, value, )); } @@ -2445,8 +2523,9 @@ impl Vm { target, Protocol::SHL_ASSIGN, || VmErrorKind::Overflow, - |a, b| a.checked_shl(u32::try_from(b).ok()?), - |a, b| a.checked_shl(u32::try_from(b).ok()?), + i64::checked_shl, + u64::checked_shl, + u8::checked_shl, value, )); } @@ -2455,8 +2534,9 @@ impl Vm { target, Protocol::SHR_ASSIGN, || VmErrorKind::Underflow, - |a, b| a.checked_shr(u32::try_from(b).ok()?), - |a, b| a.checked_shr(u32::try_from(b).ok()?), + i64::checked_shr, + u64::checked_shr, + u8::checked_shr, value, )); } @@ -2629,13 +2709,8 @@ impl Vm { let target = vm_try!(self.stack.at(target)); match vm_try!(index.as_ref_repr()) { - RefRepr::Inline(Inline::Integer(index)) => { - let Ok(index) = usize::try_from(*index) else { - return err(VmErrorKind::MissingIndexInteger { - target: vm_try!(target.type_info()), - index: VmIntegerRepr::from(*index), - }); - }; + RefRepr::Inline(inline) => { + let index = vm_try!(inline.try_as_integer::()); if let Some(value) = vm_try!(Self::try_tuple_like_index_get(target, index)) { break 'store value; @@ -3062,11 +3137,24 @@ impl Vm { } #[cfg_attr(feature = "bench", inline(never))] - fn op_eq_integer(&mut self, addr: InstAddress, value: i64, out: Output) -> VmResult<()> { + fn op_eq_unsigned(&mut self, addr: InstAddress, value: u64, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); let is_match = match vm_try!(v.as_inline()) { - Some(Inline::Integer(actual)) => *actual == value, + Some(Inline::Unsigned(actual)) => *actual == value, + _ => false, + }; + + vm_try!(out.store(&mut self.stack, is_match)); + VmResult::Ok(()) + } + + #[cfg_attr(feature = "bench", inline(never))] + fn op_eq_signed(&mut self, addr: InstAddress, value: i64, out: Output) -> VmResult<()> { + let v = vm_try!(self.stack.at(addr)); + + let is_match = match vm_try!(v.as_inline()) { + Some(Inline::Signed(actual)) => *actual == value, _ => false, }; @@ -3878,8 +3966,11 @@ impl Vm { Inst::EqChar { addr, value, out } => { vm_try!(self.op_eq_character(addr, value, out)); } - Inst::EqInteger { addr, value, out } => { - vm_try!(self.op_eq_integer(addr, value, out)); + Inst::EqUnsigned { addr, value, out } => { + vm_try!(self.op_eq_unsigned(addr, value, out)); + } + Inst::EqSigned { addr, value, out } => { + vm_try!(self.op_eq_signed(addr, value, out)); } Inst::EqBool { addr, diff --git a/crates/rune/src/runtime/vm_error.rs b/crates/rune/src/runtime/vm_error.rs index 006dd6d1c..9a4c4c9e4 100644 --- a/crates/rune/src/runtime/vm_error.rs +++ b/crates/rune/src/runtime/vm_error.rs @@ -759,6 +759,9 @@ pub(crate) enum VmErrorKind { ExpectedAny { actual: TypeInfo, }, + ExpectedNumber { + actual: TypeInfo, + }, MissingConstantConstructor { hash: Hash, }, @@ -965,6 +968,9 @@ impl fmt::Display for VmErrorKind { VmErrorKind::ExpectedAny { actual } => { write!(f, "Expected `Any` type, but found `{actual}`") } + VmErrorKind::ExpectedNumber { actual } => { + write!(f, "Expected number type, but found `{actual}`") + } VmErrorKind::MissingConstantConstructor { hash } => { write!(f, "Missing constant constructor for type with hash {hash}") } diff --git a/crates/rune/src/tests/bug_344.rs b/crates/rune/src/tests/bug_344.rs index ed1356a0d..9416ed57f 100644 --- a/crates/rune/src/tests/bug_344.rs +++ b/crates/rune/src/tests/bug_344.rs @@ -31,7 +31,7 @@ fn bug_344_function() -> Result<()> { let mut stack = Stack::new(); stack.push(rune::to_value(GuardCheck::new())?)?; function(&mut stack, InstAddress::new(0), 1, Output::keep(0)).into_result()?; - assert_eq!(stack.at(InstAddress::new(0))?.as_integer()?, 42); + assert_eq!(stack.at(InstAddress::new(0))?.as_signed()?, 42); return Ok(()); fn function(check: &GuardCheck) -> i64 { @@ -67,7 +67,7 @@ fn bug_344_inst_fn() -> Result<()> { stack.push(rune::to_value(GuardCheck::new())?)?; function(&mut stack, InstAddress::new(0), 2, Output::keep(0)).into_result()?; - assert_eq!(stack.at(InstAddress::new(0))?.as_integer()?, 42); + assert_eq!(stack.at(InstAddress::new(0))?.as_signed()?, 42); Ok(()) } @@ -89,7 +89,7 @@ fn bug_344_async_function() -> Result<()> { stack.push(rune::to_value(GuardCheck::new())?)?; function(&mut stack, InstAddress::new(0), 1, Output::keep(0)).into_result()?; let future = stack.at(InstAddress::new(0))?.clone().into_future()?; - assert_eq!(block_on(future).into_result()?.as_integer()?, 42); + assert_eq!(block_on(future).into_result()?.as_signed()?, 42); return Ok(()); async fn function(check: Ref) -> i64 { @@ -126,7 +126,7 @@ fn bug_344_async_inst_fn() -> Result<()> { function(&mut stack, InstAddress::new(0), 2, Output::keep(0)).into_result()?; let future = stack.at(InstAddress::new(0))?.clone().into_future()?; - assert_eq!(block_on(future).into_result()?.as_integer()?, 42); + assert_eq!(block_on(future).into_result()?.as_signed()?, 42); Ok(()) } diff --git a/crates/rune/src/tests/bug_700.rs b/crates/rune/src/tests/bug_700.rs index 44d3214ff..c7b66ce68 100644 --- a/crates/rune/src/tests/bug_700.rs +++ b/crates/rune/src/tests/bug_700.rs @@ -42,7 +42,7 @@ pub fn test_bug_700() -> Result<()> { error.into_kind(), VmErrorKind::Expected { expected: TypeInfo::static_type(static_type::TUPLE), - actual: TypeInfo::static_type(static_type::INTEGER) + actual: TypeInfo::static_type(static_type::SIGNED) } ); diff --git a/crates/rune/src/tests/compiler_literals.rs b/crates/rune/src/tests/compiler_literals.rs index 70753f08c..a6896cf38 100644 --- a/crates/rune/src/tests/compiler_literals.rs +++ b/crates/rune/src/tests/compiler_literals.rs @@ -19,17 +19,17 @@ fn test_number_literals() { assert_errors! { r#"pub fn main() { -9223372036854775809 }"#, - span!(16, 36), BadNumberOutOfBounds { .. } + span!(16, 36), BadSignedOutOfBounds { .. } }; assert_parse!(r#"pub fn main() { 9223372036854775807 }"#); assert_errors! { r#"pub fn main() { 9223372036854775808 }"#, - span!(16, 35), BadNumberOutOfBounds { .. } + span!(16, 35), BadSignedOutOfBounds { .. } }; assert_errors! { r#"pub fn main() { 0b1000000000000000000000000000000000000000000000000000000000000000 }"#, - span!(16, 82), BadNumberOutOfBounds { .. } + span!(16, 82), BadSignedOutOfBounds { .. } }; } diff --git a/crates/rune/src/tests/unit_constants.rs b/crates/rune/src/tests/unit_constants.rs index 731c55894..4a086b9b1 100644 --- a/crates/rune/src/tests/unit_constants.rs +++ b/crates/rune/src/tests/unit_constants.rs @@ -16,7 +16,7 @@ fn test_get_const() -> Result<()> { unit.constant(Hash::type_hash(["LEET"])) .context("missing constant")? .to_value()? - .as_integer()?, + .as_signed()?, 1337 ); Ok(()) @@ -42,7 +42,7 @@ fn test_get_const_re_export() -> Result<()> { unit.constant(Hash::type_hash(["LEET"])) .context("missing constant")? .to_value()? - .as_integer()?, + .as_signed()?, 1337 ); Ok(()) @@ -67,7 +67,7 @@ fn test_get_const_nested() -> Result<()> { .expect("successful lookup") .to_value() .expect("could not allocate value") - .as_integer() + .as_signed() .expect("the inner value"), 1337 ); diff --git a/crates/rune/src/tests/vm_function.rs b/crates/rune/src/tests/vm_function.rs index 79a0c95bb..c93376aec 100644 --- a/crates/rune/src/tests/vm_function.rs +++ b/crates/rune/src/tests/vm_function.rs @@ -59,7 +59,7 @@ fn test_function() { assert!(function.call::((1i64,)).into_result().is_err()); let value: Value = function.call((1i64, 2i64)).unwrap(); - assert_eq!(value.as_integer().unwrap(), 3); + assert_eq!(value.as_signed().unwrap(), 3); // closure with captures let function: Function = run( @@ -72,5 +72,5 @@ fn test_function() { assert!(function.call::((1i64,)).into_result().is_err()); let value: Value = function.call(()).unwrap(); - assert_eq!(value.as_integer().unwrap(), 3); + assert_eq!(value.as_signed().unwrap(), 3); }