Repository: rust-lang/rust
Pull request: Make `Ipv6Addr::multicast_scope()` exhaustive

And make `Ipv6MulticastScope` exhaustive, with `repr(u8)` and the proper discriminant values. The variants for the two reserved scopes are gated behind a perma-unstable feature, in order to avoid committing to names for them.

[IETF RFC 4291](https://datatracker.ietf.org/doc/html/rfc4291#section-2.7) and [IETF RFC 7346](https://datatracker.ietf.org/doc/html/rfc7346#section-2) define the list of multicast scopes. The former document says:

> scopes labeled "(unassigned)" are available for administrators to define additional multicast regions.

Tracking issue: https://github.com/rust-lang/rust/issues/27709
@rustbot label A-io

<!-- homu-ignore:start -->
<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r? <reviewer name>
-->
<!-- homu-ignore:end -->


Changed files:
filename: library/core/src/net/ip_addr.rs
patch: @@ -177,15 +177,17 @@ impl Hash for Ipv6Addr {
     }
 }
 
-/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
+/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2],
+/// which updates [IETF RFC 4291 section 2.7].
 ///
 /// # Stability Guarantees
 ///
-/// Not all possible values for a multicast scope have been assigned.
-/// Future RFCs may introduce new scopes, which will be added as variants to this enum;
-/// because of this the enum is marked as `#[non_exhaustive]`.
+/// Scopes 0 and F are currently reserved by IETF, and may be assigned in the future.
+/// For this reason, the enum variants for those two scopes are not currently nameable.
+/// You can still check for them in your code using `as` casts.
 ///
 /// # Examples
+///
 /// ```
 /// #![feature(ip)]
 ///
@@ -204,32 +206,76 @@ impl Hash for Ipv6Addr {
 ///     Some(SiteLocal) => println!("Site-Local scope"),
 ///     Some(OrganizationLocal) => println!("Organization-Local scope"),
 ///     Some(Global) => println!("Global scope"),
-///     Some(_) => println!("Unknown scope"),
+///     Some(s) => {
+///         let snum = s as u8;
+///         if matches!(0x0 | 0xF, snum) {
+///             println!("Reserved scope {snum:X}")
+///         } else {
+///             println!("Unassigned scope {snum:X}")
+///         }
+///     }
 ///     None => println!("Not a multicast address!")
 /// }
-///
 /// ```
 ///
 /// [IPv6 multicast address]: Ipv6Addr
 /// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2
-#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
+/// [IETF RFC 4291 section 2.7]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.7
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[unstable(feature = "ip", issue = "27709")]
-#[non_exhaustive]
 pub enum Ipv6MulticastScope {
+    /// Reserved by IETF.
+    #[doc(hidden)]
+    #[unstable(
+        feature = "ip_multicast_reserved",
+        reason = "not yet assigned by IETF",
+        issue = "none"
+    )]
+    Reserved0 = 0x0,
     /// Interface-Local scope.
-    InterfaceLocal,
+    InterfaceLocal = 0x1,
     /// Link-Local scope.
-    LinkLocal,
+    LinkLocal = 0x2,
     /// Realm-Local scope.
-    RealmLocal,
+    RealmLocal = 0x3,
     /// Admin-Local scope.
-    AdminLocal,
+    AdminLocal = 0x4,
     /// Site-Local scope.
-    SiteLocal,
+    SiteLocal = 0x5,
+
+    /// Scope 6. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    Unassigned6 = 0x6,
+    /// Scope 7. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    Unassigned7 = 0x7,
     /// Organization-Local scope.
-    OrganizationLocal,
+    OrganizationLocal = 0x8,
+    /// Scope 9. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    Unassigned9 = 0x9,
+    /// Scope A. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    UnassignedA = 0xA,
+    /// Scope B. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    UnassignedB = 0xB,
+    /// Scope C. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    UnassignedC = 0xC,
+    /// Scope D. Unassigned, available for administrators
+    /// to define additional multicast regions.
+    UnassignedD = 0xD,
     /// Global scope.
-    Global,
+    Global = 0xE,
+    /// Reserved by IETF.
+    #[doc(hidden)]
+    #[unstable(
+        feature = "ip_multicast_reserved",
+        reason = "not yet assigned by IETF",
+        issue = "none"
+    )]
+    ReservedF = 0xF,
 }
 
 impl IpAddr {
@@ -1848,14 +1894,23 @@ impl Ipv6Addr {
     pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
         if self.is_multicast() {
             match self.segments()[0] & 0x000f {
-                1 => Some(Ipv6MulticastScope::InterfaceLocal),
-                2 => Some(Ipv6MulticastScope::LinkLocal),
-                3 => Some(Ipv6MulticastScope::RealmLocal),
-                4 => Some(Ipv6MulticastScope::AdminLocal),
-                5 => Some(Ipv6MulticastScope::SiteLocal),
-                8 => Some(Ipv6MulticastScope::OrganizationLocal),
-                14 => Some(Ipv6MulticastScope::Global),
-                _ => None,
+                0x0 => Some(Ipv6MulticastScope::Reserved0),
+                0x1 => Some(Ipv6MulticastScope::InterfaceLocal),
+                0x2 => Some(Ipv6MulticastScope::LinkLocal),
+                0x3 => Some(Ipv6MulticastScope::RealmLocal),
+                0x4 => Some(Ipv6MulticastScope::AdminLocal),
+                0x5 => Some(Ipv6MulticastScope::SiteLocal),
+                0x6 => Some(Ipv6MulticastScope::Unassigned6),
+                0x7 => Some(Ipv6MulticastScope::Unassigned7),
+                0x8 => Some(Ipv6MulticastScope::OrganizationLocal),
+                0x9 => Some(Ipv6MulticastScope::Unassigned9),
+                0xA => Some(Ipv6MulticastScope::UnassignedA),
+                0xB => Some(Ipv6MulticastScope::UnassignedB),
+                0xC => Some(Ipv6MulticastScope::UnassignedC),
+                0xD => Some(Ipv6MulticastScope::UnassignedD),
+                0xE => Some(Ipv6MulticastScope::Global),
+                0xF => Some(Ipv6MulticastScope::ReservedF),
+                _ => unreachable!(),
             }
         } else {
             None