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

Tracking Issue for NonZero_c_* integers #82363

Closed
1 of 4 tasks
KodrAus opened this issue Feb 21, 2021 · 14 comments · Fixed by #120295
Closed
1 of 4 tasks

Tracking Issue for NonZero_c_* integers #82363

KodrAus opened this issue Feb 21, 2021 · 14 comments · Fixed by #120295
Labels
A-FFI Area: Foreign function interface (FFI) C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@KodrAus
Copy link
Contributor

KodrAus commented Feb 21, 2021

Feature gate: #![feature(raw_os_nonzero)]

This is a tracking issue for non-zero variants of c_int type aliases.

Public API

pub type NonZero_c_char = NonZero?8;
pub type NonZero_c_schar = NonZeroI8;
pub type NonZero_c_uchar = NonZeroU8;
pub type NonZero_c_short = NonZeroI16;
pub type NonZero_c_ushort = NonZeroU16;
pub type NonZero_c_int = NonZeroI32;
pub type NonZero_c_uint = NonZeroU32;
#[cfg(any(target_pointer_width = "32", windows))]
pub type NonZero_c_long = NonZeroI32;
#[cfg(any(target_pointer_width = "32", windows))]
pub type NonZero_c_ulong = NonZeroU32;
#[cfg(all(target_pointer_width = "64", not(windows)))]
pub type NonZero_c_long = NonZeroI64;
#[cfg(all(target_pointer_width = "64", not(windows)))]
pub type NonZero_c_ulong = NonZeroU64;
pub type NonZero_c_longlong = NonZeroI64;
pub type NonZero_c_ulonglong = NonZeroU64;

Steps / History

Unresolved Questions

  • Naming.....
@KodrAus KodrAus added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC labels Feb 21, 2021
@tesuji

This comment has been minimized.

@jonas-schievink jonas-schievink added the A-FFI Area: Foreign function interface (FFI) label Feb 21, 2021
@joshtriplett
Copy link
Member

FCP for stabilization:

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Aug 25, 2021

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Aug 25, 2021
@dtolnay
Copy link
Member

dtolnay commented Aug 26, 2021

NonZero_c_ulonglong

I can't say that I am thrilled about Pascal_snake_concatwords-case. Can we reconsider doing a generic type alias instead?

// core::num

/// NonZero<i8> = NonZeroI8
/// NonZero<i16> = NonZeroI16
/// etc
pub type NonZero<N> = <N as private::Primitive>::NonZero;

This would automatically be usable as NonZero<c_char> = NonZeroI8/NonZeroU8 (based on platform).

Note we don't need to make any underlying trait publicly accessible. This is not for writing generic code over all different nonzero types. It is just for saner spelling of the type names.

@joshtriplett
Copy link
Member

If that kind of generic is possible, I'm all for it. Is it?

@Amanieu
Copy link
Member

Amanieu commented Aug 26, 2021

I also think that these names are very ugly and would vastly prefer a generic NonZero type based on a sealed trait.

@rfcbot concern generic NonZero

@kennytm
Copy link
Member

kennytm commented Sep 17, 2021

IMO introducing AtomicT/NonZeroT replacing Atomic<T>/NonZero<T> are the gravest API mistake Rust has ever made so +1 to introducing NonZero<T> again.

The 12 NonZeroT types are introduced in rust-lang/rfcs#2307 was to sidestep the question what should happen to NonZero<CustomType>, taking the precedent of AtomicT (but disregarding Wrapping<T>).

One problem of the current API is that it is unclear what happens or what should happen to NonZero<T> or Option<NonZero<T>> when T is some type other than a raw pointer or a primitive integer. In particular, crates outside of std can implement Zeroable for their arbitrary types since it is a public trait.

To avoid this question entirely, this RFC proposes replacing the generic type and trait with twelve concrete types in std::num, one for each primitive integer type. This is similar to the existing atomic integer types like std::sync::atomic::AtomicU32.

The Motivation no longer applies since, as demonstrated in in #88990, we can make the bounding trait IntegerPrimitive (previously Zeroable) a private/sealed trait. During FCP SimonSapin did mention (in rust-lang/rfcs#2307 (comment) and rust-lang/rfcs#2307 (comment)) why this route was not considered, mainly because of type checker bugs and personal taste. It was agreed in rust-lang/rfcs#2307 (comment) that the question won't block the FCP-merge. Yet the question of generics was not checkboxed in #49137 before stabilization of NonZeroT.


Now that this issue shows that generics is greatly favored, I think we should better do it properly by turning NonZero<T> back to a struct, and NonZeroT direct type aliases, rather than the current implementation that NonZeroT are concrete types and NonZero<T> being an alias going jumping through associated types.

// no reexport
pub trait IntegerPrimitive: Sized + Copy + Ord + Hash + Default + BitOr<Output=Self> + Debug + Display + Binary + Octal + LowerHex + UpperHex {}

#[unstable(...)] 
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonZero<T: IntegerPrimitive>(T);

impl<T: IntegerPrimitive> NonZero<T> {
    #[stable(..)]
    pub const unsafe fn new_unchecked(n: T) -> Self { unsafe { Self(n) } }
    pub const fn new(n: T) -> Option<Self> { (n != T::default()).then(|| unsafe { Self(n) }) }
    pub const fn get(self) -> T { self.0 }
}

#[stable(..)]
impl<T: IntegerPrimitive> BitOr for NonZero<T> {}

//... etc ...

#[stable(..)]
pub type NonZeroU8 = NonZero<u8>;
pub type NonZeroU16 = NonZero<u16>;
...
pub type NonZeroI128 = NonZero<i128>;
pub type NonZeroIsize = NonZero<isize>;

the inherent functions leading_zeros, trailing_zeros, {checked,saturating,unchecked}_{add,mul,pow}, checked_next_power_of_two, abs, {checked,overflowing,saturating,wrapping,unsigned}_abs, is_power_of_two still require per-instance impl by macro.

@joshtriplett
Copy link
Member

I'd be happy to review a pull request adding a generic NonZero based on a sealed trait.

@joshtriplett
Copy link
Member

Cancelling in favor of generic NonZero.
@rfcbot cancel

@rfcbot
Copy link

rfcbot commented Oct 5, 2021

@joshtriplett proposal cancelled.

@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Oct 5, 2021
@CAD97
Copy link
Contributor

CAD97 commented Jul 17, 2022

Just as a note: std is missing a reëxport of these names from core.

@reitermarkus
Copy link
Contributor

I had a go at implementing a generic NonZero type here #100428.

@briansmith
Copy link
Contributor

NonZero_c_size_t and NonZero_c_ptrdiff_t are missing from core::ffi.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 19, 2024
Consolidate all associated items on the NonZero integer types into a single impl block per type

**Before:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
}

impl NonZeroI8 {
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
}

impl NonZeroI8 {
    pub const fn abs(self) -> NonZeroI8 ...
}
...
```

**After:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
    pub const fn abs(self) -> NonZeroI8 ...
    ...
}
```

Having 6-7 different impl blocks per type is not such a problem in today's implementation, but becomes awful upon the switch to a generic `NonZero<T>` type (context: rust-lang#82363 (comment)).

In the implementation from rust-lang#100428, there end up being **67** impl blocks on that type.

<img src="https://github.com/rust-lang/rust/assets/1940490/5b68bd6f-8a36-4922-baa3-348e30dbfcc1" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2cfec71e-c2cd-4361-a542-487f13f435d9" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2fe00337-7307-405d-9036-6fe1e58b2627" width="200">

Without the refactor to a single impl block first, introducing `NonZero<T>` would be a usability regression compared to today's separate pages per type. With all those blocks expanded, Ctrl+F is obnoxious because you need to skip 12&times; past every match you don't care about. With all the blocks collapsed, Ctrl+F is useless. Getting to a state in which exactly one type's (e.g. `NonZero<u32>`) impl blocks are expanded while the rest are collapsed is annoying.

After this refactor to a single impl block, we can move forward with making `NonZero<T>` a generic struct whose docs all go on the same rustdoc page. The rustdoc will have 12 impl blocks, one per choice of `T` supported by the standard library. The reader can expand a single one of those impl blocks e.g. `NonZero<u32>` to understand the entire API of that type.

Note that moving the API into a generic `impl<T> NonZero<T> { ... }` is not going to be an option until after `NonZero<T>` has been stabilized, which may be months or years after its introduction. During the period while generic `NonZero` is unstable, it will be extra important to offer good documentation on all methods demonstrating the API being used through the stable aliases such as `NonZeroI8`.

This PR follows a `key = $value` syntax for the macros which is similar to the macros we already use for producing a single large impl block on the integer primitives.

https://github.com/rust-lang/rust/blob/1dd4db50620fb38a6382c22456a96ed7cddeff83/library/core/src/num/mod.rs#L288-L309

Best reviewed one commit at a time.
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jan 19, 2024
Rollup merge of rust-lang#118665 - dtolnay:signedness, r=Nilstrieb

Consolidate all associated items on the NonZero integer types into a single impl block per type

**Before:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
}

impl NonZeroI8 {
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
}

impl NonZeroI8 {
    pub const fn abs(self) -> NonZeroI8 ...
}
...
```

**After:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
    pub const fn abs(self) -> NonZeroI8 ...
    ...
}
```

Having 6-7 different impl blocks per type is not such a problem in today's implementation, but becomes awful upon the switch to a generic `NonZero<T>` type (context: rust-lang#82363 (comment)).

In the implementation from rust-lang#100428, there end up being **67** impl blocks on that type.

<img src="https://github.com/rust-lang/rust/assets/1940490/5b68bd6f-8a36-4922-baa3-348e30dbfcc1" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2cfec71e-c2cd-4361-a542-487f13f435d9" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2fe00337-7307-405d-9036-6fe1e58b2627" width="200">

Without the refactor to a single impl block first, introducing `NonZero<T>` would be a usability regression compared to today's separate pages per type. With all those blocks expanded, Ctrl+F is obnoxious because you need to skip 12&times; past every match you don't care about. With all the blocks collapsed, Ctrl+F is useless. Getting to a state in which exactly one type's (e.g. `NonZero<u32>`) impl blocks are expanded while the rest are collapsed is annoying.

After this refactor to a single impl block, we can move forward with making `NonZero<T>` a generic struct whose docs all go on the same rustdoc page. The rustdoc will have 12 impl blocks, one per choice of `T` supported by the standard library. The reader can expand a single one of those impl blocks e.g. `NonZero<u32>` to understand the entire API of that type.

Note that moving the API into a generic `impl<T> NonZero<T> { ... }` is not going to be an option until after `NonZero<T>` has been stabilized, which may be months or years after its introduction. During the period while generic `NonZero` is unstable, it will be extra important to offer good documentation on all methods demonstrating the API being used through the stable aliases such as `NonZeroI8`.

This PR follows a `key = $value` syntax for the macros which is similar to the macros we already use for producing a single large impl block on the integer primitives.

https://github.com/rust-lang/rust/blob/1dd4db50620fb38a6382c22456a96ed7cddeff83/library/core/src/num/mod.rs#L288-L309

Best reviewed one commit at a time.
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Jan 21, 2024
Consolidate all associated items on the NonZero integer types into a single impl block per type

**Before:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
}

impl NonZeroI8 {
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
}

impl NonZeroI8 {
    pub const fn abs(self) -> NonZeroI8 ...
}
...
```

**After:**

```rust
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
pub struct NonZeroI8(i8);

impl NonZeroI8 {
    pub const fn new(n: i8) -> Option<Self> ...
    pub const fn get(self) -> i8 ...
    pub const fn leading_zeros(self) -> u32 ...
    pub const fn trailing_zeros(self) -> u32 ...
    pub const fn abs(self) -> NonZeroI8 ...
    ...
}
```

Having 6-7 different impl blocks per type is not such a problem in today's implementation, but becomes awful upon the switch to a generic `NonZero<T>` type (context: rust-lang/rust#82363 (comment)).

In the implementation from rust-lang/rust#100428, there end up being **67** impl blocks on that type.

<img src="https://github.com/rust-lang/rust/assets/1940490/5b68bd6f-8a36-4922-baa3-348e30dbfcc1" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2cfec71e-c2cd-4361-a542-487f13f435d9" width="200"><img src="https://github.com/rust-lang/rust/assets/1940490/2fe00337-7307-405d-9036-6fe1e58b2627" width="200">

Without the refactor to a single impl block first, introducing `NonZero<T>` would be a usability regression compared to today's separate pages per type. With all those blocks expanded, Ctrl+F is obnoxious because you need to skip 12&times; past every match you don't care about. With all the blocks collapsed, Ctrl+F is useless. Getting to a state in which exactly one type's (e.g. `NonZero<u32>`) impl blocks are expanded while the rest are collapsed is annoying.

After this refactor to a single impl block, we can move forward with making `NonZero<T>` a generic struct whose docs all go on the same rustdoc page. The rustdoc will have 12 impl blocks, one per choice of `T` supported by the standard library. The reader can expand a single one of those impl blocks e.g. `NonZero<u32>` to understand the entire API of that type.

Note that moving the API into a generic `impl<T> NonZero<T> { ... }` is not going to be an option until after `NonZero<T>` has been stabilized, which may be months or years after its introduction. During the period while generic `NonZero` is unstable, it will be extra important to offer good documentation on all methods demonstrating the API being used through the stable aliases such as `NonZeroI8`.

This PR follows a `key = $value` syntax for the macros which is similar to the macros we already use for producing a single large impl block on the integer primitives.

https://github.com/rust-lang/rust/blob/1dd4db50620fb38a6382c22456a96ed7cddeff83/library/core/src/num/mod.rs#L288-L309

Best reviewed one commit at a time.
@bors bors closed this as completed in 4f4ceef Jan 30, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jan 30, 2024
Rollup merge of rust-lang#120295 - reitermarkus:remove-ffi-nonzero, r=dtolnay

Remove `raw_os_nonzero` feature.

This feature is superseded by a generic `NonZero` type: rust-lang#120257

Closes rust-lang#82363.
@dtolnay
Copy link
Member

dtolnay commented Jan 30, 2024

Superseded by #120257.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-FFI Area: Foreign function interface (FFI) C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.