Skip to content

Commit dd8b2d6

Browse files
committed
Auto merge of #155598 - makai410:partial-ord, r=nnethercote
Implement fast path for `derive(PartialOrd)` when deriving `Ord` Closes: #155537 Unfortunately, this PR shares the same issue with #124794, which doesn't work when `derive(PartialOrd)` is before `derive(Ord)`.
2 parents 23a3312 + 766428b commit dd8b2d6

16 files changed

Lines changed: 223 additions & 312 deletions

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety};
1+
use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast};
22
use rustc_expand::base::{Annotatable, ExtCtxt};
33
use rustc_span::{Ident, Span, sym};
4-
use thin_vec::thin_vec;
4+
use thin_vec::{ThinVec, thin_vec};
55

66
use crate::deriving::generic::ty::*;
77
use crate::deriving::generic::*;
@@ -41,6 +41,45 @@ pub(crate) fn expand_deriving_partial_ord(
4141
} else {
4242
true
4343
};
44+
45+
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
46+
let has_derive_ord = cx.resolver.has_derive_ord(container_id);
47+
let is_simple_candidate = |params: &ThinVec<ast::GenericParam>| -> bool {
48+
has_derive_ord
49+
&& !params.iter().any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
50+
};
51+
52+
let default_substructure = combine_substructure(Box::new(|cx, span, substr| {
53+
cs_partial_cmp(cx, span, substr, discr_then_data)
54+
}));
55+
let simple_substructure = combine_substructure(Box::new(|cx, span, _| {
56+
cs_partial_cmp_simple(cx, span, cx.expr_ident(span, Ident::new(sym::other, span)))
57+
}));
58+
let (is_simple, substructure) = match item {
59+
Annotatable::Item(annitem) => match &annitem.kind {
60+
// For unit structs/zero-variant enums, the default generated code is better.
61+
ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure),
62+
// Also for single fieldless variant enum
63+
ItemKind::Enum(.., enum_def) if enum_def.variants.is_empty() => {
64+
(false, default_substructure)
65+
}
66+
ItemKind::Enum(.., enum_def)
67+
if enum_def.variants.len() == 1
68+
&& matches!(enum_def.variants[0].data, ast::VariantData::Unit(..)) =>
69+
{
70+
(false, default_substructure)
71+
}
72+
ItemKind::Struct(_, ast::Generics { params, .. }, _)
73+
| ItemKind::Enum(_, ast::Generics { params, .. }, _)
74+
if is_simple_candidate(params) =>
75+
{
76+
(true, simple_substructure)
77+
}
78+
_ => (false, default_substructure),
79+
},
80+
_ => (false, default_substructure),
81+
};
82+
4483
let partial_cmp_def = MethodDef {
4584
name: sym::partial_cmp,
4685
generics: Bounds::empty(),
@@ -49,9 +88,7 @@ pub(crate) fn expand_deriving_partial_ord(
4988
ret_ty,
5089
attributes: thin_vec![cx.attr_word(sym::inline, span)],
5190
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
52-
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
53-
cs_partial_cmp(cx, span, substr, discr_then_data)
54-
})),
91+
combine_substructure: substructure,
5592
};
5693

5794
let trait_def = TraitDef {
@@ -68,7 +105,18 @@ pub(crate) fn expand_deriving_partial_ord(
68105
safety: Safety::Default,
69106
document: true,
70107
};
71-
trait_def.expand(cx, mitem, item, push)
108+
trait_def.expand_ext(cx, mitem, item, push, is_simple)
109+
}
110+
111+
// Special case for the type deriving both `PartialOrd` and `Ord`. Builds:
112+
// ```
113+
// Some(::core::cmp::Ord::cmp(self, other))
114+
// ```
115+
fn cs_partial_cmp_simple(cx: &ExtCtxt<'_>, span: Span, other_expr: Box<ast::Expr>) -> BlockOrExpr {
116+
let ord_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
117+
let cmp_expr =
118+
cx.expr_call_global(span, ord_cmp_path, thin_vec![cx.expr_self(span), other_expr]);
119+
BlockOrExpr::new_expr(cx.expr_some(span, cmp_expr))
72120
}
73121

74122
fn cs_partial_cmp(
@@ -98,7 +146,8 @@ fn cs_partial_cmp(
98146
|cx, fold| match fold {
99147
CsFold::Single(field) => {
100148
let [other_expr] = &field.other_selflike_exprs[..] else {
101-
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
149+
cx.dcx()
150+
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`");
102151
};
103152
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
104153
cx.expr_call_global(field.span, partial_cmp_path.clone(), args)

compiler/rustc_expand/src/base.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,8 @@ pub trait ResolverExpand {
11171117
// Resolver interfaces for specific built-in macros.
11181118
/// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it?
11191119
fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool;
1120+
/// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Ord` inside it?
1121+
fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool;
11201122
/// Resolve paths inside the `#[derive(...)]` attribute with the given `ExpnId`.
11211123
fn resolve_derives(
11221124
&mut self,

compiler/rustc_resolve/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,10 @@ impl ExternPreludeEntry<'_> {
13231323
struct DeriveData {
13241324
resolutions: Vec<DeriveResolution>,
13251325
helper_attrs: Vec<(usize, IdentKey, Span)>,
1326+
// if this list keeps getting extended, we could use `bitflags`,
1327+
// something like what [`rustc_type_ir::flags::TypeFlags`] is doing.
13261328
has_derive_copy: bool,
1329+
has_derive_ord: bool,
13271330
}
13281331

13291332
pub struct ResolverOutputs<'tcx> {
@@ -1467,6 +1470,7 @@ pub struct Resolver<'ra, 'tcx> {
14671470
/// Derive macros cannot modify the item themselves and have to store the markers in the global
14681471
/// context, so they attach the markers to derive container IDs using this resolver table.
14691472
containers_deriving_copy: FxHashSet<LocalExpnId> = default::fx_hash_set(),
1473+
containers_deriving_ord: FxHashSet<LocalExpnId> = default::fx_hash_set(),
14701474
/// Parent scopes in which the macros were invoked.
14711475
/// FIXME: `derives` are missing in these parent scopes and need to be taken from elsewhere.
14721476
invocation_parent_scopes: FxHashMap<LocalExpnId, ParentScope<'ra>> = default::fx_hash_map(),

compiler/rustc_resolve/src/macros.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
379379
self.containers_deriving_copy.contains(&expn_id)
380380
}
381381

382+
fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool {
383+
self.containers_deriving_ord.contains(&expn_id)
384+
}
385+
382386
fn resolve_derives(
383387
&mut self,
384388
expn_id: LocalExpnId,
@@ -398,6 +402,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
398402
resolutions: derive_paths(),
399403
helper_attrs: Vec::new(),
400404
has_derive_copy: false,
405+
has_derive_ord: false,
401406
});
402407
let parent_scope = self.invocation_parent_scopes[&expn_id];
403408
for (i, resolution) in entry.resolutions.iter_mut().enumerate() {
@@ -420,6 +425,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
420425
);
421426
}
422427
entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy);
428+
entry.has_derive_ord |= ext.builtin_name == Some(sym::Ord);
423429
ext
424430
}
425431
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
@@ -455,6 +461,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
455461
if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
456462
self.containers_deriving_copy.insert(expn_id);
457463
}
464+
// Similar to the above `Copy` and `Clone` case, the code generated for
465+
// `derive(PartialOrd)` changes if `derive(Ord)` is also present.
466+
// FIXME(makai410): this also doesn't work with `#[derive(PartialOrd)] #[derive(Ord)]`.
467+
if entry.has_derive_ord || self.has_derive_ord(parent_scope.expansion) {
468+
self.containers_deriving_ord.insert(expn_id);
469+
}
458470
assert!(self.derive_data.is_empty());
459471
self.derive_data = derive_data;
460472
Ok(())

tests/mir-opt/pre-codegen/derived_ord_debug.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub struct MultiField(char, i16);
1010
// EMIT_MIR derived_ord_debug.{impl#1}-cmp.runtime-optimized.after.mir
1111

1212
// CHECK-LABEL: partial_cmp(_1: &MultiField, _2: &MultiField) -> Option<std::cmp::Ordering>
13-
// CHECK: = <char as PartialOrd>::partial_cmp(
14-
// CHECK: = <i16 as PartialOrd>::partial_cmp(
13+
// CHECK: = <MultiField as Ord>::cmp(
14+
// CHECK: = Option::<std::cmp::Ordering>::Some(
1515

1616
// CHECK-LABEL: cmp(_1: &MultiField, _2: &MultiField) -> std::cmp::Ordering
1717
// CHECK: = <char as Ord>::cmp(

tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,14 @@ fn <impl at $DIR/derived_ord_debug.rs:6:10: 6:20>::partial_cmp(_1: &MultiField,
44
debug self => _1;
55
debug other => _2;
66
let mut _0: std::option::Option<std::cmp::Ordering>;
7-
let _3: &char;
8-
let _4: &char;
9-
let mut _5: std::option::Option<std::cmp::Ordering>;
10-
let mut _6: isize;
11-
let mut _7: i8;
12-
let _8: &i16;
13-
let _9: &i16;
14-
scope 1 {
15-
debug cmp => _5;
16-
}
7+
let mut _3: std::cmp::Ordering;
178

189
bb0: {
19-
_3 = &((*_1).0: char);
20-
_4 = &((*_2).0: char);
21-
_5 = <char as PartialOrd>::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind unreachable];
10+
_3 = <MultiField as Ord>::cmp(copy _1, copy _2) -> [return: bb1, unwind unreachable];
2211
}
2312

2413
bb1: {
25-
_6 = discriminant(_5);
26-
switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6];
27-
}
28-
29-
bb2: {
30-
_7 = discriminant(((_5 as Some).0: std::cmp::Ordering));
31-
switchInt(move _7) -> [0: bb3, otherwise: bb4];
32-
}
33-
34-
bb3: {
35-
_8 = &((*_1).1: i16);
36-
_9 = &((*_2).1: i16);
37-
_0 = <i16 as PartialOrd>::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind unreachable];
38-
}
39-
40-
bb4: {
41-
_0 = copy _5;
42-
goto -> bb5;
43-
}
44-
45-
bb5: {
14+
_0 = Option::<std::cmp::Ordering>::Some(move _3);
4615
return;
4716
}
48-
49-
bb6: {
50-
unreachable;
51-
}
5217
}

tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,14 @@ fn <impl at $DIR/derived_ord_debug.rs:6:10: 6:20>::partial_cmp(_1: &MultiField,
44
debug self => _1;
55
debug other => _2;
66
let mut _0: std::option::Option<std::cmp::Ordering>;
7-
let _3: &char;
8-
let _4: &char;
9-
let mut _5: std::option::Option<std::cmp::Ordering>;
10-
let mut _6: isize;
11-
let mut _7: i8;
12-
let _8: &i16;
13-
let _9: &i16;
14-
scope 1 {
15-
debug cmp => _5;
16-
}
7+
let mut _3: std::cmp::Ordering;
178

189
bb0: {
19-
_3 = &((*_1).0: char);
20-
_4 = &((*_2).0: char);
21-
_5 = <char as PartialOrd>::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind continue];
10+
_3 = <MultiField as Ord>::cmp(copy _1, copy _2) -> [return: bb1, unwind continue];
2211
}
2312

2413
bb1: {
25-
_6 = discriminant(_5);
26-
switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6];
27-
}
28-
29-
bb2: {
30-
_7 = discriminant(((_5 as Some).0: std::cmp::Ordering));
31-
switchInt(move _7) -> [0: bb3, otherwise: bb4];
32-
}
33-
34-
bb3: {
35-
_8 = &((*_1).1: i16);
36-
_9 = &((*_2).1: i16);
37-
_0 = <i16 as PartialOrd>::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind continue];
38-
}
39-
40-
bb4: {
41-
_0 = copy _5;
42-
goto -> bb5;
43-
}
44-
45-
bb5: {
14+
_0 = Option::<std::cmp::Ordering>::Some(move _3);
4615
return;
4716
}
48-
49-
bb6: {
50-
unreachable;
51-
}
5217
}

tests/ui/derives/deriving-all-codegen.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,21 @@ struct FooCopyAndClone(i32);
235235
#[derive(Clone)]
236236
#[derive(Copy)]
237237
struct FooCloneAndCopy(i32);
238+
239+
#[derive(PartialOrd, Ord)]
240+
struct FooPartialOrdOrd(i32);
241+
242+
#[derive(Ord, PartialOrd)]
243+
struct FooOrdPartialOrd(i32);
244+
245+
#[derive(Ord)]
246+
#[derive(PartialOrd)]
247+
struct FooOrdBeforePartialOrd(i32);
248+
249+
// FIXME: this case should also have a trivial `PartialOrd` impl.
250+
#[derive(PartialOrd)]
251+
#[derive(Ord)]
252+
struct FooPartialOrdBeforeOrd(i32);
253+
254+
#[derive(PartialOrd, Ord)]
255+
struct UnitStruct;

0 commit comments

Comments
 (0)