Skip to main content

i_slint_compiler/
passes.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
4mod apply_default_properties_from_style;
5mod binding_analysis;
6mod border_radius;
7mod check_drag_area;
8mod check_expressions;
9mod check_public_api;
10mod clip;
11mod collect_custom_fonts;
12mod collect_globals;
13mod collect_init_code;
14mod collect_libraries;
15mod collect_structs_and_enums;
16mod collect_subcomponents;
17mod compile_paths;
18pub(crate) mod const_propagation;
19mod deduplicate_property_read;
20mod default_geometry;
21mod deprecated_rotation_origin;
22#[cfg(feature = "renderer-software")]
23mod embed_glyphs;
24mod embed_images;
25mod flickable;
26mod focus_handling;
27pub mod generate_item_indices;
28pub mod infer_aliases_types;
29mod inject_debug_hooks;
30pub use inject_debug_hooks::property_id;
31mod inlining;
32mod key_bindings;
33mod lower_absolute_coordinates;
34mod lower_accessibility;
35mod lower_component_container;
36pub(crate) mod lower_layout;
37mod lower_menus;
38mod lower_platform;
39mod lower_popups;
40mod lower_property_to_element;
41mod lower_radiogroup;
42mod lower_repeated_rows;
43mod lower_shadows;
44mod lower_states;
45mod lower_tabwidget;
46mod lower_text_input_interface;
47mod lower_timers;
48mod lower_tooltips;
49pub mod materialize_fake_properties;
50pub mod move_declarations;
51mod optimize_useless_rectangles;
52mod purity_check;
53mod remove_aliases;
54mod remove_constant_conditions;
55mod remove_return;
56mod remove_unused_properties;
57mod repeater_component;
58pub mod resolve_native_classes;
59pub mod resolving;
60mod unique_declared_type_names;
61mod unique_id;
62mod visible;
63mod windows;
64mod z_order;
65
66use smol_str::SmolStr;
67
68pub use binding_analysis::GlobalAnalysis;
69
70pub async fn run_passes(
71    doc: &mut crate::object_tree::Document,
72    type_loader: &mut crate::typeloader::TypeLoader,
73    keep_raw: bool,
74    diag: &mut crate::diagnostics::BuildDiagnostics,
75) -> Option<crate::typeloader::TypeLoader> {
76    let style_metrics = {
77        // Ignore import errors
78        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
79        // Import from style-base.slint instead of std-widgets.slint to avoid loading
80        // the entire widget library just to get StyleMetrics.
81        type_loader
82            .import_component("style-base.slint", "StyleMetrics", &mut build_diags_to_ignore)
83            .await
84            .unwrap_or_else(|| panic!("can't load style metrics"))
85    };
86
87    let palette = {
88        // Ignore import errors
89        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
90        // Import from style-base.slint instead of std-widgets.slint to avoid loading
91        // the entire widget library just to get Palette.
92        type_loader
93            .import_component("style-base.slint", "Palette", &mut build_diags_to_ignore)
94            .await
95            .unwrap_or_else(|| panic!("can't load palette"))
96    };
97
98    let global_type_registry = type_loader.global_type_registry.clone();
99    // The shared symbol-name counters, handed to the passes that generate names.
100    let symbol_counters = type_loader.symbol_counters.clone();
101
102    run_import_passes(doc, type_loader, diag);
103    check_public_api::check_public_api(doc, &type_loader.compiler_config, diag);
104
105    let raw_type_loader =
106        keep_raw.then(|| crate::typeloader::snapshot_with_extra_doc(type_loader, doc).unwrap());
107
108    let mut forwarded_references =
109        crate::object_tree::forward_inherited_expression::ForwardedReferenceCache::default();
110
111    // Inject debug hooks early — before any lowering or inlining — so source element identity
112    // is preserved and hooks can be attributed to the correct source location.
113    if let Some(random_state) = &type_loader.compiler_config.debug_hooks {
114        let root_components = doc.exported_roots().collect::<Vec<_>>();
115        inject_debug_hooks::inject_debug_hooks(
116            &root_components,
117            random_state,
118            &symbol_counters,
119            &mut forwarded_references,
120        );
121    }
122
123    collect_libraries::collect_libraries(doc);
124    collect_subcomponents::collect_subcomponents(doc);
125    lower_tooltips::lower_tooltips(doc, type_loader, diag).await;
126    lower_tabwidget::lower_tabwidget(doc, type_loader, diag).await;
127    lower_radiogroup::lower_radiogroup(doc, type_loader, diag).await;
128    lower_menus::lower_menus(doc, type_loader, diag).await;
129    lower_component_container::lower_component_container(doc, type_loader, diag);
130    collect_subcomponents::collect_subcomponents(doc);
131
132    doc.visit_all_used_components(|component| {
133        apply_default_properties_from_style::apply_default_properties_from_style(
134            component,
135            &style_metrics,
136            &palette,
137            diag,
138        );
139        lower_states::lower_states(component, &symbol_counters, &mut forwarded_references, diag);
140        lower_text_input_interface::lower_text_input_interface(component);
141        compile_paths::check_derived_paths(component, &doc.local_registry, diag);
142        repeater_component::process_repeater_components(component);
143        lower_popups::lower_popups(component, &doc.local_registry, diag);
144        collect_init_code::collect_init_code(component);
145        lower_timers::lower_timers(component, diag);
146    });
147
148    inlining::inline(doc, inlining::InlineSelection::InlineOnlyRequiredComponents, diag);
149    collect_subcomponents::collect_subcomponents(doc);
150
151    for root_component in doc.exported_roots() {
152        focus_handling::call_focus_on_init(&root_component);
153        // Before ensure_window, which gives a non-Window root a synthetic
154        // background from the style, and after inlining, which is what brings
155        // a background inherited from a base component onto the root
156        #[cfg(feature = "slint-sc")]
157        if diag.slint_sc {
158            windows::check_sc_window_background(&root_component, diag);
159        }
160        windows::ensure_window(&root_component, &doc.local_registry, &style_metrics, diag);
161    }
162    if let Some(popup_menu_impl) = &doc.popup_menu_impl {
163        focus_handling::call_focus_on_init(popup_menu_impl);
164    }
165
166    doc.visit_all_used_components(|component| {
167        // After inlining, so that path elements added through `@children` or to a
168        // component inheriting `Path` are direct children of the `Path` element
169        compile_paths::compile_paths(component, &doc.local_registry, diag);
170        border_radius::handle_border_radius(component, diag);
171        check_drag_area::check_drag_area(component, diag);
172        deprecated_rotation_origin::handle_rotation_origin(component, diag);
173        flickable::handle_flickable(component, &global_type_registry.borrow());
174        lower_layout::lower_layouts(component, type_loader, &style_metrics, diag);
175        default_geometry::default_geometry(component, diag, &symbol_counters);
176        lower_layout::optimize_single_cell_layouts(component);
177        lower_layout::synthesize_layoutinfo_v_with_constraint(component);
178        lower_absolute_coordinates::lower_absolute_coordinates(component);
179        z_order::reorder_by_z_order(component);
180        lower_property_to_element::lower_property_to_element(
181            component,
182            core::iter::once("opacity"),
183            core::iter::empty(),
184            None,
185            &SmolStr::new_static("Opacity"),
186            &global_type_registry.borrow(),
187            diag,
188        );
189        lower_property_to_element::lower_property_to_element(
190            component,
191            core::iter::once("cache-rendering-hint"),
192            core::iter::empty(),
193            None,
194            &SmolStr::new_static("Layer"),
195            &global_type_registry.borrow(),
196            diag,
197        );
198        visible::handle_visible(component, &global_type_registry.borrow(), diag);
199        lower_property_to_element::lower_transform_properties(
200            component,
201            &global_type_registry.borrow(),
202            diag,
203        );
204        lower_shadows::lower_shadow_properties(component, &doc.local_registry, diag);
205        clip::handle_clip(component, &global_type_registry.borrow(), diag);
206        if type_loader.compiler_config.accessibility {
207            lower_accessibility::lower_accessibility_properties(component, diag);
208        }
209        lower_repeated_rows::lower_repeated_rows(component, &global_type_registry.borrow());
210        materialize_fake_properties::materialize_fake_properties(component);
211        lower_layout::check_popup_layout(component);
212    });
213    for root_component in doc.exported_roots() {
214        lower_layout::check_window_layout(&root_component);
215    }
216    collect_globals::collect_globals(doc, diag);
217    // Must be done before passes that rely on `NamedReference::is_constant`.
218    collect_globals::mark_library_globals(doc);
219
220    if type_loader.compiler_config.inline_all_elements {
221        inlining::inline(doc, inlining::InlineSelection::InlineAllComponents, diag);
222        doc.used_types.borrow_mut().sub_components.clear();
223    }
224
225    let global_analysis =
226        binding_analysis::binding_analysis(doc, &type_loader.compiler_config, diag);
227    unique_id::assign_unique_id(doc);
228
229    doc.visit_all_used_components(|component| {
230        key_bindings::warn_duplicates(component, diag);
231        lower_platform::lower_platform(component, type_loader);
232
233        // Don't perform the empty rectangle removal when debug info is requested, because the resulting
234        // item tree ends up with a hierarchy where certain items have children that aren't child elements
235        // but siblings or sibling children. We need a new data structure to perform a correct element tree
236        // traversal.
237        // Also keep the rectangles when debug hooks are enabled: their (synthetic) hooks are
238        // what makes the elements live-editable, and removing the element would drop them.
239        if !type_loader.compiler_config.debug_info
240            && type_loader.compiler_config.debug_hooks.is_none()
241        {
242            optimize_useless_rectangles::optimize_useless_rectangles(component);
243        }
244        move_declarations::move_declarations(component);
245    });
246
247    remove_aliases::remove_aliases(doc, diag);
248    remove_return::remove_return(doc, &symbol_counters);
249
250    doc.visit_all_used_components(|component| {
251        if !diag.has_errors() {
252            // binding loop causes panics in const_propagation
253            const_propagation::const_propagation(component, &global_analysis);
254            remove_constant_conditions::remove_constant_conditions(component);
255        }
256        deduplicate_property_read::deduplicate_property_read(component);
257        if !component.is_global() && !component.is_interface() {
258            resolve_native_classes::resolve_native_classes(component);
259        }
260    });
261
262    remove_unused_properties::remove_unused_properties(doc);
263
264    // With debug hooks enabled, every synthetic hook must by now either have been upgraded
265    // (by a pass computing the property's value or by inlining merging the definition's
266    // default) or sit on a property that exists at runtime. An orphan would abort the
267    // interpreter at instantiation ("unknown property ..."); catch it here with a source
268    // location instead.
269    if type_loader.compiler_config.debug_hooks.is_some() && !diag.has_errors() {
270        doc.visit_all_used_components(|component| {
271            inject_debug_hooks::validate_no_orphan_synthetic_hooks(component);
272        });
273    }
274
275    // collect globals once more: After optimizations we might have less globals
276    collect_globals::collect_globals(doc, diag);
277    unique_declared_type_names::assign_unique_declared_type_names(doc);
278    collect_structs_and_enums::collect_structs_and_enums(doc);
279
280    doc.visit_all_used_components(|component| {
281        if !component.is_global() {
282            generate_item_indices::generate_item_indices(component);
283        }
284    });
285
286    // The fonts (system + imported) used to embed glyphs and rasterize SVG text are
287    // shared between `embed_images` and `embed_glyphs`, so the system is scanned once.
288    #[cfg(feature = "renderer-software")]
289    let font_collection = (type_loader.compiler_config.embed_resources
290        == crate::EmbedResourcesKind::EmbedTextures)
291        .then(|| {
292            let custom = embed_glyphs::read_custom_fonts(
293                std::iter::once(&*doc).chain(type_loader.all_documents()),
294                diag,
295            );
296            embed_glyphs::shared_font_collection(custom)
297        });
298    #[cfg(not(feature = "renderer-software"))]
299    let font_collection: Option<embed_images::SharedFontCollection> = None;
300
301    embed_images::embed_images(
302        doc,
303        type_loader.compiler_config.embed_resources,
304        type_loader.compiler_config.const_scale_factor.unwrap_or(1.),
305        &type_loader.compiler_config.resource_url_mapper,
306        font_collection.as_ref(),
307        diag,
308    )
309    .await;
310
311    #[cfg(feature = "bundle-translations")]
312    if let Some(path) = &type_loader.compiler_config.translation_path_bundle {
313        match crate::translations::TranslationsBuilder::load_translations(
314            path,
315            type_loader.compiler_config.translation_domain.as_deref().unwrap_or(""),
316            &mut diag.all_loaded_files,
317        ) {
318            Ok(builder) => {
319                doc.translation_builder = Some(builder);
320            }
321            Err(err) => {
322                diag.push_error(
323                    format!("Cannot load bundled translation: {err}"),
324                    doc.node.as_ref().expect("Unexpected empty document"),
325                );
326            }
327        }
328    }
329
330    match type_loader.compiler_config.embed_resources {
331        #[cfg(feature = "renderer-software")]
332        crate::EmbedResourcesKind::EmbedTextures => {
333            let mut characters_seen = std::collections::HashSet::new();
334
335            let sf = type_loader.compiler_config.const_scale_factor.unwrap_or(1.) as f64;
336
337            // Include at least the default font sizes used in the MCU backend
338            let mut font_pixel_sizes = vec![(12. * sf) as i16];
339            use i_slint_common::sharedfontique::fontique;
340            let mut font_weights = vec![fontique::FontWeight::NORMAL.value() as u16];
341            doc.visit_all_used_components(|component| {
342                embed_glyphs::collect_font_sizes_used(component, sf, &mut font_pixel_sizes);
343                embed_glyphs::collect_font_weights_used(component, &mut font_weights);
344                embed_glyphs::scan_string_literals(component, &mut characters_seen);
345            });
346
347            // This is not perfect, as this includes translations that may not be used.
348            #[cfg(feature = "bundle-translations")]
349            if let Some(translation_builder) = doc.translation_builder.as_ref() {
350                translation_builder.collect_characters_seen(&mut characters_seen);
351            }
352
353            embed_glyphs::embed_glyphs(
354                doc,
355                &type_loader.compiler_config,
356                font_pixel_sizes,
357                font_weights,
358                characters_seen,
359                font_collection.as_ref().expect("EmbedTextures builds the shared font collection"),
360                diag,
361            );
362        }
363        _ => {
364            // Create font registration calls for custom fonts, unless we're embedding pre-rendered glyphs
365            collect_custom_fonts::collect_custom_fonts(
366                doc,
367                std::iter::once(&*doc).chain(type_loader.all_documents()),
368                type_loader.compiler_config.embed_resources
369                    == crate::EmbedResourcesKind::EmbedAllResources,
370            );
371        }
372    };
373
374    raw_type_loader
375}
376
377/// Run the passes on imported documents
378pub fn run_import_passes(
379    doc: &crate::object_tree::Document,
380    type_loader: &crate::typeloader::TypeLoader,
381    diag: &mut crate::diagnostics::BuildDiagnostics,
382) {
383    infer_aliases_types::resolve_aliases(doc, diag, &type_loader.symbol_counters);
384    resolving::resolve_expressions(doc, type_loader, diag);
385    purity_check::purity_check(doc, diag);
386    focus_handling::replace_forward_focus_bindings_with_focus_functions(doc, diag);
387    check_expressions::check_expressions(doc, diag);
388    windows::warn_about_child_windows(doc, diag);
389    unique_id::check_unique_id(doc, diag);
390}