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 6fc06e2

Browse files
committedFeb 16, 2024
Auto merge of #121053 - clubby789:derive-skip, r=<try>
Implement `#[skip]` for builtin derives Implement #121050 Still needs some work but here's an initial working implementation to get feedback on the strategy.
2 parents a447249 + 048a438 commit 6fc06e2

File tree

22 files changed

+354
-23
lines changed

22 files changed

+354
-23
lines changed
 

‎compiler/rustc_builtin_macros/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a
108108
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
109109
.suggestion = remove the value
110110
111+
builtin_macros_derive_skip_bad_argument = incorrect usage of the `#[skip]` attribute
112+
.note = the `#[skip]` attribute accepts an optional list of traits
113+
.help = try using `#[skip]` or `#[skip(Trait)]`
114+
115+
builtin_macros_derive_skip_unsupported = the `#[skip]` attribute does not support this trait
116+
111117
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
112118
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
113119
.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: &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

+123-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,76 @@ 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 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+
attr.span,
1591+
"the `#[skip]` attribute is experimental",
1592+
)
1593+
.emit();
1594+
}
1595+
let Some(skip_attr) = ast::Attribute::meta_kind(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 span = item.span();
1608+
let ast::NestedMetaItem::MetaItem(ast::MetaItem {
1609+
path,
1610+
kind: ast::MetaItemKind::Word,
1611+
..
1612+
}) = item
1613+
else {
1614+
cx.dcx().emit_err(errors::DeriveSkipBadArgument {
1615+
span,
1616+
});
1617+
continue;
1618+
};
1619+
let name = path.segments[0].ident;
1620+
const SUPPORTED_TRAITS: [Symbol; 5] = [
1621+
sym::PartialEq,
1622+
sym::PartialOrd,
1623+
sym::Ord,
1624+
sym::Hash,
1625+
sym::Debug,
1626+
];
1627+
if SUPPORTED_TRAITS.contains(&name.name) {
1628+
skipped_derives.add(path.segments[0].ident.name)
1629+
} else {
1630+
let traits = SUPPORTED_TRAITS.iter().map(|s| format!("`{s}`")).collect::<Vec<_>>().join(", ");
1631+
cx.parse_sess().buffer_lint_with_diagnostic(
1632+
rustc_session::lint::builtin::UNSUPPORTED_DERIVE_SKIP,
1633+
span,
1634+
cx.current_expansion.lint_node_id,
1635+
crate::fluent_generated::builtin_macros_derive_skip_unsupported,
1636+
rustc_session::lint::BuiltinLintDiagnostics::DeriveSkipUnsupported { traits },
1637+
)
1638+
}
1639+
}
1640+
}
1641+
ast::MetaItemKind::NameValue(lit) => {
1642+
cx.dcx().emit_err(errors::DeriveSkipBadArgument { span: lit.span });
1643+
}
1644+
}
1645+
}
15351646
FieldInfo {
15361647
span: sp.with_ctxt(self.span.ctxt()),
15371648
name: struct_field.ident,
15381649
self_expr,
15391650
other_selflike_exprs,
1651+
skipped_derives,
15401652
}
15411653
})
15421654
.collect()
@@ -1552,7 +1664,7 @@ impl<'a> TraitDef<'a> {
15521664
struct_def: &'a VariantData,
15531665
prefixes: &[String],
15541666
) -> Vec<FieldInfo> {
1555-
self.create_fields(struct_def, |i, _struct_field, sp| {
1667+
self.create_fields(cx, struct_def, |i, _struct_field, sp| {
15561668
prefixes
15571669
.iter()
15581670
.map(|prefix| {
@@ -1570,7 +1682,7 @@ impl<'a> TraitDef<'a> {
15701682
struct_def: &'a VariantData,
15711683
is_packed: bool,
15721684
) -> Vec<FieldInfo> {
1573-
self.create_fields(struct_def, |i, struct_field, sp| {
1685+
self.create_fields(cx, struct_def, |i, struct_field, sp| {
15741686
selflike_args
15751687
.iter()
15761688
.map(|selflike_arg| {
@@ -1665,13 +1777,19 @@ pub fn cs_fold<F>(
16651777
cx: &mut ExtCtxt<'_>,
16661778
trait_span: Span,
16671779
substructure: &Substructure<'_>,
1780+
trait_name: Symbol,
16681781
mut f: F,
16691782
) -> P<Expr>
16701783
where
16711784
F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
16721785
{
16731786
match substructure.fields {
16741787
EnumMatching(.., all_fields) | Struct(_, all_fields) => {
1788+
let all_fields = all_fields
1789+
.iter()
1790+
.filter(|fi| !fi.skipped_derives.is_skipped(trait_name))
1791+
.collect::<Vec<&FieldInfo>>();
1792+
16751793
if all_fields.is_empty() {
16761794
return f(cx, CsFold::Fieldless);
16771795
}
@@ -1684,7 +1802,7 @@ where
16841802

16851803
let base_expr = f(cx, CsFold::Single(base_field));
16861804

1687-
let op = |old, field: &FieldInfo| {
1805+
let op = |old, field: &&FieldInfo| {
16881806
let new = f(cx, CsFold::Single(field));
16891807
f(cx, CsFold::Combine(field.span, old, new))
16901808
};

‎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_builtin_macros/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,15 @@ pub(crate) struct DerivePathArgsValue {
316316
pub(crate) span: Span,
317317
}
318318

319+
#[derive(Diagnostic)]
320+
#[diag(builtin_macros_derive_skip_bad_argument)]
321+
pub(crate) struct DeriveSkipBadArgument {
322+
#[note]
323+
#[help]
324+
#[primary_span]
325+
pub(crate) span: Span,
326+
}
327+
319328
#[derive(Diagnostic)]
320329
#[diag(builtin_macros_no_default_variant)]
321330
#[help]

‎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_lint/src/context/diagnostics.rs

+3
Original file line numberDiff line numberDiff line change
@@ -583,5 +583,8 @@ pub(super) fn builtin(
583583
db.span_note(span, format!("the most public imported item is `{max_vis}`"));
584584
db.help("reduce the glob import's visibility or increase visibility of imported items");
585585
}
586+
BuiltinLintDiagnostics::DeriveSkipUnsupported { traits } => {
587+
db.help(format!("the supported traits are {traits}"));
588+
}
586589
}
587590
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.