From 77c83c09653f5c4437717e2e8aa7f10c1ff94fe4 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 20 Apr 2023 16:23:13 +0000 Subject: [PATCH 1/6] Add `impl_tag!` macro to implement `Tag` for tagged pointer easily --- .../rustc_data_structures/src/tagged_ptr.rs | 41 ++++- .../src/tagged_ptr/impl_tag.rs | 170 ++++++++++++++++++ .../src/tagged_ptr/impl_tag/tests.rs | 33 ++++ compiler/rustc_middle/src/ty/mod.rs | 29 +-- 4 files changed, 244 insertions(+), 29 deletions(-) create mode 100644 compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs create mode 100644 compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 8568aac67c08c..a25046986cc59 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -24,6 +24,7 @@ use crate::aligned::Aligned; mod copy; mod drop; +mod impl_tag; pub use copy::CopyTaggedPtr; pub use drop::TaggedPtr; @@ -141,6 +142,40 @@ pub unsafe trait Tag: Copy { unsafe fn from_usize(tag: usize) -> Self; } +/// Returns the number of bits available for use for tags in a pointer to `T` +/// (this is based on `T`'s alignment). +pub const fn bits_for() -> u32 { + crate::aligned::align_of::().as_nonzero().trailing_zeros() +} + +/// Returns the correct [`Tag::BITS`] constant for a set of tag values. +pub const fn bits_for_tags(mut tags: &[usize]) -> u32 { + let mut bits = 0; + + while let &[tag, ref rest @ ..] = tags { + tags = rest; + + let b = bits_for_tag(tag); + if b > bits { + bits = b; + } + } + + bits +} + +/// Returns `(size_of::() * 8) - tag.leading_zeros()` +const fn bits_for_tag(mut tag: usize) -> u32 { + let mut bits = 0; + + while tag > 0 { + bits += 1; + tag >>= 1; + } + + bits +} + unsafe impl Pointer for Box { const BITS: u32 = bits_for::(); @@ -221,12 +256,6 @@ unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a mut T { } } -/// Returns the number of bits available for use for tags in a pointer to `T` -/// (this is based on `T`'s alignment). -pub const fn bits_for() -> u32 { - crate::aligned::align_of::().as_nonzero().trailing_zeros() -} - /// A tag type used in [`CopyTaggedPtr`] and [`TaggedPtr`] tests. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg(test)] diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs new file mode 100644 index 0000000000000..deaaee4cc1728 --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs @@ -0,0 +1,170 @@ +/// Implements [`Tag`] for a given type. +/// +/// You can use `impl_tag` on structs and enums. +/// You need to specify the type and all its possible values, +/// which can only be paths with optional fields. +/// +/// [`Tag`]: crate::tagged_ptr::Tag +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use rustc_data_structures::{impl_tag, tagged_ptr::Tag}; +/// +/// #[derive(Copy, Clone, PartialEq, Debug)] +/// enum SomeTag { +/// A, +/// B, +/// X { v: bool }, +/// Y(bool, bool), +/// } +/// +/// impl_tag! { +/// // The type for which the `Tag` will be implemented +/// for SomeTag; +/// // You need to specify the `{value_of_the_type} <=> {tag}` relationship +/// SomeTag::A <=> 0, +/// SomeTag::B <=> 1, +/// // For variants with fields, you need to specify the fields: +/// SomeTag::X { v: true } <=> 2, +/// SomeTag::X { v: false } <=> 3, +/// // For tuple variants use named syntax: +/// SomeTag::Y { 0: true, 1: true } <=> 4, +/// SomeTag::Y { 0: false, 1: true } <=> 5, +/// SomeTag::Y { 0: true, 1: false } <=> 6, +/// SomeTag::Y { 0: false, 1: false } <=> 7, +/// } +/// +/// assert_eq!(SomeTag::A.into_usize(), 0); +/// assert_eq!(SomeTag::X { v: false }.into_usize(), 3); +/// assert_eq!(SomeTag::Y(false, true).into_usize(), 5); +/// +/// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B); +/// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true }); +/// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false)); +/// ``` +/// +/// Structs are supported: +/// +/// ``` +/// # use rustc_data_structures::impl_tag; +/// #[derive(Copy, Clone)] +/// struct Flags { a: bool, b: bool } +/// +/// impl_tag! { +/// for Flags; +/// Flags { a: true, b: true } <=> 3, +/// Flags { a: false, b: true } <=> 2, +/// Flags { a: true, b: false } <=> 1, +/// Flags { a: false, b: false } <=> 0, +/// } +/// ``` +/// +// This is supposed to produce a compile error, but does not, +// see for more information. +// +// Using the same pattern twice results in a compile error: +// +// ```compile_fail +// # use rustc_data_structures::impl_tag; +// #[derive(Copy, Clone)] +// struct Unit; +// +// impl_tag! { +// for Unit; +// Unit <=> 0, +// Unit <=> 1, +// } +// ``` +// +// Using the same tag twice results in a compile error: +// +// ```compile_fail +// # use rustc_data_structures::impl_tag; +// #[derive(Copy, Clone)] +// enum E { A, B }; +// +// impl_tag! { +// for E; +// E::A <=> 0, +// E::B <=> 0, +// } +// ``` +// +/// Not specifying all values results in a compile error: +/// +/// ```compile_fail,E0004 +/// # use rustc_data_structures::impl_tag; +/// #[derive(Copy, Clone)] +/// enum E { +/// A, +/// B, +/// } +/// +/// impl_tag! { +/// for E; +/// E::A <=> 0, +/// } +/// ``` +#[macro_export] +macro_rules! impl_tag { + ( + for $Self:ty; + $( + $($path:ident)::* $( { $( $fields:tt )* })? <=> $tag:literal, + )* + ) => { + // Safety: + // `into_usize` only returns one of `$tag`s, + // `bits_for_tags` is called on all `$tag`s, + // thus `BITS` constant is correct. + unsafe impl $crate::tagged_ptr::Tag for $Self { + const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[ + $( $tag, )* + ]); + + fn into_usize(self) -> usize { + // This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc) + // (or at least it should, see ) + #[forbid(unreachable_patterns)] + match self { + // `match` is doing heavy lifting here, by requiring exhaustiveness + $( + $($path)::* $( { $( $fields )* } )? => $tag, + )* + } + } + + unsafe fn from_usize(tag: usize) -> Self { + // Similarly to the above, this forbids repeating tags + // (or at least it should, see ) + #[forbid(unreachable_patterns)] + match tag { + $( + $tag => $($path)::* $( { $( $fields )* } )?, + )* + + // Safety: + // `into_usize` only returns one of `$tag`s, + // all `$tag`s are filtered up above, + // thus if this is reached, the safety contract of this + // function was already breached. + _ => unsafe { + debug_assert!( + false, + "invalid tag: {tag}\ + (this is a bug in the caller of `from_usize`)" + ); + std::hint::unreachable_unchecked() + }, + } + } + + } + }; +} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs new file mode 100644 index 0000000000000..26d1cf3b45422 --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs @@ -0,0 +1,33 @@ +#[test] +fn bits_constant() { + use crate::tagged_ptr::Tag; + + #[derive(Copy, Clone)] + struct Unit; + impl_tag! { for Unit; Unit <=> 0, } + assert_eq!(Unit::BITS, 0); + + #[derive(Copy, Clone)] + struct Unit1; + impl_tag! { for Unit1; Unit1 <=> 1, } + assert_eq!(Unit1::BITS, 1); + + #[derive(Copy, Clone)] + struct Unit2; + impl_tag! { for Unit2; Unit2 <=> 0b10, } + assert_eq!(Unit2::BITS, 2); + + #[derive(Copy, Clone)] + struct Unit3; + impl_tag! { for Unit3; Unit3 <=> 0b100, } + assert_eq!(Unit3::BITS, 3); + + #[derive(Copy, Clone)] + enum Enum { + A, + B, + C, + } + impl_tag! { for Enum; Enum::A <=> 0b1, Enum::B <=> 0b1000, Enum::C <=> 0b10, } + assert_eq!(Enum::BITS, 4); +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1061c32079305..f520faa6a6154 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1626,29 +1626,12 @@ struct ParamTag { constness: hir::Constness, } -unsafe impl rustc_data_structures::tagged_ptr::Tag for ParamTag { - const BITS: u32 = 2; - - #[inline] - fn into_usize(self) -> usize { - match self { - Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } => 0, - Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } => 1, - Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } => 2, - Self { reveal: traits::Reveal::All, constness: hir::Constness::Const } => 3, - } - } - - #[inline] - unsafe fn from_usize(ptr: usize) -> Self { - match ptr { - 0 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst }, - 1 => Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst }, - 2 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const }, - 3 => Self { reveal: traits::Reveal::All, constness: hir::Constness::Const }, - _ => std::hint::unreachable_unchecked(), - } - } +impl_tag! { + for ParamTag; + ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } <=> 0, + ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } <=> 1, + ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } <=> 2, + ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::Const } <=> 3, } impl<'tcx> fmt::Debug for ParamEnv<'tcx> { From 96905d568a6f1db806d43047a6392c5e2766009e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 20 Apr 2023 18:49:40 +0000 Subject: [PATCH 2/6] Use `impl Tag for $T` syntax for `impl_tag!` --- .../rustc_data_structures/src/tagged_ptr/impl_tag.rs | 12 ++++++------ .../src/tagged_ptr/impl_tag/tests.rs | 10 +++++----- compiler/rustc_middle/src/ty/mod.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs index deaaee4cc1728..c996895936866 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs @@ -23,7 +23,7 @@ /// /// impl_tag! { /// // The type for which the `Tag` will be implemented -/// for SomeTag; +/// impl Tag for SomeTag; /// // You need to specify the `{value_of_the_type} <=> {tag}` relationship /// SomeTag::A <=> 0, /// SomeTag::B <=> 1, @@ -54,7 +54,7 @@ /// struct Flags { a: bool, b: bool } /// /// impl_tag! { -/// for Flags; +/// impl Tag for Flags; /// Flags { a: true, b: true } <=> 3, /// Flags { a: false, b: true } <=> 2, /// Flags { a: true, b: false } <=> 1, @@ -73,7 +73,7 @@ // struct Unit; // // impl_tag! { -// for Unit; +// impl Tag for Unit; // Unit <=> 0, // Unit <=> 1, // } @@ -87,7 +87,7 @@ // enum E { A, B }; // // impl_tag! { -// for E; +// impl Tag for E; // E::A <=> 0, // E::B <=> 0, // } @@ -104,14 +104,14 @@ /// } /// /// impl_tag! { -/// for E; +/// impl Tag for E; /// E::A <=> 0, /// } /// ``` #[macro_export] macro_rules! impl_tag { ( - for $Self:ty; + impl Tag for $Self:ty; $( $($path:ident)::* $( { $( $fields:tt )* })? <=> $tag:literal, )* diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs index 26d1cf3b45422..cd19b30ff5340 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs @@ -4,22 +4,22 @@ fn bits_constant() { #[derive(Copy, Clone)] struct Unit; - impl_tag! { for Unit; Unit <=> 0, } + impl_tag! { impl Tag for Unit; Unit <=> 0, } assert_eq!(Unit::BITS, 0); #[derive(Copy, Clone)] struct Unit1; - impl_tag! { for Unit1; Unit1 <=> 1, } + impl_tag! { impl Tag for Unit1; Unit1 <=> 1, } assert_eq!(Unit1::BITS, 1); #[derive(Copy, Clone)] struct Unit2; - impl_tag! { for Unit2; Unit2 <=> 0b10, } + impl_tag! { impl Tag for Unit2; Unit2 <=> 0b10, } assert_eq!(Unit2::BITS, 2); #[derive(Copy, Clone)] struct Unit3; - impl_tag! { for Unit3; Unit3 <=> 0b100, } + impl_tag! { impl Tag for Unit3; Unit3 <=> 0b100, } assert_eq!(Unit3::BITS, 3); #[derive(Copy, Clone)] @@ -28,6 +28,6 @@ fn bits_constant() { B, C, } - impl_tag! { for Enum; Enum::A <=> 0b1, Enum::B <=> 0b1000, Enum::C <=> 0b10, } + impl_tag! { impl Tag for Enum; Enum::A <=> 0b1, Enum::B <=> 0b1000, Enum::C <=> 0b10, } assert_eq!(Enum::BITS, 4); } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f520faa6a6154..d13c71b78e466 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1627,7 +1627,7 @@ struct ParamTag { } impl_tag! { - for ParamTag; + impl Tag for ParamTag; ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } <=> 0, ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } <=> 1, ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } <=> 2, From 7cfecf26f77f0d2c3d83be946d4da6ab997c939e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 20 Apr 2023 19:07:57 +0000 Subject: [PATCH 3/6] Remove confusing comment --- .../src/tagged_ptr/impl_tag.rs | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs index c996895936866..d263bed9581ad 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs @@ -62,37 +62,6 @@ /// } /// ``` /// -// This is supposed to produce a compile error, but does not, -// see for more information. -// -// Using the same pattern twice results in a compile error: -// -// ```compile_fail -// # use rustc_data_structures::impl_tag; -// #[derive(Copy, Clone)] -// struct Unit; -// -// impl_tag! { -// impl Tag for Unit; -// Unit <=> 0, -// Unit <=> 1, -// } -// ``` -// -// Using the same tag twice results in a compile error: -// -// ```compile_fail -// # use rustc_data_structures::impl_tag; -// #[derive(Copy, Clone)] -// enum E { A, B }; -// -// impl_tag! { -// impl Tag for E; -// E::A <=> 0, -// E::B <=> 0, -// } -// ``` -// /// Not specifying all values results in a compile error: /// /// ```compile_fail,E0004 From ad8c7b6705d94bdf9293fbfe0c4d386d5f7e81a2 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 20 Apr 2023 19:24:04 +0000 Subject: [PATCH 4/6] Simplify `bits_for_tags` impl --- compiler/rustc_data_structures/src/tagged_ptr.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index a25046986cc59..2914eece6796b 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -155,7 +155,9 @@ pub const fn bits_for_tags(mut tags: &[usize]) -> u32 { while let &[tag, ref rest @ ..] = tags { tags = rest; - let b = bits_for_tag(tag); + // bits required to represent `tag`, + // position of the most significant 1 + let b = usize::BITS - tag.leading_zeros(); if b > bits { bits = b; } @@ -164,18 +166,6 @@ pub const fn bits_for_tags(mut tags: &[usize]) -> u32 { bits } -/// Returns `(size_of::() * 8) - tag.leading_zeros()` -const fn bits_for_tag(mut tag: usize) -> u32 { - let mut bits = 0; - - while tag > 0 { - bits += 1; - tag >>= 1; - } - - bits -} - unsafe impl Pointer for Box { const BITS: u32 = bits_for::(); From 2b8d27b402b621d20c7c29c500852c727d4cc8cd Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 24 Apr 2023 16:48:37 +0000 Subject: [PATCH 5/6] Switch `impl_tag!` from explicit tags to `${index()}` --- compiler/rustc_data_structures/src/lib.rs | 1 + .../src/tagged_ptr/impl_tag.rs | 59 ++++++++++--------- .../src/tagged_ptr/impl_tag/tests.rs | 39 ++++++------ compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 8 +-- 5 files changed, 57 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 426d2c4034bd8..004017ec5f31a 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -31,6 +31,7 @@ #![feature(unwrap_infallible)] #![feature(strict_provenance)] #![feature(ptr_alignment_type)] +#![feature(macro_metavar_expr)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs index d263bed9581ad..0b0c152cc4db4 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs @@ -11,6 +11,7 @@ /// Basic usage: /// /// ``` +/// #![feature(macro_metavar_expr)] /// use rustc_data_structures::{impl_tag, tagged_ptr::Tag}; /// /// #[derive(Copy, Clone, PartialEq, Debug)] @@ -24,19 +25,20 @@ /// impl_tag! { /// // The type for which the `Tag` will be implemented /// impl Tag for SomeTag; -/// // You need to specify the `{value_of_the_type} <=> {tag}` relationship -/// SomeTag::A <=> 0, -/// SomeTag::B <=> 1, +/// // You need to specify all possible tag values: +/// SomeTag::A, // 0 +/// SomeTag::B, // 1 /// // For variants with fields, you need to specify the fields: -/// SomeTag::X { v: true } <=> 2, -/// SomeTag::X { v: false } <=> 3, +/// SomeTag::X { v: true }, // 2 +/// SomeTag::X { v: false }, // 3 /// // For tuple variants use named syntax: -/// SomeTag::Y { 0: true, 1: true } <=> 4, -/// SomeTag::Y { 0: false, 1: true } <=> 5, -/// SomeTag::Y { 0: true, 1: false } <=> 6, -/// SomeTag::Y { 0: false, 1: false } <=> 7, +/// SomeTag::Y { 0: true, 1: true }, // 4 +/// SomeTag::Y { 0: false, 1: true }, // 5 +/// SomeTag::Y { 0: true, 1: false }, // 6 +/// SomeTag::Y { 0: false, 1: false }, // 7 /// } /// +/// // Tag values are assigned in order: /// assert_eq!(SomeTag::A.into_usize(), 0); /// assert_eq!(SomeTag::X { v: false }.into_usize(), 3); /// assert_eq!(SomeTag::Y(false, true).into_usize(), 5); @@ -49,22 +51,24 @@ /// Structs are supported: /// /// ``` +/// #![feature(macro_metavar_expr)] /// # use rustc_data_structures::impl_tag; /// #[derive(Copy, Clone)] /// struct Flags { a: bool, b: bool } /// /// impl_tag! { /// impl Tag for Flags; -/// Flags { a: true, b: true } <=> 3, -/// Flags { a: false, b: true } <=> 2, -/// Flags { a: true, b: false } <=> 1, -/// Flags { a: false, b: false } <=> 0, +/// Flags { a: true, b: true }, +/// Flags { a: false, b: true }, +/// Flags { a: true, b: false }, +/// Flags { a: false, b: false }, /// } /// ``` /// /// Not specifying all values results in a compile error: /// /// ```compile_fail,E0004 +/// #![feature(macro_metavar_expr)] /// # use rustc_data_structures::impl_tag; /// #[derive(Copy, Clone)] /// enum E { @@ -74,7 +78,7 @@ /// /// impl_tag! { /// impl Tag for E; -/// E::A <=> 0, +/// E::A, /// } /// ``` #[macro_export] @@ -82,16 +86,18 @@ macro_rules! impl_tag { ( impl Tag for $Self:ty; $( - $($path:ident)::* $( { $( $fields:tt )* })? <=> $tag:literal, + $($path:ident)::* $( { $( $fields:tt )* })?, )* ) => { // Safety: - // `into_usize` only returns one of `$tag`s, - // `bits_for_tags` is called on all `$tag`s, - // thus `BITS` constant is correct. + // `bits_for_tags` is called on the same `${index()}`-es as + // `into_usize` returns, thus `BITS` constant is correct. unsafe impl $crate::tagged_ptr::Tag for $Self { const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[ - $( $tag, )* + $( + ${index()}, + $( ${ignore(path)} )* + )* ]); fn into_usize(self) -> usize { @@ -101,25 +107,22 @@ macro_rules! impl_tag { match self { // `match` is doing heavy lifting here, by requiring exhaustiveness $( - $($path)::* $( { $( $fields )* } )? => $tag, + $($path)::* $( { $( $fields )* } )? => ${index()}, )* } } unsafe fn from_usize(tag: usize) -> Self { - // Similarly to the above, this forbids repeating tags - // (or at least it should, see ) - #[forbid(unreachable_patterns)] match tag { $( - $tag => $($path)::* $( { $( $fields )* } )?, + ${index()} => $($path)::* $( { $( $fields )* } )?, )* // Safety: - // `into_usize` only returns one of `$tag`s, - // all `$tag`s are filtered up above, - // thus if this is reached, the safety contract of this - // function was already breached. + // `into_usize` only returns `${index()}` of the same + // repetition as we are filtering above, thus if this is + // reached, the safety contract of this function was + // already breached. _ => unsafe { debug_assert!( false, diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs index cd19b30ff5340..62c926153e1e9 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs @@ -4,30 +4,31 @@ fn bits_constant() { #[derive(Copy, Clone)] struct Unit; - impl_tag! { impl Tag for Unit; Unit <=> 0, } + impl_tag! { impl Tag for Unit; Unit, } assert_eq!(Unit::BITS, 0); #[derive(Copy, Clone)] - struct Unit1; - impl_tag! { impl Tag for Unit1; Unit1 <=> 1, } - assert_eq!(Unit1::BITS, 1); - - #[derive(Copy, Clone)] - struct Unit2; - impl_tag! { impl Tag for Unit2; Unit2 <=> 0b10, } - assert_eq!(Unit2::BITS, 2); - - #[derive(Copy, Clone)] - struct Unit3; - impl_tag! { impl Tag for Unit3; Unit3 <=> 0b100, } - assert_eq!(Unit3::BITS, 3); - - #[derive(Copy, Clone)] - enum Enum { + enum Enum3 { A, B, C, } - impl_tag! { impl Tag for Enum; Enum::A <=> 0b1, Enum::B <=> 0b1000, Enum::C <=> 0b10, } - assert_eq!(Enum::BITS, 4); + impl_tag! { impl Tag for Enum3; Enum3::A, Enum3::B, Enum3::C, } + assert_eq!(Enum3::BITS, 2); + + #[derive(Copy, Clone)] + struct Eight(bool, bool, bool); + impl_tag! { + impl Tag for Eight; + Eight { 0: true, 1: true, 2: true }, + Eight { 0: true, 1: true, 2: false }, + Eight { 0: true, 1: false, 2: true }, + Eight { 0: true, 1: false, 2: false }, + Eight { 0: false, 1: true, 2: true }, + Eight { 0: false, 1: true, 2: false }, + Eight { 0: false, 1: false, 2: true }, + Eight { 0: false, 1: false, 2: false }, + } + + assert_eq!(Eight::BITS, 3); } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 76a8367b2c460..e9172e767e036 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -60,6 +60,7 @@ #![feature(const_option)] #![feature(trait_alias)] #![feature(ptr_alignment_type)] +#![feature(macro_metavar_expr)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d13c71b78e466..88de43478b886 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1628,10 +1628,10 @@ struct ParamTag { impl_tag! { impl Tag for ParamTag; - ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } <=> 0, - ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } <=> 1, - ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } <=> 2, - ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::Const } <=> 3, + ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst }, + ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst }, + ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const }, + ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::Const }, } impl<'tcx> fmt::Debug for ParamEnv<'tcx> { From bec7ce482458429471c1a4c1b50bcc3728297e5e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 25 Apr 2023 17:37:12 +0000 Subject: [PATCH 6/6] Add `#[inline]` in `impl_tag` --- compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs index 0b0c152cc4db4..cb7f7d318dc41 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs @@ -100,6 +100,7 @@ macro_rules! impl_tag { )* ]); + #[inline] fn into_usize(self) -> usize { // This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc) // (or at least it should, see ) @@ -112,6 +113,7 @@ macro_rules! impl_tag { } } + #[inline] unsafe fn from_usize(tag: usize) -> Self { match tag { $(