Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 71d0aa0

Browse files
committedFeb 13, 2024
Implement #[skip] for builtin derives
1 parent bc1b9e0 commit 71d0aa0

File tree

17 files changed

+235
-23
lines changed

17 files changed

+235
-23
lines changed
 

‎compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ pub fn expand_deriving_eq(
3838
cx.attr_nested_word(sym::coverage, sym::off, span)
3939
],
4040
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
41-
combine_substructure: combine_substructure(Box::new(|a, b, c| {
42-
cs_total_eq_assert(a, b, c)
43-
})),
41+
combine_substructure: combine_substructure(Box::new(cs_total_eq_assert)),
4442
}],
4543
associated_types: Vec::new(),
4644
is_const,

‎compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub fn expand_deriving_ord(
3030
ret_ty: Path(path_std!(cmp::Ordering)),
3131
attributes: thin_vec![cx.attr_word(sym::inline, span)],
3232
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
33-
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
33+
combine_substructure: combine_substructure(Box::new(cs_cmp)),
3434
}],
3535
associated_types: Vec::new(),
3636
is_const,
@@ -58,6 +58,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
5858
cx,
5959
span,
6060
substr,
61+
sym::Ord,
6162
|cx, fold| match fold {
6263
CsFold::Single(field) => {
6364
let [other_expr] = &field.other_selflike_exprs[..] else {

‎compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub fn expand_deriving_partial_eq(
2323
cx,
2424
span,
2525
substr,
26+
sym::PartialEq,
2627
|cx, fold| match fold {
2728
CsFold::Single(field) => {
2829
let [other_expr] = &field.other_selflike_exprs[..] else {
@@ -98,7 +99,7 @@ pub fn expand_deriving_partial_eq(
9899
ret_ty: Path(path_local!(bool)),
99100
attributes: thin_vec![cx.attr_word(sym::inline, span)],
100101
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
101-
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
102+
combine_substructure: combine_substructure(Box::new(cs_eq)),
102103
}];
103104

104105
let trait_def = TraitDef {

‎compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn cs_partial_cmp(
9292
cx,
9393
span,
9494
substr,
95+
sym::PartialOrd,
9596
|cx, fold| match fold {
9697
CsFold::Single(field) => {
9798
let [other_expr] = &field.other_selflike_exprs[..] else {

‎compiler/rustc_builtin_macros/src/deriving/debug.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ pub fn expand_deriving_debug(
3636
attributes: thin_vec![cx.attr_word(sym::inline, span)],
3737
fieldless_variants_strategy:
3838
FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
39-
combine_substructure: combine_substructure(Box::new(|a, b, c| {
40-
show_substructure(a, b, c)
41-
})),
39+
combine_substructure: combine_substructure(Box::new(show_substructure)),
4240
}],
4341
associated_types: Vec::new(),
4442
is_const,
@@ -91,6 +89,10 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
9189
cx.expr_addr_of(field.span, field.self_expr.clone())
9290
}
9391
}
92+
let fields = fields
93+
.iter()
94+
.filter(|fi| !fi.skipped_derives.is_skipped(sym::Debug))
95+
.collect::<ThinVec<_>>();
9496

9597
if fields.is_empty() {
9698
// Special case for no fields.

‎compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+101-5
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt};
188188
use rustc_session::lint::builtin::BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE;
189189
use rustc_span::symbol::{kw, sym, Ident, Symbol};
190190
use rustc_span::{Span, DUMMY_SP};
191+
use smallvec::SmallVec;
191192
use std::cell::RefCell;
192193
use std::iter;
193194
use std::ops::Not;
@@ -263,6 +264,7 @@ pub enum FieldlessVariantsStrategy {
263264
}
264265

265266
/// All the data about the data structure/method being derived upon.
267+
#[derive(Debug)]
266268
pub struct Substructure<'a> {
267269
/// ident of self
268270
pub type_ident: Ident,
@@ -273,6 +275,7 @@ pub struct Substructure<'a> {
273275
}
274276

275277
/// Summary of the relevant parts of a struct/enum field.
278+
#[derive(Debug)]
276279
pub struct FieldInfo {
277280
pub span: Span,
278281
/// None for tuple structs/normal enum variants, Some for normal
@@ -284,9 +287,41 @@ pub struct FieldInfo {
284287
/// The expressions corresponding to references to this field in
285288
/// the other selflike arguments.
286289
pub other_selflike_exprs: Vec<P<Expr>>,
290+
/// The derives for which this field should be ignored
291+
pub skipped_derives: SkippedDerives,
292+
}
293+
294+
/// Derives for which this field should be ignored
295+
#[derive(Debug)]
296+
pub enum SkippedDerives {
297+
/// No `#[skip]`
298+
None,
299+
/// `#[skip(Trait, Names)]`
300+
List(SmallVec<[Symbol; 1]>),
301+
/// `#[skip]` with no arguments
302+
All,
303+
}
304+
305+
impl SkippedDerives {
306+
pub fn add(&mut self, derive: Symbol) {
307+
match self {
308+
Self::None => *self = Self::List(SmallVec::from([derive])),
309+
Self::List(idents) => idents.push(derive),
310+
Self::All => (),
311+
}
312+
}
313+
314+
pub fn is_skipped(&self, derive: Symbol) -> bool {
315+
match self {
316+
Self::None => false,
317+
Self::List(idents) => idents.contains(&derive),
318+
Self::All => true,
319+
}
320+
}
287321
}
288322

289323
/// Fields for a static method
324+
#[derive(Debug)]
290325
pub enum StaticFields {
291326
/// Tuple and unit structs/enum variants like this.
292327
Unnamed(Vec<Span>, bool /*is tuple*/),
@@ -295,6 +330,7 @@ pub enum StaticFields {
295330
}
296331

297332
/// A summary of the possible sets of fields.
333+
#[derive(Debug)]
298334
pub enum SubstructureFields<'a> {
299335
/// A non-static method where `Self` is a struct.
300336
Struct(&'a ast::VariantData, Vec<FieldInfo>),
@@ -1213,7 +1249,13 @@ impl<'a> MethodDef<'a> {
12131249

12141250
let self_expr = tag_exprs.remove(0);
12151251
let other_selflike_exprs = tag_exprs;
1216-
let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
1252+
let tag_field = FieldInfo {
1253+
span,
1254+
name: None,
1255+
self_expr,
1256+
other_selflike_exprs,
1257+
skipped_derives: SkippedDerives::None,
1258+
};
12171259

12181260
let tag_let_stmts: ThinVec<_> = iter::zip(&tag_idents, &selflike_args)
12191261
.map(|(&ident, selflike_arg)| {
@@ -1517,7 +1559,12 @@ impl<'a> TraitDef<'a> {
15171559
.collect()
15181560
}
15191561

1520-
fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo>
1562+
fn create_fields<F>(
1563+
&self,
1564+
cx: &ExtCtxt<'_>,
1565+
struct_def: &'a VariantData,
1566+
mk_exprs: F,
1567+
) -> Vec<FieldInfo>
15211568
where
15221569
F: Fn(usize, &ast::FieldDef, Span) -> Vec<P<ast::Expr>>,
15231570
{
@@ -1532,11 +1579,54 @@ impl<'a> TraitDef<'a> {
15321579
let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp);
15331580
let self_expr = exprs.remove(0);
15341581
let other_selflike_exprs = exprs;
1582+
let mut skipped_derives = SkippedDerives::None;
1583+
let skip_enabled = cx.ecfg.features.derive_skip
1584+
|| struct_field.span.allows_unstable(sym::derive_skip);
1585+
for skip_attr in attr::filter_by_name(&struct_field.attrs, sym::skip) {
1586+
if !skip_enabled {
1587+
rustc_session::parse::feature_err(
1588+
&cx.sess,
1589+
sym::derive_skip,
1590+
skip_attr.span,
1591+
"the `#[skip]` attribute is experimental",
1592+
)
1593+
.emit();
1594+
}
1595+
let Some(skip_attr) = ast::Attribute::meta_kind(skip_attr) else {
1596+
unreachable!()
1597+
};
1598+
1599+
// FIXME: better errors
1600+
match skip_attr {
1601+
ast::MetaItemKind::Word => {
1602+
skipped_derives = SkippedDerives::All;
1603+
break;
1604+
}
1605+
ast::MetaItemKind::List(items) => {
1606+
for item in items {
1607+
let ast::NestedMetaItem::MetaItem(ast::MetaItem {
1608+
path,
1609+
kind: ast::MetaItemKind::Word,
1610+
..
1611+
}) = item
1612+
else {
1613+
cx.dcx().span_err(item.span(), "incorrect skip argument");
1614+
continue;
1615+
};
1616+
skipped_derives.add(path.segments[0].ident.name);
1617+
}
1618+
}
1619+
ast::MetaItemKind::NameValue(lit) => {
1620+
cx.dcx().span_err(lit.span, "invalid skip attribute");
1621+
}
1622+
}
1623+
}
15351624
FieldInfo {
15361625
span: sp.with_ctxt(self.span.ctxt()),
15371626
name: struct_field.ident,
15381627
self_expr,
15391628
other_selflike_exprs,
1629+
skipped_derives,
15401630
}
15411631
})
15421632
.collect()
@@ -1552,7 +1642,7 @@ impl<'a> TraitDef<'a> {
15521642
struct_def: &'a VariantData,
15531643
prefixes: &[String],
15541644
) -> Vec<FieldInfo> {
1555-
self.create_fields(struct_def, |i, _struct_field, sp| {
1645+
self.create_fields(cx, struct_def, |i, _struct_field, sp| {
15561646
prefixes
15571647
.iter()
15581648
.map(|prefix| {
@@ -1570,7 +1660,7 @@ impl<'a> TraitDef<'a> {
15701660
struct_def: &'a VariantData,
15711661
is_packed: bool,
15721662
) -> Vec<FieldInfo> {
1573-
self.create_fields(struct_def, |i, struct_field, sp| {
1663+
self.create_fields(cx, struct_def, |i, struct_field, sp| {
15741664
selflike_args
15751665
.iter()
15761666
.map(|selflike_arg| {
@@ -1665,13 +1755,19 @@ pub fn cs_fold<F>(
16651755
cx: &mut ExtCtxt<'_>,
16661756
trait_span: Span,
16671757
substructure: &Substructure<'_>,
1758+
trait_name: Symbol,
16681759
mut f: F,
16691760
) -> P<Expr>
16701761
where
16711762
F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
16721763
{
16731764
match substructure.fields {
16741765
EnumMatching(.., all_fields) | Struct(_, all_fields) => {
1766+
let all_fields = all_fields
1767+
.iter()
1768+
.filter(|fi| !fi.skipped_derives.is_skipped(trait_name))
1769+
.collect::<Vec<&FieldInfo>>();
1770+
16751771
if all_fields.is_empty() {
16761772
return f(cx, CsFold::Fieldless);
16771773
}
@@ -1684,7 +1780,7 @@ where
16841780

16851781
let base_expr = f(cx, CsFold::Single(base_field));
16861782

1687-
let op = |old, field: &FieldInfo| {
1783+
let op = |old, field: &&FieldInfo| {
16881784
let new = f(cx, CsFold::Single(field));
16891785
f(cx, CsFold::Combine(field.span, old, new))
16901786
};

‎compiler/rustc_builtin_macros/src/deriving/hash.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ pub fn expand_deriving_hash(
3535
ret_ty: Unit,
3636
attributes: thin_vec![cx.attr_word(sym::inline, span)],
3737
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
38-
combine_substructure: combine_substructure(Box::new(|a, b, c| {
39-
hash_substructure(a, b, c)
40-
})),
38+
combine_substructure: combine_substructure(Box::new(hash_substructure)),
4139
}],
4240
associated_types: Vec::new(),
4341
is_const,
@@ -66,8 +64,11 @@ fn hash_substructure(
6664

6765
let (stmts, match_expr) = match substr.fields {
6866
Struct(_, fields) | EnumMatching(.., fields) => {
69-
let stmts =
70-
fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect();
67+
let stmts = fields
68+
.iter()
69+
.filter(|fi| !fi.skipped_derives.is_skipped(sym::Hash))
70+
.map(|field| call_hash(field.span, field.self_expr.clone()))
71+
.collect();
7172
(stmts, None)
7273
}
7374
EnumTag(tag_field, match_expr) => {

‎compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ declare_features! (
438438
(unstable, deprecated_safe, "1.61.0", Some(94978)),
439439
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
440440
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
441+
/// Allows using the `#[skip]` attribute in derives
442+
(unstable, derive_skip, "CURRENT_RUSTC_VERSION", Some(121050)),
441443
/// Allows using the `#[diagnostic]` attribute tool namespace
442444
(unstable, diagnostic_namespace, "1.73.0", Some(111996)),
443445
/// Controls errors in trait implementations.

‎compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ symbols! {
669669
derive,
670670
derive_const,
671671
derive_default_enum,
672+
derive_skip,
672673
destruct,
673674
destructuring_assignment,
674675
diagnostic,

‎library/core/src/cmp.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
267267

268268
/// Derive macro generating an impl of the trait [`PartialEq`].
269269
/// The behavior of this macro is described in detail [here](PartialEq#derivable).
270-
#[rustc_builtin_macro]
270+
#[rustc_builtin_macro(PartialEq, attributes(skip))]
271271
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
272272
#[allow_internal_unstable(core_intrinsics, structural_match)]
273273
pub macro PartialEq($item:item) {
@@ -911,7 +911,7 @@ pub trait Ord: Eq + PartialOrd<Self> {
911911

912912
/// Derive macro generating an impl of the trait [`Ord`].
913913
/// The behavior of this macro is described in detail [here](Ord#derivable).
914-
#[rustc_builtin_macro]
914+
#[rustc_builtin_macro(Ord, attributes(skip))]
915915
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
916916
#[allow_internal_unstable(core_intrinsics)]
917917
pub macro Ord($item:item) {
@@ -1225,7 +1225,7 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
12251225

12261226
/// Derive macro generating an impl of the trait [`PartialOrd`].
12271227
/// The behavior of this macro is described in detail [here](PartialOrd#derivable).
1228-
#[rustc_builtin_macro]
1228+
#[rustc_builtin_macro(PartialOrd, attributes(skip))]
12291229
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
12301230
#[allow_internal_unstable(core_intrinsics)]
12311231
pub macro PartialOrd($item:item) {

‎library/core/src/fmt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ pub trait Debug {
606606
// Separate module to reexport the macro `Debug` from prelude without the trait `Debug`.
607607
pub(crate) mod macros {
608608
/// Derive macro generating an impl of the trait `Debug`.
609-
#[rustc_builtin_macro]
609+
#[rustc_builtin_macro(Debug, attributes(skip))]
610610
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
611611
#[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)]
612612
pub macro Debug($item:item) {

‎library/core/src/hash/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ pub trait Hash {
248248
// Separate module to reexport the macro `Hash` from prelude without the trait `Hash`.
249249
pub(crate) mod macros {
250250
/// Derive macro generating an impl of the trait `Hash`.
251-
#[rustc_builtin_macro]
251+
#[rustc_builtin_macro(Hash, attributes(skip))]
252252
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
253253
#[allow_internal_unstable(core_intrinsics)]
254254
pub macro Hash($item:item) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![crate_type = "lib"]
2+
#![feature(derive_skip)]
3+
4+
#[derive(Debug)]
5+
struct KeyVal(#[skip = "Debug"] usize); //~ ERROR invalid skip attribute
6+
7+
#[derive(Debug)]
8+
struct BadArg(#[skip("Debug")] usize); //~ ERROR incorrect skip argument
9+
10+
// FIXME: better error for derives not supporting `skip`
11+
#[derive(Clone)]
12+
struct SkipClone(#[skip] usize); //~ ERROR cannot find attribute `skip` in this scope
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.