Skip to main content

i_slint_compiler/passes/resolving/
remove_noop.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::{diagnostics::BuildDiagnostics, expression_tree::Expression, parser::SyntaxNode};
5
6/// Remove all expressions that are proven to have no effect from the given Expressions.
7///
8/// This function assumes that the given Expressions will form the an [Expression::CodeBlock], and
9/// will therefore not modify the last Expression in the Vec, as that forms the result value of the
10/// CodeBlock itself.
11pub fn remove_from_codeblock(
12    code_block: &mut Vec<(SyntaxNode, Expression)>,
13    diagnostics: &mut BuildDiagnostics,
14) {
15    if code_block.len() > 1 {
16        // In a code block, only the last expression returns a value.
17        // Therefore all other expressions inside the block are only useful if they have side
18        // effects.
19        //
20        // Remove all expressions without side effects (except for the last one) and emit a
21        // warning.
22        //
23        // Note: Iterate over the indices in reverse, so that all to-be-iterated indices remain
24        // valid when removing items from the vector.
25        for index in (0..(code_block.len() - 1)).rev() {
26            let (node, expression) = &code_block[index];
27            if without_side_effects(expression) {
28                diagnostics.push_warning("Expression has no effect!".to_owned(), node);
29                code_block.remove(index);
30            }
31        }
32    }
33}
34
35/// Returns whether the expression is certain to be without side effects.
36/// This function is conservative and may still return `false`, even if a given expression
37/// is without side effects.
38/// It is only guaranteed that if this function returns `true`, the expression definitely does not
39/// contain side effects.
40fn without_side_effects(expression: &Expression) -> bool {
41    match expression {
42        Expression::Condition { condition, true_expr, false_expr } => {
43            without_side_effects(condition)
44                && without_side_effects(true_expr)
45                && without_side_effects(false_expr)
46        }
47        Expression::NumberLiteral(_, _) => true,
48        Expression::StringLiteral(_) => true,
49        Expression::BoolLiteral(_) => true,
50        Expression::Keys(_) => true,
51        Expression::CodeBlock(expressions) => expressions.iter().all(without_side_effects),
52        Expression::FunctionParameterReference { .. } => true,
53        // Invalid and uncompiled expressions are unknown at this point, so default to
54        // `false`, because they may have side-efffects.
55        Expression::Invalid => false,
56        Expression::Uncompiled(_) => false,
57        // A property reference may cause re-evaluation of a property, which may result in
58        // side effects
59        Expression::PropertyReference(_) => false,
60        Expression::ElementReference(_) => false,
61        Expression::RepeaterIndexReference { .. } => true,
62        Expression::RepeaterModelReference { .. } => true,
63        Expression::StoreLocalVariable { .. } => false,
64        Expression::ReadLocalVariable { .. } => true,
65        Expression::StructFieldAccess { base, name: _ } => without_side_effects(base),
66        Expression::ArrayIndex { array, index } => {
67            without_side_effects(array) && without_side_effects(index)
68        }
69        // Note: This assumes that the cast itself does not have any side effects, which may not be
70        // the case if custom casting rules are implemented.
71        Expression::Cast { from, to: _ } => without_side_effects(from),
72        // Note: Calling a *pure* function is without side effects, however
73        // just from the expression, the purity of the function is not known.
74        // We would need to resolve the function to determine its purity.
75        Expression::FunctionCall { .. } => false,
76        Expression::SelfAssignment { .. } => false,
77        Expression::BinaryExpression { lhs, rhs, .. } => {
78            without_side_effects(lhs) && without_side_effects(rhs)
79        }
80        Expression::UnaryOp { sub, op: _ } => without_side_effects(sub),
81        Expression::ImageReference { .. } => true,
82        Expression::Array { element_ty: _, values } => values.iter().all(without_side_effects),
83        Expression::Struct { ty: _, values } => values.values().all(without_side_effects),
84        Expression::PathData(_) => true,
85        Expression::EasingCurve(_) => true,
86        Expression::LinearGradient { angle, stops } => {
87            without_side_effects(angle)
88                && stops
89                    .iter()
90                    .all(|(start, end)| without_side_effects(start) && without_side_effects(end))
91        }
92        Expression::RadialGradient { stops } => stops
93            .iter()
94            .all(|(start, end)| without_side_effects(start) && without_side_effects(end)),
95        Expression::ConicGradient { from_angle, stops } => {
96            without_side_effects(from_angle)
97                && stops
98                    .iter()
99                    .all(|(start, end)| without_side_effects(start) && without_side_effects(end))
100        }
101        Expression::EnumerationValue(_) => true,
102        // A return statement is never without side effects, as an important "side effect" is that
103        // the current function stops at this point.
104        Expression::ReturnStatement(_) => false,
105        Expression::LayoutCacheAccess { .. } => false,
106        Expression::GridRepeaterCacheAccess { .. } => false,
107        Expression::ComputeBoxLayoutInfo(_, _) => false,
108        Expression::ComputeGridLayoutInfo { .. } => false,
109        Expression::OrganizeGridLayout(_) => false,
110        Expression::SolveBoxLayout(_, _) => false,
111        Expression::SolveGridLayout { .. } => false,
112        Expression::SolveFlexboxLayout(..) => false,
113        Expression::ComputeFlexboxLayoutInfo(..) => false,
114        Expression::MinMax { ty: _, op: _, lhs, rhs } => {
115            without_side_effects(lhs) && without_side_effects(rhs)
116        }
117        Expression::DebugHook { .. } => false,
118        Expression::EmptyComponentFactory => false,
119    }
120}