i_slint_compiler/passes/
remove_constant_conditions.rs1use crate::expression_tree::Expression;
11use crate::layout::{BOX_LAYOUT_CACHE_ENTRIES_PER_CELL, Layout};
12use crate::namedreference::NamedReference;
13use crate::object_tree::*;
14use std::collections::HashMap;
15use std::rc::Rc;
16
17pub fn remove_constant_conditions(component: &Rc<Component>) {
18 let mut dead = Vec::new();
19 recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
20 let e = elem.borrow();
21 let is_grid_cell = e.grid_layout_cell.is_some()
25 || matches!(&e.base_type, crate::langtype::ElementType::Component(c)
26 if c.root_element.borrow().grid_layout_cell.is_some());
27 if e.repeated.as_ref().is_some_and(|r| {
28 r.is_conditional_element && matches!(r.model, Expression::BoolLiteral(false))
29 }) && !is_grid_cell
30 && !e.is_component_placeholder
31 {
32 dead.push(elem.clone());
33 }
34 });
35 if dead.is_empty() {
36 return;
37 }
38
39 visit_all_expressions(component, |expr, _| {
42 expr.visit_recursive_mut(&mut |e| {
43 if let Expression::SolveFlexboxLayout(l)
44 | Expression::ComputeFlexboxLayoutInfo { layout: l, .. } = e
45 {
46 dead.retain(|c| !l.elems.iter().any(|it| Rc::ptr_eq(&it.element, c)));
47 }
48 })
49 });
50 if dead.is_empty() {
51 return;
52 }
53 let is_dead = |e: &ElementRc| dead.iter().any(|d| Rc::ptr_eq(d, e));
54
55 let mut dead_roots = std::collections::HashSet::new();
58 for c in &dead {
59 if let crate::langtype::ElementType::Component(base) = &c.borrow().base_type {
60 dead_roots.insert(Rc::as_ptr(&base.root_element));
61 }
62 }
63
64 let mut fixes: HashMap<NamedReference, Vec<usize>> = HashMap::new();
65 recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
66 visit_element_expressions(elem, |expr, name, _| {
67 let Some(name) = name else { return };
68 expr.visit_recursive_mut(&mut |e| match e {
69 Expression::SolveBoxLayout(l, _) => {
70 let bases: Vec<usize> = l
71 .elems
72 .iter()
73 .enumerate()
74 .filter(|(_, it)| is_dead(&it.element))
75 .map(|(k, _)| BOX_LAYOUT_CACHE_ENTRIES_PER_CELL * k)
76 .collect();
77 if !bases.is_empty() {
78 fixes.insert(NamedReference::new(elem, name.into()), bases);
79 l.elems.retain(|it| !is_dead(&it.element));
80 }
81 }
82 Expression::ComputeBoxLayoutInfo { layout: l, .. } => {
83 l.elems.retain(|it| !is_dead(&it.element))
84 }
85 _ => {}
86 });
87 });
88 for d in elem.borrow_mut().debug.iter_mut() {
91 if let Some(Layout::BoxLayout(l)) = d.layout.as_mut() {
92 l.elems.retain(|it| {
93 let mut hit = false;
94 it.constraints.clone().visit_named_references(&mut |nr| {
95 hit |= dead_roots.contains(&Rc::as_ptr(&nr.element()))
96 });
97 !hit
98 });
99 }
100 }
101 elem.borrow_mut().children.retain(|c| !is_dead(c));
102 });
103
104 visit_all_expressions(component, |expr, _| {
107 expr.visit_recursive_mut(&mut |e| {
108 if let Expression::LayoutCacheAccess { layout_cache_prop, index, .. } = e
109 && let Some(bases) = fixes.get(layout_cache_prop)
110 {
111 *index -= BOX_LAYOUT_CACHE_ENTRIES_PER_CELL
112 * bases.iter().filter(|b| **b < *index).count();
113 }
114 })
115 });
116}
117
118#[test]
119fn removes_constant_false_conditionals() {
120 let mut compiler_config =
121 crate::CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
122 compiler_config.style = Some("fluent".into());
123 let mut test_diags = crate::diagnostics::BuildDiagnostics::default();
124 let doc_node = crate::parser::parse(
125 r#"
126export component Foo {
127 in property <bool> dynamic;
128 property <bool> never: false;
129 if false: Rectangle {}
130 if never: Rectangle {}
131 if dynamic: Rectangle {}
132}
133"#
134 .into(),
135 Some(std::path::Path::new("test.slint")),
136 &mut test_diags,
137 );
138 let (doc, diag, _) =
139 spin_on::spin_on(crate::compile_syntax_node(doc_node, test_diags, compiler_config));
140 assert!(!diag.has_errors(), "slint compile error {:#?}", diag.to_string_vec());
141
142 let foo = doc.inner_components.iter().find(|c| c.id == "Foo").unwrap();
143 let mut models = Vec::new();
144 recurse_elem_including_sub_components(foo, &(), &mut |elem, _| {
145 if let Some(r) = &elem.borrow().repeated
146 && r.is_conditional_element
147 {
148 models.push(r.model.clone());
149 }
150 });
151
152 assert_eq!(models.len(), 1, "{models:?}");
156 assert!(!matches!(models[0], Expression::BoolLiteral(_)), "{:?}", models[0]);
157}