From 32446f2d734b8d7d595dfb4a0105f1a1e91dadbc Mon Sep 17 00:00:00 2001 From: Makai Date: Tue, 9 Jun 2026 00:15:13 +0800 Subject: [PATCH] Add `SpecialDerives` --- Cargo.lock | 1 + .../src/deriving/clone.rs | 4 +- .../src/deriving/cmp/partial_ord.rs | 4 +- compiler/rustc_expand/Cargo.toml | 1 + compiler/rustc_expand/src/base.rs | 17 +++++-- compiler/rustc_resolve/src/lib.rs | 12 ++--- compiler/rustc_resolve/src/macros.rs | 45 +++++++++---------- 7 files changed, 45 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8938c49449711..bdcad1d2b8e51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3983,6 +3983,7 @@ dependencies = [ name = "rustc_expand" version = "0.0.0" dependencies = [ + "bitflags", "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index af3db65bd0a63..f2e29a34c3b54 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -1,6 +1,6 @@ use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData}; use rustc_data_structures::fx::FxHashSet; -use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::base::{Annotatable, ExtCtxt, SpecialDerives}; use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -37,7 +37,7 @@ pub(crate) fn expand_deriving_clone( ItemKind::Struct(_, Generics { params, .. }, _) | ItemKind::Enum(_, Generics { params, .. }, _) => { let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); - let has_derive_copy = cx.resolver.has_derive_copy(container_id); + let has_derive_copy = cx.resolver.has_derives(container_id, SpecialDerives::COPY); if has_derive_copy && !params .iter() diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index d66d14bb881a1..28bf76663b269 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,5 +1,5 @@ use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast}; -use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::base::{Annotatable, ExtCtxt, SpecialDerives}; use rustc_span::{Ident, Span, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -43,7 +43,7 @@ pub(crate) fn expand_deriving_partial_ord( }; let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); - let has_derive_ord = cx.resolver.has_derive_ord(container_id); + let has_derive_ord = cx.resolver.has_derives(container_id, SpecialDerives::ORD); let is_simple_candidate = |params: &ThinVec| -> bool { has_derive_ord && !params.iter().any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 0154d05266fd7..666cc73af9082 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start +bitflags = "2.9.1" rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 9f474fbeb7650..bc74e3156d580 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1115,10 +1115,8 @@ pub trait ResolverExpand { fn check_unused_macros(&mut self); // Resolver interfaces for specific built-in macros. - /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it? - fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool; - /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Ord` inside it? - fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool; + /// Does `#[derive(...)]` attribute with the given `ExpnId` have the given special built-in derives inside it? + fn has_derives(&self, expn_id: LocalExpnId, derives: SpecialDerives) -> bool; /// Resolve paths inside the `#[derive(...)]` attribute with the given `ExpnId`. fn resolve_derives( &mut self, @@ -1389,6 +1387,17 @@ impl<'a> ExtCtxt<'a> { } } +bitflags::bitflags! { + /// Set of special built-in derives that can affect expansion of other derives. + /// E.g., deriving `Copy` affects how `Clone` is derived, + /// and deriving `Ord` affects how `PartialOrd` is derived. + #[derive(PartialEq, Eq, Clone, Copy)] + pub struct SpecialDerives: u8 { + const COPY = 1 << 0; + const ORD = 1 << 1; + } +} + /// Resolves a `path` mentioned inside Rust code, returning an absolute path. /// /// This unifies the logic used for resolving `include_X!`. diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ffb2181bae3a9..50171530bae0a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -48,7 +48,7 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::{UnordItems, UnordMap, UnordSet}; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; -use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{DeriveResolution, SpecialDerives, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::Namespace::{self, *}; @@ -1311,10 +1311,7 @@ impl ExternPreludeEntry<'_> { struct DeriveData { resolutions: Vec, helper_attrs: Vec<(usize, IdentKey, Span)>, - // if this list keeps getting extended, we could use `bitflags`, - // something like what [`rustc_type_ir::flags::TypeFlags`] is doing. - has_derive_copy: bool, - has_derive_ord: bool, + has_derives: SpecialDerives, } pub struct ResolverOutputs<'tcx> { @@ -1450,11 +1447,10 @@ pub struct Resolver<'ra, 'tcx> { multi_segment_macro_resolutions: CmRefCell, Span, MacroKind, ParentScope<'ra>, Option, Namespace)>>, builtin_attrs: Vec<(Ident, ParentScope<'ra>)> = Vec::new(), - /// `derive(Copy)` marks items they are applied to so they are treated specially later. + /// Some built-in derives marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global /// context, so they attach the markers to derive container IDs using this resolver table. - containers_deriving_copy: FxHashSet = default::fx_hash_set(), - containers_deriving_ord: FxHashSet = default::fx_hash_set(), + containers_special_derives: FxHashMap = default::fx_hash_map(), /// Parent scopes in which the macros were invoked. /// FIXME: `derives` are missing in these parent scopes and need to be taken from elsewhere. invocation_parent_scopes: FxHashMap> = default::fx_hash_map(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 2da26e05863fd..df21f78fa54fa 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -9,7 +9,7 @@ use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ - Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, + Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SpecialDerives, SyntaxExtension, SyntaxExtensionKind, }; use rustc_expand::compile_declarative_macro; @@ -375,12 +375,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } } - fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool { - self.containers_deriving_copy.contains(&expn_id) - } - - fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool { - self.containers_deriving_ord.contains(&expn_id) + fn has_derives(&self, expn_id: LocalExpnId, derives: SpecialDerives) -> bool { + self.containers_special_derives.get(&expn_id).map_or(false, |d| d.contains(derives)) } fn resolve_derives( @@ -401,8 +397,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData { resolutions: derive_paths(), helper_attrs: Vec::new(), - has_derive_copy: false, - has_derive_ord: false, + has_derives: SpecialDerives::empty(), }); let parent_scope = self.invocation_parent_scopes[&expn_id]; for (i, resolution) in entry.resolutions.iter_mut().enumerate() { @@ -424,8 +419,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { .map(|&name| (i, IdentKey { name, ctxt }, span)), ); } - entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); - entry.has_derive_ord |= ext.builtin_name == Some(sym::Ord); + if ext.builtin_name == Some(sym::Copy) { + entry.has_derives |= SpecialDerives::COPY + } + if ext.builtin_name == Some(sym::Ord) { + entry.has_derives |= SpecialDerives::ORD + } ext } Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive), @@ -450,22 +449,22 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { }) .collect(); self.helper_attrs.insert(expn_id, helper_attrs); - // Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive - // has `Copy`, to support `#[derive(Copy, Clone)]`, `#[derive(Clone, Copy)]`, or - // `#[derive(Copy)] #[derive(Clone)]`. We do this because the code generated for - // `derive(Clone)` changes if `derive(Copy)` is also present. + // Mark this derive container as having special built-in derives either if it + // has them itself or if its parent derive container has them, to support + // e.g. `#[derive(Copy, Clone)]`, `#[derive(Clone, Copy)]`, or + // `#[derive(Copy)] #[derive(Clone)]`. This is needed because the code + // generated for certain derives (e.g. `Clone`, `PartialOrd`) changes if + // other derives (e.g. `Copy`, `Ord`) are also present. // // FIXME(#124794): unfortunately this doesn't work with `#[derive(Clone)] #[derive(Copy)]`. // When the `Clone` impl is generated the `#[derive(Copy)]` hasn't been processed and - // `has_derive_copy` hasn't been set yet. - if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) { - self.containers_deriving_copy.insert(expn_id); + // the derive markers haven't been set yet. + let mut all_derives = entry.has_derives; + if let Some(parent_derives) = self.containers_special_derives.get(&parent_scope.expansion) { + all_derives |= *parent_derives; } - // Similar to the above `Copy` and `Clone` case, the code generated for - // `derive(PartialOrd)` changes if `derive(Ord)` is also present. - // FIXME(makai410): this also doesn't work with `#[derive(PartialOrd)] #[derive(Ord)]`. - if entry.has_derive_ord || self.has_derive_ord(parent_scope.expansion) { - self.containers_deriving_ord.insert(expn_id); + if !all_derives.is_empty() { + self.containers_special_derives.insert(expn_id, all_derives); } assert!(self.derive_data.is_empty()); self.derive_data = derive_data;