Simplify cs_fold
.
`cs_fold` has four distinct cases, covered by three different function arguments: - first field - combine current field with previous results - no fields - non-matching enum variants This commit clarifies things by replacing the three function arguments with one that takes a new `CsFold` type with four slightly different) cases - single field - combine result for current field with results for previous fields - no fields - non-matching enum variants This makes the code shorter and clearer.
This commit is contained in:
parent
559398fa78
commit
16a286b003
4 changed files with 116 additions and 164 deletions
|
@ -55,57 +55,38 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
|
||||||
// foldr nests the if-elses correctly, leaving the first field
|
// foldr nests the if-elses correctly, leaving the first field
|
||||||
// as the outermost one, and the last as the innermost.
|
// as the outermost one, and the last as the innermost.
|
||||||
false,
|
false,
|
||||||
|cx, span, old, self_expr, other_selflike_exprs| {
|
|
||||||
// match new {
|
|
||||||
// ::core::cmp::Ordering::Equal => old,
|
|
||||||
// cmp => cmp
|
|
||||||
// }
|
|
||||||
let new = {
|
|
||||||
let [other_expr] = other_selflike_exprs else {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
|
|
||||||
};
|
|
||||||
let args = vec![
|
|
||||||
cx.expr_addr_of(span, self_expr),
|
|
||||||
cx.expr_addr_of(span, other_expr.clone()),
|
|
||||||
];
|
|
||||||
cx.expr_call_global(span, cmp_path.clone(), args)
|
|
||||||
};
|
|
||||||
|
|
||||||
let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), old);
|
|
||||||
let neq_arm = cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
|
|
||||||
|
|
||||||
cx.expr_match(span, new, vec![eq_arm, neq_arm])
|
|
||||||
},
|
|
||||||
|cx, args| match args {
|
|
||||||
Some((span, self_expr, other_selflike_exprs)) => {
|
|
||||||
let new = {
|
|
||||||
let [other_expr] = other_selflike_exprs else {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
|
|
||||||
};
|
|
||||||
let args = vec![
|
|
||||||
cx.expr_addr_of(span, self_expr),
|
|
||||||
cx.expr_addr_of(span, other_expr.clone()),
|
|
||||||
];
|
|
||||||
cx.expr_call_global(span, cmp_path.clone(), args)
|
|
||||||
};
|
|
||||||
|
|
||||||
new
|
|
||||||
}
|
|
||||||
None => cx.expr_path(equal_path.clone()),
|
|
||||||
},
|
|
||||||
Box::new(|cx, span, tag_tuple| {
|
|
||||||
if tag_tuple.len() != 2 {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`")
|
|
||||||
} else {
|
|
||||||
let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
|
|
||||||
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
|
|
||||||
let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
|
|
||||||
cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt])
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
cx,
|
cx,
|
||||||
span,
|
span,
|
||||||
substr,
|
substr,
|
||||||
|
|cx, fold| match fold {
|
||||||
|
CsFold::Single(field) => {
|
||||||
|
let [other_expr] = &field.other_selflike_exprs[..] else {
|
||||||
|
cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
|
||||||
|
};
|
||||||
|
let args = vec![
|
||||||
|
cx.expr_addr_of(field.span, field.self_expr.clone()),
|
||||||
|
cx.expr_addr_of(field.span, other_expr.clone()),
|
||||||
|
];
|
||||||
|
cx.expr_call_global(field.span, cmp_path.clone(), args)
|
||||||
|
}
|
||||||
|
CsFold::Combine(span, expr1, expr2) => {
|
||||||
|
let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1);
|
||||||
|
let neq_arm =
|
||||||
|
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
|
||||||
|
cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
|
||||||
|
}
|
||||||
|
CsFold::Fieldless => cx.expr_path(equal_path.clone()),
|
||||||
|
CsFold::EnumNonMatching(span, tag_tuple) => {
|
||||||
|
if tag_tuple.len() != 2 {
|
||||||
|
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`")
|
||||||
|
} else {
|
||||||
|
let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
|
||||||
|
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
|
||||||
|
let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
|
||||||
|
cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
BlockOrExpr::new_expr(expr)
|
BlockOrExpr::new_expr(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
|
||||||
use crate::deriving::generic::*;
|
use crate::deriving::generic::*;
|
||||||
use crate::deriving::{path_local, path_std};
|
use crate::deriving::{path_local, path_std};
|
||||||
|
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::{BinOpKind, MetaItem};
|
||||||
use rustc_ast::{BinOpKind, Expr, MetaItem};
|
|
||||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -23,34 +22,22 @@ pub fn expand_deriving_partial_eq(
|
||||||
combiner: BinOpKind,
|
combiner: BinOpKind,
|
||||||
base: bool,
|
base: bool,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let op = |cx: &mut ExtCtxt<'_>,
|
|
||||||
span: Span,
|
|
||||||
self_expr: P<Expr>,
|
|
||||||
other_selflike_exprs: &[P<Expr>]| {
|
|
||||||
let [other_expr] = other_selflike_exprs else {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`");
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.expr_binary(span, op, self_expr, other_expr.clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
let expr = cs_fold(
|
let expr = cs_fold(
|
||||||
true, // use foldl
|
true, // use foldl
|
||||||
|cx, span, old, self_expr, other_selflike_exprs| {
|
|
||||||
let eq = op(cx, span, self_expr, other_selflike_exprs);
|
|
||||||
cx.expr_binary(span, combiner, old, eq)
|
|
||||||
},
|
|
||||||
|cx, args| match args {
|
|
||||||
Some((span, self_expr, other_selflike_exprs)) => {
|
|
||||||
// Special-case the base case to generate cleaner code.
|
|
||||||
op(cx, span, self_expr, other_selflike_exprs)
|
|
||||||
}
|
|
||||||
None => cx.expr_bool(span, base),
|
|
||||||
},
|
|
||||||
Box::new(|cx, span, _| cx.expr_bool(span, !base)),
|
|
||||||
cx,
|
cx,
|
||||||
span,
|
span,
|
||||||
substr,
|
substr,
|
||||||
|
|cx, fold| match fold {
|
||||||
|
CsFold::Single(field) => {
|
||||||
|
let [other_expr] = &field.other_selflike_exprs[..] else {
|
||||||
|
cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
|
||||||
|
};
|
||||||
|
cx.expr_binary(field.span, op, field.self_expr.clone(), other_expr.clone())
|
||||||
|
}
|
||||||
|
CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2),
|
||||||
|
CsFold::Fieldless => cx.expr_bool(span, base),
|
||||||
|
CsFold::EnumNonMatching(span, _tag_tuple) => cx.expr_bool(span, !base),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
BlockOrExpr::new_expr(expr)
|
BlockOrExpr::new_expr(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,61 +63,40 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
|
||||||
// foldr nests the if-elses correctly, leaving the first field
|
// foldr nests the if-elses correctly, leaving the first field
|
||||||
// as the outermost one, and the last as the innermost.
|
// as the outermost one, and the last as the innermost.
|
||||||
false,
|
false,
|
||||||
|cx, span, old, self_expr, other_selflike_exprs| {
|
|
||||||
// match new {
|
|
||||||
// Some(::core::cmp::Ordering::Equal) => old,
|
|
||||||
// cmp => cmp
|
|
||||||
// }
|
|
||||||
let new = {
|
|
||||||
let [other_expr] = other_selflike_exprs else {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`");
|
|
||||||
};
|
|
||||||
|
|
||||||
let args = vec![
|
|
||||||
cx.expr_addr_of(span, self_expr),
|
|
||||||
cx.expr_addr_of(span, other_expr.clone()),
|
|
||||||
];
|
|
||||||
|
|
||||||
cx.expr_call_global(span, partial_cmp_path.clone(), args)
|
|
||||||
};
|
|
||||||
|
|
||||||
let eq_arm =
|
|
||||||
cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), old);
|
|
||||||
let neq_arm = cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
|
|
||||||
|
|
||||||
cx.expr_match(span, new, vec![eq_arm, neq_arm])
|
|
||||||
},
|
|
||||||
|cx, args| match args {
|
|
||||||
Some((span, self_expr, other_selflike_exprs)) => {
|
|
||||||
let new = {
|
|
||||||
let [other_expr] = other_selflike_exprs else {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
|
|
||||||
};
|
|
||||||
let args = vec![
|
|
||||||
cx.expr_addr_of(span, self_expr),
|
|
||||||
cx.expr_addr_of(span, other_expr.clone()),
|
|
||||||
];
|
|
||||||
cx.expr_call_global(span, partial_cmp_path.clone(), args)
|
|
||||||
};
|
|
||||||
|
|
||||||
new
|
|
||||||
}
|
|
||||||
None => cx.expr_some(span, cx.expr_path(equal_path.clone())),
|
|
||||||
},
|
|
||||||
Box::new(|cx, span, tag_tuple| {
|
|
||||||
if tag_tuple.len() != 2 {
|
|
||||||
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
|
|
||||||
} else {
|
|
||||||
let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
|
|
||||||
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
|
|
||||||
let fn_partial_cmp_path =
|
|
||||||
cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
|
|
||||||
cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt])
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
cx,
|
cx,
|
||||||
span,
|
span,
|
||||||
substr,
|
substr,
|
||||||
|
|cx, fold| match fold {
|
||||||
|
CsFold::Single(field) => {
|
||||||
|
let [other_expr] = &field.other_selflike_exprs[..] else {
|
||||||
|
cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
|
||||||
|
};
|
||||||
|
let args = vec![
|
||||||
|
cx.expr_addr_of(field.span, field.self_expr.clone()),
|
||||||
|
cx.expr_addr_of(field.span, other_expr.clone()),
|
||||||
|
];
|
||||||
|
cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
|
||||||
|
}
|
||||||
|
CsFold::Combine(span, expr1, expr2) => {
|
||||||
|
let eq_arm =
|
||||||
|
cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
|
||||||
|
let neq_arm =
|
||||||
|
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
|
||||||
|
cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
|
||||||
|
}
|
||||||
|
CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
|
||||||
|
CsFold::EnumNonMatching(span, tag_tuple) => {
|
||||||
|
if tag_tuple.len() != 2 {
|
||||||
|
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
|
||||||
|
} else {
|
||||||
|
let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
|
||||||
|
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
|
||||||
|
let fn_partial_cmp_path =
|
||||||
|
cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
|
||||||
|
cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
BlockOrExpr::new_expr(expr)
|
BlockOrExpr::new_expr(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,11 +296,6 @@ pub enum SubstructureFields<'a> {
|
||||||
pub type CombineSubstructureFunc<'a> =
|
pub type CombineSubstructureFunc<'a> =
|
||||||
Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>;
|
Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>;
|
||||||
|
|
||||||
/// Deal with non-matching enum variants. The slice is the identifiers holding
|
|
||||||
/// the variant index value for each of the `Self` arguments.
|
|
||||||
pub type EnumNonMatchCollapsedFunc<'a> =
|
|
||||||
Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &[Ident]) -> P<Expr> + 'a>;
|
|
||||||
|
|
||||||
pub fn combine_substructure(
|
pub fn combine_substructure(
|
||||||
f: CombineSubstructureFunc<'_>,
|
f: CombineSubstructureFunc<'_>,
|
||||||
) -> RefCell<CombineSubstructureFunc<'_>> {
|
) -> RefCell<CombineSubstructureFunc<'_>> {
|
||||||
|
@ -1601,55 +1596,65 @@ impl<'a> TraitDef<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function to fold over fields, with three cases, to generate more efficient and concise code.
|
/// The function passed to `cs_fold` is called repeatedly with a value of this
|
||||||
/// When the `substructure` has grouped fields, there are two cases:
|
/// type. It describes one part of the code generation. The result is always an
|
||||||
/// Zero fields: call the base case function with `None` (like the usual base case of `cs_fold`).
|
/// expression.
|
||||||
/// One or more fields: call the base case function on the first value (which depends on
|
pub enum CsFold<'a> {
|
||||||
/// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the
|
/// The basic case: a field expression for one or more selflike args. E.g.
|
||||||
/// fields.
|
/// for `PartialEq::eq` this is something like `self.x == other.x`.
|
||||||
/// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f`
|
Single(&'a FieldInfo),
|
||||||
/// is returned. Statics may not be folded over.
|
|
||||||
pub fn cs_fold<F, B>(
|
/// The combination of two field expressions. E.g. for `PartialEq::eq` this
|
||||||
|
/// is something like `<field1 equality> && <field2 equality>`.
|
||||||
|
Combine(Span, P<Expr>, P<Expr>),
|
||||||
|
|
||||||
|
// The fallback case for a struct or enum variant with no fields.
|
||||||
|
Fieldless,
|
||||||
|
|
||||||
|
/// The fallback case for non-matching enum variants. The slice is the
|
||||||
|
/// identifiers holding the variant index value for each of the `Self`
|
||||||
|
/// arguments.
|
||||||
|
EnumNonMatching(Span, &'a [Ident]),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Folds over fields, combining the expressions for each field in a sequence.
|
||||||
|
/// Statics may not be folded over.
|
||||||
|
pub fn cs_fold<F>(
|
||||||
use_foldl: bool,
|
use_foldl: bool,
|
||||||
mut f: F,
|
|
||||||
mut b: B,
|
|
||||||
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>,
|
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
substructure: &Substructure<'_>,
|
substructure: &Substructure<'_>,
|
||||||
|
mut f: F,
|
||||||
) -> P<Expr>
|
) -> P<Expr>
|
||||||
where
|
where
|
||||||
F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
|
F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
|
||||||
B: FnMut(&mut ExtCtxt<'_>, Option<(Span, P<Expr>, &[P<Expr>])>) -> P<Expr>,
|
|
||||||
{
|
{
|
||||||
match *substructure.fields {
|
match *substructure.fields {
|
||||||
EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => {
|
EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => {
|
||||||
let (base, rest) = match (all_fields.is_empty(), use_foldl) {
|
if all_fields.is_empty() {
|
||||||
(false, true) => {
|
return f(cx, CsFold::Fieldless);
|
||||||
let (first, rest) = all_fields.split_first().unwrap();
|
}
|
||||||
let args =
|
|
||||||
(first.span, first.self_expr.clone(), &first.other_selflike_exprs[..]);
|
let (base_field, rest) = if use_foldl {
|
||||||
(b(cx, Some(args)), rest)
|
all_fields.split_first().unwrap()
|
||||||
}
|
} else {
|
||||||
(false, false) => {
|
all_fields.split_last().unwrap()
|
||||||
let (last, rest) = all_fields.split_last().unwrap();
|
};
|
||||||
let args = (last.span, last.self_expr.clone(), &last.other_selflike_exprs[..]);
|
|
||||||
(b(cx, Some(args)), rest)
|
let base_expr = f(cx, CsFold::Single(base_field));
|
||||||
}
|
|
||||||
(true, _) => (b(cx, None), &all_fields[..]),
|
let op = |old, field: &FieldInfo| {
|
||||||
|
let new = f(cx, CsFold::Single(field));
|
||||||
|
f(cx, CsFold::Combine(field.span, old, new))
|
||||||
};
|
};
|
||||||
|
|
||||||
if use_foldl {
|
if use_foldl {
|
||||||
rest.iter().fold(base, |old, field| {
|
rest.iter().fold(base_expr, op)
|
||||||
f(cx, field.span, old, field.self_expr.clone(), &field.other_selflike_exprs)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
rest.iter().rev().fold(base, |old, field| {
|
rest.iter().rfold(base_expr, op)
|
||||||
f(cx, field.span, old, field.self_expr.clone(), &field.other_selflike_exprs)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EnumNonMatchingCollapsed(tuple) => enum_nonmatch_f(cx, trait_span, tuple),
|
EnumNonMatchingCollapsed(tuple) => f(cx, CsFold::EnumNonMatching(trait_span, tuple)),
|
||||||
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
|
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue