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 0d3c0c4

Browse files
committedApr 24, 2024
Auto merge of rust-lang#121053 - clubby789:derive-skip, r=<try>
Implement `#[skip]` for builtin derives Implement rust-lang#121050 Still needs some work but here's an initial working implementation to get feedback on the strategy.
2 parents c1feb3e + 93300c6 commit 0d3c0c4

File tree

22 files changed

+355
-24
lines changed

22 files changed

+355
-24
lines changed
 

‎compiler/rustc_builtin_macros/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a
110110
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
111111
.suggestion = remove the value
112112
113+
builtin_macros_derive_skip_bad_argument = incorrect usage of the `#[skip]` attribute
114+
.note = the `#[skip]` attribute accepts an optional list of traits
115+
.help = try using `#[skip]` or `#[skip(Trait)]`
116+
117+
builtin_macros_derive_skip_unsupported = the `#[skip]` attribute does not support this trait
118+
113119
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
114120
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
115121
.custom = use `std::env::var({$var_expr})` to read the variable at run time

‎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: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockO
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
@@ -35,9 +35,7 @@ pub fn expand_deriving_debug(
3535
attributes: thin_vec![cx.attr_word(sym::inline, span)],
3636
fieldless_variants_strategy:
3737
FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
38-
combine_substructure: combine_substructure(Box::new(|a, b, c| {
39-
show_substructure(a, b, c)
40-
})),
38+
combine_substructure: combine_substructure(Box::new(show_substructure)),
4139
}],
4240
associated_types: Vec::new(),
4341
is_const,
@@ -90,6 +88,10 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
9088
cx.expr_addr_of(field.span, field.self_expr.clone())
9189
}
9290
}
91+
let fields = fields
92+
.iter()
93+
.filter(|fi| !fi.skipped_derives.is_skipped(sym::Debug))
94+
.collect::<ThinVec<_>>();
9395

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

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

+124-6
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,15 +287,47 @@ 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,
287292
}
288293

289-
#[derive(Copy, Clone)]
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+
}
321+
}
322+
323+
#[derive(Copy, Clone, Debug)]
290324
pub enum IsTuple {
291325
No,
292326
Yes,
293327
}
294328

295329
/// Fields for a static method
330+
#[derive(Debug)]
296331
pub enum StaticFields {
297332
/// Tuple and unit structs/enum variants like this.
298333
Unnamed(Vec<Span>, IsTuple),
@@ -301,6 +336,7 @@ pub enum StaticFields {
301336
}
302337

303338
/// A summary of the possible sets of fields.
339+
#[derive(Debug)]
304340
pub enum SubstructureFields<'a> {
305341
/// A non-static method where `Self` is a struct.
306342
Struct(&'a ast::VariantData, Vec<FieldInfo>),
@@ -1215,7 +1251,13 @@ impl<'a> MethodDef<'a> {
12151251

12161252
let self_expr = discr_exprs.remove(0);
12171253
let other_selflike_exprs = discr_exprs;
1218-
let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
1254+
let discr_field = FieldInfo {
1255+
span,
1256+
name: None,
1257+
self_expr,
1258+
other_selflike_exprs,
1259+
skipped_derives: SkippedDerives::None,
1260+
};
12191261

12201262
let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args)
12211263
.map(|(&ident, selflike_arg)| {
@@ -1518,7 +1560,12 @@ impl<'a> TraitDef<'a> {
15181560
.collect()
15191561
}
15201562

1521-
fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo>
1563+
fn create_fields<F>(
1564+
&self,
1565+
cx: &ExtCtxt<'_>,
1566+
struct_def: &'a VariantData,
1567+
mk_exprs: F,
1568+
) -> Vec<FieldInfo>
15221569
where
15231570
F: Fn(usize, &ast::FieldDef, Span) -> Vec<P<ast::Expr>>,
15241571
{
@@ -1533,11 +1580,76 @@ impl<'a> TraitDef<'a> {
15331580
let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp);
15341581
let self_expr = exprs.remove(0);
15351582
let other_selflike_exprs = exprs;
1583+
let mut skipped_derives = SkippedDerives::None;
1584+
let skip_enabled = cx.ecfg.features.derive_skip
1585+
|| struct_field.span.allows_unstable(sym::derive_skip);
1586+
for attr in attr::filter_by_name(&struct_field.attrs, sym::skip) {
1587+
if !skip_enabled {
1588+
rustc_session::parse::feature_err(
1589+
&cx.sess,
1590+
sym::derive_skip,
1591+
attr.span,
1592+
"the `#[skip]` attribute is experimental",
1593+
)
1594+
.emit();
1595+
}
1596+
let Some(skip_attr) = ast::Attribute::meta_kind(attr) else {
1597+
unreachable!()
1598+
};
1599+
1600+
// FIXME: better errors
1601+
match skip_attr {
1602+
ast::MetaItemKind::Word => {
1603+
skipped_derives = SkippedDerives::All;
1604+
break;
1605+
}
1606+
ast::MetaItemKind::List(items) => {
1607+
for item in items {
1608+
let span = item.span();
1609+
let ast::NestedMetaItem::MetaItem(ast::MetaItem {
1610+
path,
1611+
kind: ast::MetaItemKind::Word,
1612+
..
1613+
}) = item
1614+
else {
1615+
cx.dcx().emit_err(errors::DeriveSkipBadArgument {
1616+
span,
1617+
});
1618+
continue;
1619+
};
1620+
let name = path.segments[0].ident;
1621+
const SUPPORTED_TRAITS: [Symbol; 5] = [
1622+
sym::PartialEq,
1623+
sym::PartialOrd,
1624+
sym::Ord,
1625+
sym::Hash,
1626+
sym::Debug,
1627+
];
1628+
if SUPPORTED_TRAITS.contains(&name.name) {
1629+
skipped_derives.add(path.segments[0].ident.name)
1630+
} else {
1631+
let traits = SUPPORTED_TRAITS.iter().map(|s| format!("`{s}`")).collect::<Vec<_>>().join(", ");
1632+
cx.psess().buffer_lint_with_diagnostic(
1633+
rustc_session::lint::builtin::UNSUPPORTED_DERIVE_SKIP,
1634+
span,
1635+
cx.current_expansion.lint_node_id,
1636+
crate::fluent_generated::builtin_macros_derive_skip_unsupported,
1637+
rustc_session::lint::BuiltinLintDiag::DeriveSkipUnsupported { traits },
1638+
)
1639+
}
1640+
}
1641+
}
1642+
ast::MetaItemKind::NameValue(lit) => {
1643+
cx.dcx().emit_err(errors::DeriveSkipBadArgument { span: lit.span });
1644+
}
1645+
}
1646+
}
15361647
FieldInfo {
15371648
span: sp.with_ctxt(self.span.ctxt()),
15381649
name: struct_field.ident,
15391650
self_expr,
15401651
other_selflike_exprs,
1652+
skipped_derives,
15411653
}
15421654
})
15431655
.collect()
@@ -1553,7 +1665,7 @@ impl<'a> TraitDef<'a> {
15531665
struct_def: &'a VariantData,
15541666
prefixes: &[String],
15551667
) -> Vec<FieldInfo> {
1556-
self.create_fields(struct_def, |i, _struct_field, sp| {
1668+
self.create_fields(cx, struct_def, |i, _struct_field, sp| {
15571669
prefixes
15581670
.iter()
15591671
.map(|prefix| {
@@ -1571,7 +1683,7 @@ impl<'a> TraitDef<'a> {
15711683
struct_def: &'a VariantData,
15721684
is_packed: bool,
15731685
) -> Vec<FieldInfo> {
1574-
self.create_fields(struct_def, |i, struct_field, sp| {
1686+
self.create_fields(cx, struct_def, |i, struct_field, sp| {
15751687
selflike_args
15761688
.iter()
15771689
.map(|selflike_arg| {
@@ -1667,13 +1779,19 @@ pub fn cs_fold<F>(
16671779
cx: &ExtCtxt<'_>,
16681780
trait_span: Span,
16691781
substructure: &Substructure<'_>,
1782+
trait_name: Symbol,
16701783
mut f: F,
16711784
) -> P<Expr>
16721785
where
16731786
F: FnMut(&ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
16741787
{
16751788
match substructure.fields {
16761789
EnumMatching(.., all_fields) | Struct(_, all_fields) => {
1790+
let all_fields = all_fields
1791+
.iter()
1792+
.filter(|fi| !fi.skipped_derives.is_skipped(trait_name))
1793+
.collect::<Vec<&FieldInfo>>();
1794+
16771795
if all_fields.is_empty() {
16781796
return f(cx, CsFold::Fieldless);
16791797
}
@@ -1686,7 +1804,7 @@ where
16861804

16871805
let base_expr = f(cx, CsFold::Single(base_field));
16881806

1689-
let op = |old, field: &FieldInfo| {
1807+
let op = |old, field: &&FieldInfo| {
16901808
let new = f(cx, CsFold::Single(field));
16911809
f(cx, CsFold::Combine(field.span, old, new))
16921810
};

‎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,
@@ -62,8 +60,11 @@ fn hash_substructure(cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'
6260

6361
let (stmts, match_expr) = match substr.fields {
6462
Struct(_, fields) | EnumMatching(.., fields) => {
65-
let stmts =
66-
fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect();
63+
let stmts = fields
64+
.iter()
65+
.filter(|fi| !fi.skipped_derives.is_skipped(sym::Hash))
66+
.map(|field| call_hash(field.span, field.self_expr.clone()))
67+
.collect();
6768
(stmts, None)
6869
}
6970
EnumDiscr(discr_field, match_expr) => {

‎compiler/rustc_builtin_macros/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,15 @@ pub(crate) struct DerivePathArgsValue {
295295
pub(crate) span: Span,
296296
}
297297

298+
#[derive(Diagnostic)]
299+
#[diag(builtin_macros_derive_skip_bad_argument)]
300+
pub(crate) struct DeriveSkipBadArgument {
301+
#[note]
302+
#[help]
303+
#[primary_span]
304+
pub(crate) span: Span,
305+
}
306+
298307
#[derive(Diagnostic)]
299308
#[diag(builtin_macros_no_default_variant)]
300309
#[help]

‎compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,8 @@ declare_features! (
442442
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
443443
/// Allows deref patterns.
444444
(incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)),
445+
/// Allows using the `#[skip]` attribute in derives
446+
(unstable, derive_skip, "CURRENT_RUSTC_VERSION", Some(121050)),
445447
/// Controls errors in trait implementations.
446448
(unstable, do_not_recommend, "1.67.0", Some(51992)),
447449
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.

‎compiler/rustc_lint/src/context/diagnostics.rs

+3
Original file line numberDiff line numberDiff line change
@@ -347,5 +347,8 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
347347
"reduce the glob import's visibility or increase visibility of imported items",
348348
);
349349
}
350+
BuiltinLintDiag::DeriveSkipUnsupported { traits } => {
351+
diag.help(format!("the supported traits are {traits}"));
352+
}
350353
}
351354
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.