Auto merge of #79342 - CDirkx:ipaddr-const, r=oli-obk

Stabilize all stable methods of `Ipv4Addr`, `Ipv6Addr` and `IpAddr` as const

This PR stabilizes all currently stable methods of `Ipv4Addr`, `Ipv6Addr` and `IpAddr` as const.
Tracking issue: #76205

`Ipv4Addr` (`const_ipv4`):
 - `octets`
 - `is_loopback`
 - `is_private`
 - `is_link_local`
 - `is_multicast`
 - `is_broadcast`
 - `is_docmentation`
 - `to_ipv6_compatible`
 - `to_ipv6_mapped`

`Ipv6Addr` (`const_ipv6`):
 - `segments`
 - `is_unspecified`
 - `is_loopback`
 - `is_multicast`
 - `to_ipv4`

`IpAddr` (`const_ip`):
 - `is_unspecified`
 - `is_loopback`
 - `is_multicast`

## Motivation
The ip methods seem like prime candidates to be made const: their behavior is defined by an external spec, and based solely on the byte contents of an address. These methods have been made unstable const in the beginning of September, after the necessary const integer arithmetic was stabilized.

There is currently a PR open (#78802) to change the internal representation of `IpAddr{4,6}` from `libc` types to a byte array. This does not have any impact on the constness of the methods.

## Implementation
Most of the stabilizations are straightforward, with the exception of `Ipv6Addr::segments`, which uses the unstable feature `const_fn_transmute`. The code could be rewritten to equivalent stable code, but this leads to worse code generation (#75085).
This is why `segments` gets marked with `#[rustc_allow_const_fn_unstable(const_fn_transmute)]`, like the already const-stable `Ipv6Addr::new`, the justification being that a const-stable alternative implementation exists https://github.com/rust-lang/rust/pull/76206#issuecomment-685044184.

## Future posibilities
This PR const-stabilizes all currently stable ip methods, however there are also a number of unstable methods under the `ip` feature (#27709). These methods are already unstable const. There is a PR open (#76098) to stabilize those methods, which could include const-stabilization. However, stabilizing those methods as const is dependent on `Ipv4Addr::octets` and `Ipv6Addr::segments` (covered by this PR).
This commit is contained in:
bors 2020-12-19 13:13:41 +00:00
commit bd2f1cb278

View File

@ -148,7 +148,7 @@ impl IpAddr {
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true);
/// ```
#[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[rustc_const_stable(feature = "const_ip", since = "1.50.0")]
#[stable(feature = "ip_shared", since = "1.12.0")]
pub const fn is_unspecified(&self) -> bool {
match self {
@ -170,7 +170,7 @@ pub const fn is_unspecified(&self) -> bool {
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true);
/// ```
#[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[rustc_const_stable(feature = "const_ip", since = "1.50.0")]
#[stable(feature = "ip_shared", since = "1.12.0")]
pub const fn is_loopback(&self) -> bool {
match self {
@ -215,7 +215,7 @@ pub const fn is_global(&self) -> bool {
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true);
/// ```
#[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[rustc_const_stable(feature = "const_ip", since = "1.50.0")]
#[stable(feature = "ip_shared", since = "1.12.0")]
pub const fn is_multicast(&self) -> bool {
match self {
@ -301,8 +301,8 @@ impl Ipv4Addr {
///
/// let addr = Ipv4Addr::new(127, 0, 0, 1);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
// `s_addr` is stored as BE on all machine and the array is in BE order.
// So the native endian conversion method is used so that it's never swapped.
@ -358,7 +358,7 @@ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
/// let addr = Ipv4Addr::new(127, 0, 0, 1);
/// assert_eq!(addr.octets(), [127, 0, 0, 1]);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn octets(&self) -> [u8; 4] {
// This returns the order we want because s_addr is stored in big-endian.
@ -380,8 +380,8 @@ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
/// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true);
/// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false);
/// ```
#[stable(feature = "ip_shared", since = "1.12.0")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")]
#[stable(feature = "ip_shared", since = "1.12.0")]
pub const fn is_unspecified(&self) -> bool {
self.inner.s_addr == 0
}
@ -400,7 +400,7 @@ pub const fn is_unspecified(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
/// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_loopback(&self) -> bool {
self.octets()[0] == 127
@ -429,7 +429,7 @@ pub const fn is_loopback(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
/// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_private(&self) -> bool {
match self.octets() {
@ -455,7 +455,7 @@ pub const fn is_private(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true);
/// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_link_local(&self) -> bool {
matches!(self.octets(), [169, 254, ..])
@ -675,7 +675,7 @@ pub const fn is_reserved(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true);
/// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_multicast(&self) -> bool {
self.octets()[0] >= 224 && self.octets()[0] <= 239
@ -695,7 +695,7 @@ pub const fn is_multicast(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true);
/// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_broadcast(&self) -> bool {
u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets())
@ -721,7 +721,7 @@ pub const fn is_broadcast(&self) -> bool {
/// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true);
/// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_documentation(&self) -> bool {
match self.octets() {
@ -751,7 +751,7 @@ pub const fn is_documentation(&self) -> bool {
/// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 767)
/// );
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
let [a, b, c, d] = self.octets();
@ -774,7 +774,7 @@ pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
/// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(),
/// Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 49152, 767));
/// ```
#[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn to_ipv6_mapped(&self) -> Ipv6Addr {
let [a, b, c, d] = self.octets();
@ -1043,9 +1043,9 @@ impl Ipv6Addr {
///
/// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")]
#[rustc_allow_const_fn_unstable(const_fn_transmute)]
#[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
let addr16 = [
a.to_be(),
@ -1061,6 +1061,8 @@ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16)
inner: c::in6_addr {
// All elements in `addr16` are big endian.
// SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`.
// rustc_allow_const_fn_unstable: the transmute could be written as stable const
// code, but that leads to worse code generation (#75085)
s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) },
},
}
@ -1102,11 +1104,14 @@ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16)
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
/// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
/// ```
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[rustc_allow_const_fn_unstable(const_fn_transmute)]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn segments(&self) -> [u16; 8] {
// All elements in `s6_addr` must be big endian.
// SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`.
// rustc_allow_const_fn_unstable: the transmute could be written as stable const code, but
// that leads to worse code generation (#75085)
let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) };
// We want native endian u16
[
@ -1135,7 +1140,7 @@ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16)
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true);
/// ```
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_unspecified(&self) -> bool {
u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
@ -1155,7 +1160,7 @@ pub const fn is_unspecified(&self) -> bool {
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true);
/// ```
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_loopback(&self) -> bool {
u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
@ -1465,7 +1470,7 @@ pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
/// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false);
/// ```
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(since = "1.7.0", feature = "ip_17")]
pub const fn is_multicast(&self) -> bool {
(self.segments()[0] & 0xff00) == 0xff00
@ -1520,7 +1525,7 @@ pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(),
/// Some(Ipv4Addr::new(0, 0, 0, 1)));
/// ```
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() {
@ -1540,8 +1545,8 @@ pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
/// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(),
/// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// ```
#[stable(feature = "ipv6_to_octets", since = "1.12.0")]
#[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")]
#[stable(feature = "ipv6_to_octets", since = "1.12.0")]
pub const fn octets(&self) -> [u8; 16] {
self.inner.s6_addr
}