about summary refs log tree commit diff
path: root/compiler/rustc_hir/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir/src')
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs473
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs86
-rw-r--r--compiler/rustc_hir/src/attrs/mod.rs54
-rw-r--r--compiler/rustc_hir/src/attrs/pretty_printing.rs149
-rw-r--r--compiler/rustc_hir/src/def.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs21
-rw-r--r--compiler/rustc_hir/src/lang_items.rs3
-rw-r--r--compiler/rustc_hir/src/lib.rs5
-rw-r--r--compiler/rustc_hir/src/lints.rs26
-rw-r--r--compiler/rustc_hir/src/stability.rs207
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs6
-rw-r--r--compiler/rustc_hir/src/version.rs47
12 files changed, 1065 insertions, 16 deletions
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
new file mode 100644
index 00000000000..80f5e6177f7
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -0,0 +1,473 @@
+pub use ReprAttr::*;
+use rustc_abi::Align;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrStyle, ast};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
+use rustc_span::def_id::DefId;
+use rustc_span::hygiene::Transparency;
+use rustc_span::{Ident, Span, Symbol};
+use thin_vec::ThinVec;
+
+use crate::attrs::pretty_printing::PrintAttribute;
+use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
+
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
+pub enum InlineAttr {
+    None,
+    Hint,
+    Always,
+    Never,
+    /// `#[rustc_force_inline]` forces inlining to happen in the MIR inliner - it reports an error
+    /// if the inlining cannot happen. It is limited to only free functions so that the calls
+    /// can always be resolved.
+    Force {
+        attr_span: Span,
+        reason: Option<Symbol>,
+    },
+}
+
+impl InlineAttr {
+    pub fn always(&self) -> bool {
+        match self {
+            InlineAttr::Always | InlineAttr::Force { .. } => true,
+            InlineAttr::None | InlineAttr::Hint | InlineAttr::Never => false,
+        }
+    }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
+pub enum InstructionSetAttr {
+    ArmA32,
+    ArmT32,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+pub enum OptimizeAttr {
+    /// No `#[optimize(..)]` attribute
+    #[default]
+    Default,
+    /// `#[optimize(none)]`
+    DoNotOptimize,
+    /// `#[optimize(speed)]`
+    Speed,
+    /// `#[optimize(size)]`
+    Size,
+}
+
+impl OptimizeAttr {
+    pub fn do_not_optimize(&self) -> bool {
+        matches!(self, Self::DoNotOptimize)
+    }
+}
+
+#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
+pub enum ReprAttr {
+    ReprInt(IntType),
+    ReprRust,
+    ReprC,
+    ReprPacked(Align),
+    ReprSimd,
+    ReprTransparent,
+    ReprAlign(Align),
+}
+
+pub enum TransparencyError {
+    UnknownTransparency(Symbol, Span),
+    MultipleTransparencyAttrs(Span, Span),
+}
+
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
+#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
+pub enum IntType {
+    SignedInt(ast::IntTy),
+    UnsignedInt(ast::UintTy),
+}
+
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
+pub struct Deprecation {
+    pub since: DeprecatedSince,
+    /// The note to issue a reason.
+    pub note: Option<Symbol>,
+    /// A text snippet used to completely replace any use of the deprecated item in an expression.
+    ///
+    /// This is currently unstable.
+    pub suggestion: Option<Symbol>,
+}
+
+/// Release in which an API is deprecated.
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
+pub enum DeprecatedSince {
+    RustcVersion(RustcVersion),
+    /// Deprecated in the future ("to be determined").
+    Future,
+    /// `feature(staged_api)` is off. Deprecation versions outside the standard
+    /// library are allowed to be arbitrary strings, for better or worse.
+    NonStandard(Symbol),
+    /// Deprecation version is unspecified but optional.
+    Unspecified,
+    /// Failed to parse a deprecation version, or the deprecation version is
+    /// unspecified and required. An error has already been emitted.
+    Err,
+}
+
+/// Successfully-parsed value of a `#[coverage(..)]` attribute.
+#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum CoverageAttrKind {
+    On,
+    Off,
+}
+
+impl Deprecation {
+    /// Whether an item marked with #[deprecated(since = "X")] is currently
+    /// deprecated (i.e., whether X is not greater than the current rustc
+    /// version).
+    pub fn is_in_effect(&self) -> bool {
+        match self.since {
+            DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
+            DeprecatedSince::Future => false,
+            // The `since` field doesn't have semantic purpose without `#![staged_api]`.
+            DeprecatedSince::NonStandard(_) => true,
+            // Assume deprecation is in effect if "since" field is absent or invalid.
+            DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
+        }
+    }
+
+    pub fn is_since_rustc_version(&self) -> bool {
+        matches!(self.since, DeprecatedSince::RustcVersion(_))
+    }
+}
+
+/// There are three valid forms of the attribute:
+/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable.
+/// `#[used(compiler)]`
+/// `#[used(linker)]`
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum UsedBy {
+    Compiler,
+    Linker,
+}
+
+#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum MacroUseArgs {
+    UseAll,
+    UseSpecific(ThinVec<Ident>),
+}
+
+impl Default for MacroUseArgs {
+    fn default() -> Self {
+        Self::UseSpecific(ThinVec::new())
+    }
+}
+
+#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
+pub struct StrippedCfgItem<ModId = DefId> {
+    pub parent_module: ModId,
+    pub ident: Ident,
+    pub cfg: (CfgEntry, Span),
+}
+
+impl<ModId> StrippedCfgItem<ModId> {
+    pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
+        StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
+    }
+}
+
+#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum CfgEntry {
+    All(ThinVec<CfgEntry>, Span),
+    Any(ThinVec<CfgEntry>, Span),
+    Not(Box<CfgEntry>, Span),
+    Bool(bool, Span),
+    NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span },
+    Version(Option<RustcVersion>, Span),
+}
+
+/// Represents parsed *built-in* inert attributes.
+///
+/// ## Overview
+/// These attributes are markers that guide the compilation process and are never expanded into other code.
+/// They persist throughout the compilation phases, from AST to HIR and beyond.
+///
+/// ## Attribute Processing
+/// While attributes are initially parsed by [`rustc_parse`] into [`ast::Attribute`], they still contain raw token streams
+/// because different attributes have different internal structures. This enum represents the final,
+/// fully parsed form of these attributes, where each variant contains all the information and
+/// structure relevant for the specific attribute.
+///
+/// Some attributes can be applied multiple times to the same item, and they are "collapsed" into a single
+/// semantic attribute. For example:
+/// ```rust
+/// #[repr(C)]
+/// #[repr(packed)]
+/// struct S { }
+/// ```
+/// This is equivalent to `#[repr(C, packed)]` and results in a single [`AttributeKind::Repr`] containing
+/// both `C` and `packed` annotations. This collapsing happens during parsing and is reflected in the
+/// data structures defined in this enum.
+///
+/// ## Usage
+/// These parsed attributes are used throughout the compiler to:
+/// - Control code generation (e.g., `#[repr]`)
+/// - Mark API stability (`#[stable]`, `#[unstable]`)
+/// - Provide documentation (`#[doc]`)
+/// - Guide compiler behavior (e.g., `#[allow_internal_unstable]`)
+///
+/// ## Note on Attribute Organization
+/// Some attributes like `InlineAttr`, `OptimizeAttr`, and `InstructionSetAttr` are defined separately
+/// from this enum because they are used in specific compiler phases (like code generation) and don't
+/// need to persist throughout the entire compilation process. They are typically processed and
+/// converted into their final form earlier in the compilation pipeline.
+///
+/// For example:
+/// - `InlineAttr` is used during code generation to control function inlining
+/// - `OptimizeAttr` is used to control optimization levels
+/// - `InstructionSetAttr` is used for target-specific code generation
+///
+/// These attributes are handled by their respective compiler passes in the [`rustc_codegen_ssa`] crate
+/// and don't need to be preserved in the same way as the attributes in this enum.
+///
+/// For more details on attribute parsing, see the [`rustc_attr_parsing`] crate.
+///
+/// [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
+/// [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
+/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
+#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
+pub enum AttributeKind {
+    // tidy-alphabetical-start
+    /// Represents `#[align(N)]`.
+    // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
+    Align { align: Align, span: Span },
+
+    /// Represents `#[rustc_allow_const_fn_unstable]`.
+    AllowConstFnUnstable(ThinVec<Symbol>, Span),
+
+    /// Represents `#[rustc_allow_incoherent_impl]`.
+    AllowIncoherentImpl(Span),
+
+    /// Represents `#[allow_internal_unstable]`.
+    AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
+
+    /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
+    AsPtr(Span),
+
+    /// Represents `#[automatically_derived]`
+    AutomaticallyDerived(Span),
+
+    /// Represents `#[rustc_default_body_unstable]`.
+    BodyStability {
+        stability: DefaultBodyStability,
+        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
+        span: Span,
+    },
+
+    /// Represents `#[rustc_coherence_is_core]`.
+    CoherenceIsCore,
+
+    /// Represents `#[rustc_coinductive]`.
+    Coinductive(Span),
+
+    /// Represents `#[cold]`.
+    Cold(Span),
+
+    /// Represents `#[rustc_confusables]`.
+    Confusables {
+        symbols: ThinVec<Symbol>,
+        // FIXME(jdonszelmann): remove when target validation code is moved
+        first_span: Span,
+    },
+
+    /// Represents `#[const_continue]`.
+    ConstContinue(Span),
+
+    /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
+    ConstStability {
+        stability: PartialConstStability,
+        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
+        span: Span,
+    },
+
+    /// Represents `#[rustc_const_stable_indirect]`.
+    ConstStabilityIndirect,
+
+    /// Represents `#[const_trait]`.
+    ConstTrait(Span),
+
+    /// Represents `#[coverage(..)]`.
+    Coverage(Span, CoverageAttrKind),
+
+    ///Represents `#[rustc_deny_explicit_impl]`.
+    DenyExplicitImpl(Span),
+
+    /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
+    Deprecation { deprecation: Deprecation, span: Span },
+
+    /// Represents `#[rustc_do_not_implement_via_object]`.
+    DoNotImplementViaObject(Span),
+
+    /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
+    DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
+
+    /// Represents `#[rustc_dummy]`.
+    Dummy,
+
+    /// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute).
+    ExportName {
+        /// The name to export this item with.
+        /// It may not contain \0 bytes as it will be converted to a null-terminated string.
+        name: Symbol,
+        span: Span,
+    },
+
+    /// Represents `#[export_stable]`.
+    ExportStable,
+
+    /// Represents `#[ffi_const]`.
+    FfiConst(Span),
+
+    /// Represents `#[ffi_pure]`.
+    FfiPure(Span),
+
+    /// Represents `#[fundamental]`.
+    Fundamental,
+
+    /// Represents `#[ignore]`
+    Ignore {
+        span: Span,
+        /// ignore can optionally have a reason: `#[ignore = "reason this is ignored"]`
+        reason: Option<Symbol>,
+    },
+
+    /// Represents `#[inline]` and `#[rustc_force_inline]`.
+    Inline(InlineAttr, Span),
+
+    /// Represents `#[link_name]`.
+    LinkName { name: Symbol, span: Span },
+
+    /// Represents `#[link_ordinal]`.
+    LinkOrdinal { ordinal: u16, span: Span },
+
+    /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
+    LinkSection { name: Symbol, span: Span },
+
+    /// Represents `#[loop_match]`.
+    LoopMatch(Span),
+
+    /// Represents `#[macro_escape]`.
+    MacroEscape(Span),
+
+    /// Represents `#[rustc_macro_transparency]`.
+    MacroTransparency(Transparency),
+
+    /// Represents `#[macro_use]`.
+    MacroUse { span: Span, arguments: MacroUseArgs },
+
+    /// Represents `#[marker]`.
+    Marker(Span),
+
+    /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
+    MayDangle(Span),
+
+    /// Represents `#[must_use]`.
+    MustUse {
+        span: Span,
+        /// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
+        reason: Option<Symbol>,
+    },
+
+    /// Represents `#[naked]`
+    Naked(Span),
+
+    /// Represents `#[no_implicit_prelude]`
+    NoImplicitPrelude(Span),
+
+    /// Represents `#[no_mangle]`
+    NoMangle(Span),
+
+    /// Represents `#[non_exhaustive]`
+    NonExhaustive(Span),
+
+    /// Represents `#[omit_gdb_pretty_printer_section]`
+    OmitGdbPrettyPrinterSection,
+
+    /// Represents `#[optimize(size|speed)]`
+    Optimize(OptimizeAttr, Span),
+
+    /// Represents `#[rustc_paren_sugar]`.
+    ParenSugar(Span),
+
+    /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
+    PassByValue(Span),
+
+    /// Represents `#[path]`
+    Path(Symbol, Span),
+
+    /// Represents `#[pointee]`
+    Pointee(Span),
+
+    /// Represents `#[proc_macro]`
+    ProcMacro(Span),
+
+    /// Represents `#[proc_macro_attribute]`
+    ProcMacroAttribute(Span),
+
+    /// Represents `#[proc_macro_derive]`
+    ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
+
+    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
+    PubTransparent(Span),
+
+    /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
+    Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
+
+    /// Represents `#[rustc_builtin_macro]`.
+    RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
+
+    /// Represents `#[rustc_layout_scalar_valid_range_end]`.
+    RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
+
+    /// Represents `#[rustc_layout_scalar_valid_range_start]`.
+    RustcLayoutScalarValidRangeStart(Box<u128>, Span),
+
+    /// Represents `#[rustc_object_lifetime_default]`.
+    RustcObjectLifetimeDefault,
+
+    /// Represents `#[rustc_skip_during_method_dispatch]`.
+    SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
+
+    /// Represents `#[rustc_specialization_trait]`.
+    SpecializationTrait(Span),
+
+    /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
+    Stability {
+        stability: Stability,
+        /// Span of the attribute.
+        span: Span,
+    },
+
+    /// Represents `#[rustc_std_internal_symbol]`.
+    StdInternalSymbol(Span),
+
+    /// Represents `#[target_feature(enable = "...")]`
+    TargetFeature(ThinVec<(Symbol, Span)>, Span),
+
+    /// Represents `#[track_caller]`
+    TrackCaller(Span),
+
+    /// Represents `#[type_const]`.
+    TypeConst(Span),
+
+    /// Represents `#[rustc_unsafe_specialization_marker]`.
+    UnsafeSpecializationMarker(Span),
+
+    /// Represents `#[unstable_feature_bound]`.
+    UnstableFeatureBound(ThinVec<(Symbol, Span)>),
+
+    /// Represents `#[used]`
+    Used { used_by: UsedBy, span: Span },
+    // tidy-alphabetical-end
+}
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
new file mode 100644
index 00000000000..bbdecb2986e
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -0,0 +1,86 @@
+use crate::attrs::AttributeKind;
+
+#[derive(PartialEq)]
+pub enum EncodeCrossCrate {
+    Yes,
+    No,
+}
+
+impl AttributeKind {
+    pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
+        use AttributeKind::*;
+        use EncodeCrossCrate::*;
+
+        match self {
+            // tidy-alphabetical-start
+            Align { .. } => No,
+            AllowConstFnUnstable(..) => No,
+            AllowIncoherentImpl(..) => No,
+            AllowInternalUnstable(..) => Yes,
+            AsPtr(..) => Yes,
+            AutomaticallyDerived(..) => Yes,
+            BodyStability { .. } => No,
+            CoherenceIsCore => No,
+            Coinductive(..) => No,
+            Cold(..) => No,
+            Confusables { .. } => Yes,
+            ConstContinue(..) => No,
+            ConstStability { .. } => Yes,
+            ConstStabilityIndirect => No,
+            ConstTrait(..) => No,
+            Coverage(..) => No,
+            DenyExplicitImpl(..) => No,
+            Deprecation { .. } => Yes,
+            DoNotImplementViaObject(..) => No,
+            DocComment { .. } => Yes,
+            Dummy => No,
+            ExportName { .. } => Yes,
+            ExportStable => No,
+            FfiConst(..) => No,
+            FfiPure(..) => No,
+            Fundamental { .. } => Yes,
+            Ignore { .. } => No,
+            Inline(..) => No,
+            LinkName { .. } => Yes, // Needed for rustdoc
+            LinkOrdinal { .. } => No,
+            LinkSection { .. } => Yes, // Needed for rustdoc
+            LoopMatch(..) => No,
+            MacroEscape(..) => No,
+            MacroTransparency(..) => Yes,
+            MacroUse { .. } => No,
+            Marker(..) => No,
+            MayDangle(..) => No,
+            MustUse { .. } => Yes,
+            Naked(..) => No,
+            NoImplicitPrelude(..) => No,
+            NoMangle(..) => Yes,      // Needed for rustdoc
+            NonExhaustive(..) => Yes, // Needed for rustdoc
+            OmitGdbPrettyPrinterSection => No,
+            Optimize(..) => No,
+            ParenSugar(..) => No,
+            PassByValue(..) => Yes,
+            Path(..) => No,
+            Pointee(..) => No,
+            ProcMacro(..) => No,
+            ProcMacroAttribute(..) => No,
+            ProcMacroDerive { .. } => No,
+            PubTransparent(..) => Yes,
+            Repr { .. } => No,
+            RustcBuiltinMacro { .. } => Yes,
+            RustcLayoutScalarValidRangeEnd(..) => Yes,
+            RustcLayoutScalarValidRangeStart(..) => Yes,
+            RustcObjectLifetimeDefault => No,
+            SkipDuringMethodDispatch { .. } => No,
+            SpecializationTrait(..) => No,
+            Stability { .. } => Yes,
+            StdInternalSymbol(..) => No,
+            TargetFeature(..) => No,
+            TrackCaller(..) => Yes,
+            TypeConst(..) => Yes,
+            UnsafeSpecializationMarker(..) => No,
+            UnstableFeatureBound(..) => No,
+            Used { .. } => No,
+            // tidy-alphabetical-end
+        }
+    }
+}
diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs
new file mode 100644
index 00000000000..482e6c90739
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/mod.rs
@@ -0,0 +1,54 @@
+//! Data structures for representing parsed attributes in the Rust compiler.
+//! Formerly `rustc_attr_data_structures`.
+//!
+//! For detailed documentation about attribute processing,
+//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html).
+
+pub use data_structures::*;
+pub use encode_cross_crate::EncodeCrossCrate;
+pub use pretty_printing::PrintAttribute;
+
+mod data_structures;
+mod encode_cross_crate;
+mod pretty_printing;
+
+/// Finds attributes in sequences of attributes by pattern matching.
+///
+/// A little like `matches` but for attributes.
+///
+/// ```rust,ignore (illustrative)
+/// // finds the repr attribute
+/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
+///
+/// }
+///
+/// // checks if one has matched
+/// if find_attr!(attrs, AttributeKind::Repr(_)) {
+///
+/// }
+/// ```
+///
+/// Often this requires you to first end up with a list of attributes.
+/// A common way to get those is through `tcx.get_all_attrs(did)`
+#[macro_export]
+macro_rules! find_attr {
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
+        $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
+    }};
+
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
+        'done: {
+            for i in $attributes_list {
+                let i: &rustc_hir::Attribute = i;
+                match i {
+                    rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
+                        break 'done Some($e);
+                    }
+                    _ => {}
+                }
+            }
+
+            None
+        }
+    }};
+}
diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs
new file mode 100644
index 00000000000..e44b29141da
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs
@@ -0,0 +1,149 @@
+use std::num::NonZero;
+
+use rustc_abi::Align;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrStyle, IntTy, UintTy};
+use rustc_ast_pretty::pp::Printer;
+use rustc_span::hygiene::Transparency;
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
+use thin_vec::ThinVec;
+
+/// This trait is used to print attributes in `rustc_hir_pretty`.
+///
+/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
+/// The output will look a lot like a `Debug` implementation, but fields of several types
+/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
+/// representation much.
+pub trait PrintAttribute {
+    /// Whether or not this will render as something meaningful, or if it's skipped
+    /// (which will force the containing struct to also skip printing a comma
+    /// and the field name).
+    fn should_render(&self) -> bool;
+
+    fn print_attribute(&self, p: &mut Printer);
+}
+
+impl PrintAttribute for u128 {
+    fn should_render(&self) -> bool {
+        true
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        p.word(self.to_string())
+    }
+}
+
+impl<T: PrintAttribute> PrintAttribute for &T {
+    fn should_render(&self) -> bool {
+        T::should_render(self)
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        T::print_attribute(self, p)
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for Option<T> {
+    fn should_render(&self) -> bool {
+        self.as_ref().is_some_and(|x| x.should_render())
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        if let Some(i) = self {
+            T::print_attribute(i, p)
+        }
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
+    fn should_render(&self) -> bool {
+        self.is_empty() || self[0].should_render()
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        let mut last_printed = false;
+        p.word("[");
+        for i in self {
+            if last_printed {
+                p.word_space(",");
+            }
+            i.print_attribute(p);
+            last_printed = i.should_render();
+        }
+        p.word("]");
+    }
+}
+macro_rules! print_skip {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { false }
+            fn print_attribute(&self, _: &mut Printer) { }
+        })*
+    };
+}
+
+macro_rules! print_disp {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{}", self));
+            }
+        }
+    )*};
+}
+macro_rules! print_debug {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{:?}", self));
+            }
+        }
+    )*};
+}
+
+macro_rules! print_tup {
+    (num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
+    () => {};
+    ($t: ident $($ts: ident)*) => {
+        #[allow(non_snake_case, unused)]
+        impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
+            fn should_render(&self) -> bool {
+                let ($t, $($ts),*) = self;
+                print_tup!(num_should_render $t $($ts)*) != 0
+            }
+
+            fn print_attribute(&self, p: &mut Printer) {
+                let ($t, $($ts),*) = self;
+                let parens = print_tup!(num_should_render $t $($ts)*) > 1;
+                if parens {
+                    p.popen();
+                }
+
+                let mut printed_anything = $t.should_render();
+
+                $t.print_attribute(p);
+
+                $(
+                    if $ts.should_render() {
+                        if printed_anything {
+                            p.word_space(",");
+                        }
+                        printed_anything = true;
+                    }
+                    $ts.print_attribute(p);
+                )*
+
+                if parens {
+                    p.pclose();
+                }
+            }
+        }
+
+        print_tup!($($ts)*);
+    };
+}
+
+print_tup!(A B C D E F G H);
+print_skip!(Span, (), ErrorGuaranteed);
+print_disp!(u16, bool, NonZero<u32>);
+print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index ca57c4f3164..3fee9af01b3 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -295,6 +295,10 @@ impl DefKind {
         }
     }
 
+    pub fn is_assoc(self) -> bool {
+        matches!(self, DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy)
+    }
+
     /// This is a "module" in name resolution sense.
     #[inline]
     pub fn is_module_like(self) -> bool {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e7898648c2b..08361718108 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -14,7 +14,6 @@ pub use rustc_ast::{
     BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
     MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
 };
-use rustc_attr_data_structures::AttributeKind;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::tagged_ptr::TaggedRef;
@@ -30,6 +29,7 @@ use thin_vec::ThinVec;
 use tracing::debug;
 
 use crate::LangItem;
+use crate::attrs::AttributeKind;
 use crate::def::{CtorKind, DefKind, PerNS, Res};
 use crate::def_id::{DefId, LocalDefIdMap};
 pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
@@ -148,6 +148,11 @@ impl From<Ident> for LifetimeSyntax {
 /// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing`
 /// — there's no way to "elide" these lifetimes.
 #[derive(Debug, Copy, Clone, HashStable_Generic)]
+// Raise the aligement to at least 4 bytes - this is relied on in other parts of the compiler(for pointer tagging):
+// https://github.com/rust-lang/rust/blob/ce5fdd7d42aba9a2925692e11af2bd39cf37798a/compiler/rustc_data_structures/src/tagged_ptr.rs#L163
+// Removing this `repr(4)` will cause the compiler to not build on platforms like `m68k` Linux, where the aligement of u32 and usize is only 2.
+// Since `repr(align)` may only raise aligement, this has no effect on platforms where the aligement is already sufficient.
+#[repr(align(4))]
 pub struct Lifetime {
     #[stable_hasher(ignore)]
     pub hir_id: HirId,
@@ -1302,6 +1307,7 @@ impl AttributeExt for Attribute {
             // FIXME: should not be needed anymore when all attrs are parsed
             Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
+            Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
             Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
@@ -1362,6 +1368,17 @@ impl AttributeExt for Attribute {
             _ => None,
         }
     }
+
+    fn is_proc_macro_attr(&self) -> bool {
+        matches!(
+            self,
+            Attribute::Parsed(
+                AttributeKind::ProcMacro(..)
+                    | AttributeKind::ProcMacroAttribute(..)
+                    | AttributeKind::ProcMacroDerive { .. }
+            )
+        )
+    }
 }
 
 // FIXME(fn_delegation): use function delegation instead of manually forwarding
@@ -2999,7 +3016,7 @@ impl fmt::Display for LoopIdError {
     }
 }
 
-#[derive(Copy, Clone, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)]
 pub struct Destination {
     /// This is `Some(_)` iff there is an explicit user-specified 'label
     pub label: Option<Label>,
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 6347f1bfa71..75c04b23ed6 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -345,9 +345,6 @@ language_item_table! {
     OwnedBox,                sym::owned_box,           owned_box,                  Target::Struct,         GenericRequirement::Minimum(1);
     GlobalAlloc,             sym::global_alloc_ty,     global_alloc_ty,            Target::Struct,         GenericRequirement::None;
 
-    // Experimental lang item for Miri
-    PtrUnique,               sym::ptr_unique,          ptr_unique,                 Target::Struct,         GenericRequirement::Exact(1);
-
     PhantomData,             sym::phantom_data,        phantom_data,               Target::Struct,         GenericRequirement::Exact(1);
 
     ManuallyDrop,            sym::manually_drop,       manually_drop,              Target::Struct,         GenericRequirement::None;
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index dafd31336fb..f1212d07ff6 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -18,6 +18,7 @@
 extern crate self as rustc_hir;
 
 mod arena;
+pub mod attrs;
 pub mod def;
 pub mod def_path_hash_map;
 pub mod definitions;
@@ -29,8 +30,10 @@ pub mod intravisit;
 pub mod lang_items;
 pub mod lints;
 pub mod pat_util;
+mod stability;
 mod stable_hash_impls;
 mod target;
+mod version;
 pub mod weak_lang_items;
 
 #[cfg(test)]
@@ -40,7 +43,9 @@ mod tests;
 pub use hir::*;
 pub use hir_id::*;
 pub use lang_items::{LangItem, LanguageItems};
+pub use stability::*;
 pub use stable_hash_impls::HashStableContext;
 pub use target::{MethodKind, Target};
+pub use version::*;
 
 arena_types!(rustc_arena::declare_arena);
diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs
index 7be6c32e57e..c55a41eb2b7 100644
--- a/compiler/rustc_hir/src/lints.rs
+++ b/compiler/rustc_hir/src/lints.rs
@@ -1,9 +1,16 @@
-use rustc_attr_data_structures::lints::AttributeLint;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_macros::HashStable_Generic;
+use rustc_span::Span;
 
 use crate::HirId;
 
+#[derive(Debug)]
+pub struct DelayedLints {
+    pub lints: Box<[DelayedLint]>,
+    // Only present when the crate hash is needed.
+    pub opt_hash: Option<Fingerprint>,
+}
+
 /// During ast lowering, no lints can be emitted.
 /// That is because lints attach to nodes either in the AST, or on the built HIR.
 /// When attached to AST nodes, they're emitted just before building HIR,
@@ -15,9 +22,16 @@ pub enum DelayedLint {
     AttributeParsing(AttributeLint<HirId>),
 }
 
-#[derive(Debug)]
-pub struct DelayedLints {
-    pub lints: Box<[DelayedLint]>,
-    // Only present when the crate hash is needed.
-    pub opt_hash: Option<Fingerprint>,
+#[derive(Clone, Debug, HashStable_Generic)]
+pub struct AttributeLint<Id> {
+    pub id: Id,
+    pub span: Span,
+    pub kind: AttributeLintKind,
+}
+
+#[derive(Clone, Debug, HashStable_Generic)]
+pub enum AttributeLintKind {
+    UnusedDuplicate { this: Span, other: Span, warning: bool },
+    IllFormedAttributeInput { suggestions: Vec<String> },
+    EmptyAttribute { first_span: Span },
 }
diff --git a/compiler/rustc_hir/src/stability.rs b/compiler/rustc_hir/src/stability.rs
new file mode 100644
index 00000000000..2b3a2a79316
--- /dev/null
+++ b/compiler/rustc_hir/src/stability.rs
@@ -0,0 +1,207 @@
+use std::num::NonZero;
+
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
+use rustc_span::{ErrorGuaranteed, Symbol, sym};
+
+use crate::RustcVersion;
+use crate::attrs::PrintAttribute;
+
+/// The version placeholder that recently stabilized features contain inside the
+/// `since` field of the `#[stable]` attribute.
+///
+/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
+pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
+// Note that the `concat!` macro above prevents `src/tools/replace-version-placeholder` from
+// replacing the constant with the current version. Hardcoding the tool to skip this file doesn't
+// work as the file can (and at some point will) be moved around.
+//
+// Turning the `concat!` macro into a string literal will make Pietro cry. That'd be sad :(
+
+/// Represents the following attributes:
+///
+/// - `#[stable]`
+/// - `#[unstable]`
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub struct Stability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+}
+
+impl Stability {
+    pub fn is_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+
+    pub fn stable_since(&self) -> Option<StableSince> {
+        self.level.stable_since()
+    }
+}
+
+/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub struct ConstStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+    /// whether the function has a `#[rustc_promotable]` attribute
+    pub promotable: bool,
+    /// This is true iff the `const_stable_indirect` attribute is present.
+    pub const_stable_indirect: bool,
+}
+
+impl ConstStability {
+    pub fn from_partial(
+        PartialConstStability { level, feature, promotable }: PartialConstStability,
+        const_stable_indirect: bool,
+    ) -> Self {
+        Self { const_stable_indirect, level, feature, promotable }
+    }
+
+    /// The stability assigned to unmarked items when -Zforce-unstable-if-unmarked is set.
+    pub fn unmarked(const_stable_indirect: bool, regular_stab: Stability) -> Self {
+        Self {
+            feature: regular_stab.feature,
+            promotable: false,
+            level: regular_stab.level,
+            const_stable_indirect,
+        }
+    }
+
+    pub fn is_const_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_const_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+}
+
+/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked`
+/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub struct PartialConstStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+    /// whether the function has a `#[rustc_promotable]` attribute
+    pub promotable: bool,
+}
+
+impl PartialConstStability {
+    pub fn is_const_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_const_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+}
+
+/// The available stability levels.
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum StabilityLevel {
+    /// `#[unstable]`
+    Unstable {
+        /// Reason for the current stability level.
+        reason: UnstableReason,
+        /// Relevant `rust-lang/rust` issue.
+        issue: Option<NonZero<u32>>,
+        is_soft: bool,
+        /// If part of a feature is stabilized and a new feature is added for the remaining parts,
+        /// then the `implied_by` attribute is used to indicate which now-stable feature previously
+        /// contained an item.
+        ///
+        /// ```pseudo-Rust
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foobar() {}
+        /// ```
+        ///
+        /// ...becomes...
+        ///
+        /// ```pseudo-Rust
+        /// #[stable(feature = "foo", since = "1.XX.X")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
+        /// fn foobar() {}
+        /// ```
+        implied_by: Option<Symbol>,
+        old_name: Option<Symbol>,
+    },
+    /// `#[stable]`
+    Stable {
+        /// Rust release which stabilized this feature.
+        since: StableSince,
+        /// This is `Some` if this item allowed to be referred to on stable via unstable modules;
+        /// the `Symbol` is the deprecation message printed in that case.
+        allowed_through_unstable_modules: Option<Symbol>,
+    },
+}
+
+/// Rust release in which a feature is stabilized.
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum StableSince {
+    /// also stores the original symbol for printing
+    Version(RustcVersion),
+    /// Stabilized in the upcoming version, whatever number that is.
+    Current,
+    /// Failed to parse a stabilization version.
+    Err(ErrorGuaranteed),
+}
+
+impl StabilityLevel {
+    pub fn is_unstable(&self) -> bool {
+        matches!(self, StabilityLevel::Unstable { .. })
+    }
+    pub fn is_stable(&self) -> bool {
+        matches!(self, StabilityLevel::Stable { .. })
+    }
+    pub fn stable_since(&self) -> Option<StableSince> {
+        match *self {
+            StabilityLevel::Stable { since, .. } => Some(since),
+            StabilityLevel::Unstable { .. } => None,
+        }
+    }
+}
+
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum UnstableReason {
+    None,
+    Default,
+    Some(Symbol),
+}
+
+/// Represents the `#[rustc_default_body_unstable]` attribute.
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub struct DefaultBodyStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+}
+
+impl UnstableReason {
+    pub fn from_opt_reason(reason: Option<Symbol>) -> Self {
+        // UnstableReason::Default constructed manually
+        match reason {
+            Some(r) => Self::Some(r),
+            None => Self::None,
+        }
+    }
+
+    pub fn to_opt_reason(&self) -> Option<Symbol> {
+        match self {
+            Self::None => None,
+            Self::Default => Some(sym::unstable_location_reason_default),
+            Self::Some(r) => Some(*r),
+        }
+    }
+}
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index 6acf1524b60..ecc608d437b 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -11,11 +11,7 @@ use crate::lints::DelayedLints;
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
-pub trait HashStableContext:
-    rustc_attr_data_structures::HashStableContext
-    + rustc_ast::HashStableContext
-    + rustc_abi::HashStableContext
-{
+pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {
     fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher);
 }
 
diff --git a/compiler/rustc_hir/src/version.rs b/compiler/rustc_hir/src/version.rs
new file mode 100644
index 00000000000..ab5ab026b4c
--- /dev/null
+++ b/compiler/rustc_hir/src/version.rs
@@ -0,0 +1,47 @@
+use std::fmt::{self, Display};
+use std::sync::OnceLock;
+
+use rustc_macros::{
+    Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
+};
+
+use crate::attrs::PrintAttribute;
+
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub struct RustcVersion {
+    pub major: u16,
+    pub minor: u16,
+    pub patch: u16,
+}
+
+impl RustcVersion {
+    pub const CURRENT: Self = current_rustc_version!();
+    pub fn current_overridable() -> Self {
+        *CURRENT_OVERRIDABLE.get_or_init(|| {
+            if let Ok(override_var) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING")
+                && let Some(override_) = Self::parse_str(&override_var)
+            {
+                override_
+            } else {
+                Self::CURRENT
+            }
+        })
+    }
+    fn parse_str(value: &str) -> Option<Self> {
+        // Ignore any suffixes such as "-dev" or "-nightly".
+        let mut components = value.split('-').next().unwrap().splitn(3, '.');
+        let major = components.next()?.parse().ok()?;
+        let minor = components.next()?.parse().ok()?;
+        let patch = components.next().unwrap_or("0").parse().ok()?;
+        Some(RustcVersion { major, minor, patch })
+    }
+}
+
+static CURRENT_OVERRIDABLE: OnceLock<RustcVersion> = OnceLock::new();
+
+impl Display for RustcVersion {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
+    }
+}