revset: rewrite 'x ~ y' to 'x & ~y' first to apply filter optimization

This is remainder of 48d10d648c "revset: add unary negate (or set
complement) operator '~y'".
This commit is contained in:
Yuya Nishihara 2022-12-07 16:58:06 +09:00
parent b6e06f6dc9
commit 222d9a6527

View file

@ -1276,9 +1276,23 @@ fn fold_difference(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpress
}) })
} }
/// Transforms binary difference to more primitive negative intersection.
///
/// For example, `all() ~ e` will become `all() & ~e`, which can be simplified
/// further by `fold_redundant_expression()`.
fn unfold_difference(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
RevsetExpression::Difference(expression1, expression2) => {
Some(expression1.intersection(&expression2.negated()))
}
_ => None,
})
}
/// Rewrites the given `expression` tree to reduce evaluation cost. Returns new /// Rewrites the given `expression` tree to reduce evaluation cost. Returns new
/// tree. /// tree.
pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> { pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> {
let expression = unfold_difference(&expression).unwrap_or(expression);
let expression = internalize_filter(&expression).unwrap_or(expression); let expression = internalize_filter(&expression).unwrap_or(expression);
let expression = fold_redundant_expression(&expression).unwrap_or(expression); let expression = fold_redundant_expression(&expression).unwrap_or(expression);
fold_difference(&expression).unwrap_or(expression) fold_difference(&expression).unwrap_or(expression)
@ -2513,10 +2527,6 @@ mod tests {
let optimized = optimize(parsed.clone()); let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(&parsed, &optimized)); assert!(Rc::ptr_eq(&parsed, &optimized));
let parsed = parse("branches() ~ tags()").unwrap();
let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(&parsed, &optimized));
// Only left subtree should be rewritten. // Only left subtree should be rewritten.
let parsed = parse("(branches() & all()) | tags()").unwrap(); let parsed = parse("(branches() & all()) | tags()").unwrap();
let optimized = optimize(parsed.clone()); let optimized = optimize(parsed.clone());
@ -2587,6 +2597,28 @@ mod tests {
) )
"###); "###);
// Binary difference operation should go through the same optimization passes.
insta::assert_debug_snapshot!(optimize(parse("foo ~ bar").unwrap()), @r###"
Difference(
Symbol(
"foo",
),
Symbol(
"bar",
),
)
"###);
insta::assert_debug_snapshot!(optimize(parse("(all() ~ foo) & bar").unwrap()), @r###"
Difference(
Symbol(
"bar",
),
Symbol(
"foo",
),
)
"###);
// Double/triple negates. // Double/triple negates.
insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar").unwrap()), @r###" insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar").unwrap()), @r###"
Intersection( Intersection(