Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the aview0, aview1, and aview2 free functions be const fns #1132

Merged
merged 5 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- stable
- beta
- nightly
- 1.51.0 # MSRV
- 1.57.0 # MSRV

name: tests/${{ matrix.rust }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
name = "ndarray"
version = "0.15.6"
edition = "2018"
rust-version = "1.51"
rust-version = "1.57"
authors = [
"Ulrik Sverdrup \"bluss\"",
"Jim Turner"
Expand Down
2 changes: 1 addition & 1 deletion scripts/all-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -e
FEATURES=$1
CHANNEL=$2

if [ "$CHANNEL" = "1.51.0" ]; then
if [ "$CHANNEL" = "1.57.0" ]; then
cargo update --package openblas-src --precise 0.10.5
cargo update --package openblas-build --precise 0.10.5
cargo update --package once_cell --precise 1.14.0
Expand Down
14 changes: 7 additions & 7 deletions src/aliases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,43 @@ use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl};
/// Create a zero-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix0() -> Ix0 {
pub const fn Ix0() -> Ix0 {
Dim::new([])
}
/// Create a one-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix1(i0: Ix) -> Ix1 {
pub const fn Ix1(i0: Ix) -> Ix1 {
Dim::new([i0])
}
/// Create a two-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix2(i0: Ix, i1: Ix) -> Ix2 {
pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 {
Dim::new([i0, i1])
}
/// Create a three-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 {
pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 {
Dim::new([i0, i1, i2])
}
/// Create a four-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 {
pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 {
Dim::new([i0, i1, i2, i3])
}
/// Create a five-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 {
pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 {
Dim::new([i0, i1, i2, i3, i4])
}
/// Create a six-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 {
pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 {
Dim::new([i0, i1, i2, i3, i4, i5])
}

Expand Down
33 changes: 6 additions & 27 deletions src/arraytraits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ pub const ARRAY_FORMAT_VERSION: u8 = 1u8;

// use "raw" form instead of type aliases here so that they show up in docs
/// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable.
///
/// **Panics** if the length of the slice overflows `isize`. (This can only
/// occur if `A` is zero-sized, because slices cannot contain more than
/// `isize::MAX` number of bytes.)
impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1>
where
Slice: AsRef<[A]>,
Expand All @@ -315,14 +319,7 @@ where
///
/// **Panics** if the slice length is greater than `isize::MAX`.
fn from(slice: &'a Slice) -> Self {
let xs = slice.as_ref();
if mem::size_of::<A>() == 0 {
assert!(
xs.len() <= ::std::isize::MAX as usize,
"Slice length must fit in `isize`.",
);
}
unsafe { Self::from_shape_ptr(xs.len(), xs.as_ptr()) }
aview1(slice.as_ref())
}
}

Expand All @@ -334,25 +331,7 @@ where
impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> {
/// Create a two-dimensional read-only array view of the data in `slice`
fn from(xs: &'a [[A; N]]) -> Self {
let cols = N;
let rows = xs.len();
let dim = Ix2(rows, cols);
if size_of::<A>() == 0 {
dimension::size_of_shape_checked(&dim)
.expect("Product of non-zero axis lengths must not overflow isize.");
} else if N == 0 {
assert!(
xs.len() <= isize::MAX as usize,
"Product of non-zero axis lengths must not overflow isize.",
);
}

// `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in
// `isize::MAX`
unsafe {
let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows);
ArrayView::from_shape_ptr(dim, data.as_ptr())
}
aview2(xs)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/dimension/dim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct Dim<I: ?Sized> {

impl<I> Dim<I> {
/// Private constructor and accessors for Dim
pub(crate) fn new(index: I) -> Dim<I> {
pub(crate) const fn new(index: I) -> Dim<I> {
Dim { index }
}
#[inline(always)]
Expand Down
92 changes: 83 additions & 9 deletions src/free_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use std::mem::{forget, size_of};
use std::ptr::NonNull;

use crate::imp_prelude::*;
use crate::{dimension, ArcArray1, ArcArray2};
Expand Down Expand Up @@ -65,32 +66,105 @@ pub fn rcarr1<A: Clone>(xs: &[A]) -> ArcArray1<A> {
}

/// Create a zero-dimensional array view borrowing `x`.
pub fn aview0<A>(x: &A) -> ArrayView0<'_, A> {
unsafe { ArrayView::from_shape_ptr(Ix0(), x) }
pub const fn aview0<A>(x: &A) -> ArrayView0<'_, A> {
ArrayBase {
data: ViewRepr::new(),
// Safe because references are always non-null.
ptr: unsafe { NonNull::new_unchecked(x as *const A as *mut A) },
dim: Ix0(),
strides: Ix0(),
}
}

/// Create a one-dimensional array view with elements borrowing `xs`.
///
/// **Panics** if the length of the slice overflows `isize`. (This can only
/// occur if `A` is zero-sized, because slices cannot contain more than
/// `isize::MAX` number of bytes.)
///
/// ```
/// use ndarray::aview1;
/// use ndarray::{aview1, ArrayView1};
///
/// let data = [1.0; 1024];
///
/// // Create a 2D array view from borrowed data
/// let a2d = aview1(&data).into_shape((32, 32)).unwrap();
///
/// assert_eq!(a2d.sum(), 1024.0);
///
/// // Create a const 1D array view
/// const C: ArrayView1<'static, f64> = aview1(&[1., 2., 3.]);
///
/// assert_eq!(C.sum(), 6.);
/// ```
pub fn aview1<A>(xs: &[A]) -> ArrayView1<'_, A> {
ArrayView::from(xs)
pub const fn aview1<A>(xs: &[A]) -> ArrayView1<'_, A> {
if size_of::<A>() == 0 {
assert!(
xs.len() <= isize::MAX as usize,
"Slice length must fit in `isize`.",
);
}
ArrayBase {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to use the internal builder methods, with_data_ptr etc? We'd prefer those instead of spreading out ArrayBase { .. } constructors again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I try to make from_data_ptr into a const fn, then I get the following compilation errors:

error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
  --> src/impl_internal_constructors.rs:14:9
   |
14 | impl<A, S> ArrayBase<S, Ix1>
   |         ^
...
26 |     pub(crate) const unsafe fn from_data_ptr(data: S, ptr: NonNull<A>) -> Self {
   |     -------------------------------------------------------------------------- function declared as const here
   |
   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information

error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
  --> src/impl_internal_constructors.rs:33:23
   |
33 |         debug_assert!(array.pointer_is_inbounds());
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0493]: destructors cannot be evaluated at compile-time
  --> src/impl_internal_constructors.rs:27:13
   |
27 |         let array = ArrayBase {
   |             ^^^^^ constant functions cannot evaluate destructors
...
35 |     }
   |     - value is dropped here

The biggest issue is the generic S type. I don't see a way to fix the errors while keeping a generic S. We could add const constructors to impl_internal_constructors.rs specifically for ArrayView, though.

data: ViewRepr::new(),
// Safe because references are always non-null.
ptr: unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) },
dim: Ix1(xs.len()),
strides: Ix1(1),
}
}

/// Create a two-dimensional array view with elements borrowing `xs`.
///
/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A
/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes).
pub fn aview2<A, const N: usize>(xs: &[[A; N]]) -> ArrayView2<'_, A> {
ArrayView2::from(xs)
/// **Panics** if the product of non-zero axis lengths overflows `isize` (This
/// can only occur if A is zero-sized or if `N` is zero, because slices cannot
/// contain more than `isize::MAX` number of bytes).
///
/// ```
/// use ndarray::{aview2, ArrayView2};
///
/// let data = vec![[1., 2., 3.], [4., 5., 6.]];
///
/// let view = aview2(&data);
/// assert_eq!(view.sum(), 21.);
///
/// // Create a const 2D array view
/// const C: ArrayView2<'static, f64> = aview2(&[[1., 2., 3.], [4., 5., 6.]]);
/// assert_eq!(C.sum(), 21.);
/// ```
pub const fn aview2<A, const N: usize>(xs: &[[A; N]]) -> ArrayView2<'_, A> {
let cols = N;
let rows = xs.len();
if size_of::<A>() == 0 {
if let Some(n_elems) = rows.checked_mul(cols) {
assert!(
rows <= isize::MAX as usize
&& cols <= isize::MAX as usize
&& n_elems <= isize::MAX as usize,
"Product of non-zero axis lengths must not overflow isize.",
);
} else {
panic!("Overflow in number of elements.");
}
} else if N == 0 {
assert!(
rows <= isize::MAX as usize,
"Product of non-zero axis lengths must not overflow isize.",
);
}
// Safe because references are always non-null.
let ptr = unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) };
let dim = Ix2(rows, cols);
let strides = if rows == 0 || cols == 0 {
Ix2(0, 0)
} else {
Ix2(cols, 1)
};
ArrayBase {
data: ViewRepr::new(),
ptr,
dim,
strides,
}
}

/// Create a one-dimensional read-write array view with elements borrowing `xs`.
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
//! needs matching memory layout to be efficient (with some exceptions).
//! + Efficient floating point matrix multiplication even for very large
//! matrices; can optionally use BLAS to improve it further.
//! - **Requires Rust 1.51 or later**
//! - **Requires Rust 1.57 or later**
//!
//! ## Crate Feature Flags
//!
Expand Down Expand Up @@ -1450,7 +1450,7 @@ pub struct RawViewRepr<A> {

impl<A> RawViewRepr<A> {
#[inline(always)]
fn new() -> Self {
const fn new() -> Self {
RawViewRepr { ptr: PhantomData }
}
}
Expand All @@ -1467,7 +1467,7 @@ pub struct ViewRepr<A> {

impl<A> ViewRepr<A> {
#[inline(always)]
fn new() -> Self {
const fn new() -> Self {
ViewRepr { life: PhantomData }
}
}
Expand Down