-
-
Notifications
You must be signed in to change notification settings - Fork 15k
Implement fast path for derive(PartialOrd) when deriving Ord
#155598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c9a560b
802938c
d436194
caabc4b
766428b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety}; | ||
| use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast}; | ||
| use rustc_expand::base::{Annotatable, ExtCtxt}; | ||
| use rustc_span::{Ident, Span, sym}; | ||
| use thin_vec::thin_vec; | ||
| use thin_vec::{ThinVec, thin_vec}; | ||
|
|
||
| use crate::deriving::generic::ty::*; | ||
| use crate::deriving::generic::*; | ||
|
|
@@ -41,6 +41,45 @@ pub(crate) fn expand_deriving_partial_ord( | |
| } else { | ||
| true | ||
| }; | ||
|
|
||
| let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); | ||
| let has_derive_ord = cx.resolver.has_derive_ord(container_id); | ||
| let is_simple_candidate = |params: &ThinVec<ast::GenericParam>| -> bool { | ||
| has_derive_ord | ||
| && !params.iter().any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) | ||
| }; | ||
|
|
||
| let default_substructure = combine_substructure(Box::new(|cx, span, substr| { | ||
| cs_partial_cmp(cx, span, substr, discr_then_data) | ||
| })); | ||
| let simple_substructure = combine_substructure(Box::new(|cx, span, _| { | ||
| cs_partial_cmp_simple(cx, span, cx.expr_ident(span, Ident::new(sym::other, span))) | ||
| })); | ||
| let (is_simple, substructure) = match item { | ||
| Annotatable::Item(annitem) => match &annitem.kind { | ||
| // For unit structs/zero-variant enums, the default generated code is better. | ||
| ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure), | ||
| // Also for single fieldless variant enum | ||
| ItemKind::Enum(.., enum_def) if enum_def.variants.is_empty() => { | ||
| (false, default_substructure) | ||
| } | ||
| ItemKind::Enum(.., enum_def) | ||
| if enum_def.variants.len() == 1 | ||
| && matches!(enum_def.variants[0].data, ast::VariantData::Unit(..)) => | ||
| { | ||
| (false, default_substructure) | ||
| } | ||
| ItemKind::Struct(_, ast::Generics { params, .. }, _) | ||
| | ItemKind::Enum(_, ast::Generics { params, .. }, _) | ||
| if is_simple_candidate(params) => | ||
| { | ||
| (true, simple_substructure) | ||
| } | ||
| _ => (false, default_substructure), | ||
| }, | ||
| _ => (false, default_substructure), | ||
| }; | ||
|
|
||
| let partial_cmp_def = MethodDef { | ||
| name: sym::partial_cmp, | ||
| generics: Bounds::empty(), | ||
|
|
@@ -49,9 +88,7 @@ pub(crate) fn expand_deriving_partial_ord( | |
| ret_ty, | ||
| attributes: thin_vec![cx.attr_word(sym::inline, span)], | ||
| fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, | ||
| combine_substructure: combine_substructure(Box::new(|cx, span, substr| { | ||
| cs_partial_cmp(cx, span, substr, discr_then_data) | ||
| })), | ||
| combine_substructure: substructure, | ||
| }; | ||
|
|
||
| let trait_def = TraitDef { | ||
|
|
@@ -68,7 +105,18 @@ pub(crate) fn expand_deriving_partial_ord( | |
| safety: Safety::Default, | ||
| document: true, | ||
| }; | ||
| trait_def.expand(cx, mitem, item, push) | ||
| trait_def.expand_ext(cx, mitem, item, push, is_simple) | ||
| } | ||
|
|
||
| // Special case for the type deriving both `PartialOrd` and `Ord`. Builds: | ||
| // ``` | ||
| // Some(::core::cmp::Ord::cmp(self, other)) | ||
| // ``` | ||
| fn cs_partial_cmp_simple(cx: &ExtCtxt<'_>, span: Span, other_expr: Box<ast::Expr>) -> BlockOrExpr { | ||
| let ord_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); | ||
| let cmp_expr = | ||
| cx.expr_call_global(span, ord_cmp_path, thin_vec![cx.expr_self(span), other_expr]); | ||
| BlockOrExpr::new_expr(cx.expr_some(span, cmp_expr)) | ||
| } | ||
|
theemathas marked this conversation as resolved.
|
||
|
|
||
| fn cs_partial_cmp( | ||
|
|
@@ -98,7 +146,8 @@ fn cs_partial_cmp( | |
| |cx, fold| match fold { | ||
| CsFold::Single(field) => { | ||
| let [other_expr] = &field.other_selflike_exprs[..] else { | ||
| cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); | ||
| cx.dcx() | ||
| .span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`"); | ||
| }; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is probably a copy-paste curse, so I changed that, not 100% sure though. |
||
| let args = thin_vec![field.self_expr.clone(), other_expr.clone()]; | ||
| cx.expr_call_global(field.span, partial_cmp_path.clone(), args) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1323,7 +1323,10 @@ impl ExternPreludeEntry<'_> { | |
| struct DeriveData { | ||
| resolutions: Vec<DeriveResolution>, | ||
| 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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We had multiple flags here in the past, and they indeed used flags, and one map in the resolver instead of two
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for providing the context. I was thinking about doing one more round of scanning derives to collect all flags such that it might solve #124794. I don't know if that had been discussed before, and if it did I'd really want to know the reason of why not doing that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If I correctly understand what you are talking about, it's a much worse hack and it cannot work correctly. |
||
| has_derive_copy: bool, | ||
| has_derive_ord: bool, | ||
| } | ||
|
|
||
| pub struct ResolverOutputs<'tcx> { | ||
|
|
@@ -1467,6 +1470,7 @@ pub struct Resolver<'ra, 'tcx> { | |
| /// 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<LocalExpnId> = default::fx_hash_set(), | ||
| containers_deriving_ord: FxHashSet<LocalExpnId> = default::fx_hash_set(), | ||
| /// 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<LocalExpnId, ParentScope<'ra>> = default::fx_hash_map(), | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't quite right, single-variant struts also simply return
Equal:https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=53cbd635cd12b824e5063013b5ab541d
So this check should be:
Rather than trying to replicate the exact conditions used for
Equalin the derive, can we just call a function containing those conditions in both places?View changes since the review
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. The exact conditions use
cs_fold(), which under the hood does fancy things that are kind of hard to extract.I tried to reuse the substructure here as
cs_fold()does, but it seems that we can't retrieve generic params infos with the substructure, and then the code becomes more complicated if we use bothSubstructureandAnnotatable.So I'm going to tweak the conditions here. Note that only single-variant fieldless enums simply return
Equal, so we should also see if it's fieldless or not.